Node.js MVC框架 未完 笔记整理

[TOC]
这个框架有点像drupal/

Node.js

MVC框架Adonis

介绍与准备

  1. node 8、npm 3以上版本
  2. 全局范围安装:
    1
    npm install @adonisjs/cli --global 
  3. 创建应用并运行应用
    1
    2
    cd ~/desktop
    adonis new sunwei-adonis
    使用模版blueprint:
    1
    adonis new sunwei-adonis ---blueprint ninghao/ninghao-adonis-blueprint 
  4. 启动服务器

    1
    adonis serve --dev 
  5. 版本控制
    git init
    git add .
    git commit -m ‘init’
    创建仓库,添加远程地址
    git push -u origin master

快速起步:

路由

打开目录:
start–routes.js 定义路由
设置:

1
2
3
Route.get('/hello',({ request })=>{
return `hello~ ${ request.input('name')}`
})

访问:

1
http://localhost:3333/hello?name=sunwei

这个路由的视图文件就是:resources ——views——hello.edge

控制器

  1. 创建控制器

    1
    adonis make:controller Hello
  2. 选择http request的这个选项
  3. 创建目录后添加方法render:
    render:交给的方法。通俗的讲就是把context的内容, 加载进templates中定义的文件, 并通过浏览器渲染呈现.
    render(request, template_name, context=None, content_type=None, status=None, using=None)
1
2
3
4
5
class HelloController {
render({request}){
return `hello~ ${ request.input('name')}`
}
}
  1. 再在路由中设置:
    1
    Route.get('/hello','HelloController.render')

视图

Provider:重复的需要一个类中的方法时,可封装它为服务类,以便重复使用,如http。

这里面里面有一个view的provider,就是有一个view的方法,那就把view这个参数解构出来。

1
2
3
4
5
6
class HelloController {
render({request,view}){
const name = request.input('name')
return view.render('hello',{ name })
}
}

创建一个edge视图:

1
adonis make:view hello

然后views下面会有一个edge的文件
添加html文件。atom添加edge插件
hello ~

数据库

  1. 默认使用sqlite
    在config——database.js里面
    在项目下安装:
    1
    adonis install sqlite3 
  2. 这儿用的是python2版本
    用到migration的功能,创建post这个表
    1
    adonis make:migration Post
  3. 选择create table
    在database里面看到 schema.js
    在up里面添加:
    table.string('title')
    1
    table.text(‘content','longtext')
    执行:
  4. adonis migration:run
插入与查询数据
  1. 打开routes.js 使用database

    1
    2
    3
    4
    5
    Database = use('Database')

    Route.get('/posts',async()=>{
    return await Database.table('posts').select('*')
    })
  2. 访问
    localhost:3333/posts

  3. 用命令行插入数据,使用repl命令交互模式
    a)输入 adonis repl

    1
    2
    await use(‘Database’).table('posts').insert({title:'apple',content:'a'})
    await use('Database').table('posts').insert({title:'lemon',content:'b'})

路由

路由

路由:简介

两个参数,一个是地址,箭头函数直接返回的值
用postman访问 返回的就List of posts

1
Route.get('/posts',()=>'List of posts') 

路由方法:Posts

想从客户端那里得到数据,可以让客户端用 HTTP 的 Post 方法;
把数据发送过来,要让客户端修改数据,可能使用HTTP的put或者patch方法:
要请求删除内容,http的delete方法。

post方法:

1
Route.post('/posts',()=>'post has been created')

\\ 把中间件注释掉 shield ,kernel

路由参数:Parameters

  1. :id 为必填, :id?为选填
    1
    2
    3
    Route.get('/posts/:id?',({ params })=>{
    return `you are watching ${params.id} . `
    })
  2. 访问:get方法
    localhost:3333/posts/33
    you are watching 33

路由方法:put/patch 与 delete

1
2
3
4
5
6
7
Route.patch('/posts/:id?',({ params })=>{
return `post ${params.id} has been updated. `
})

Route.delete('/posts/:id?',({ params })=>{
return `post ${params.id} has been removed . `
})

绑定控制器

绑定控制器:为了业务保持简单,一般不放在路由服务器中处理.
App-controller-http-post

1
adonis make:controller Post

在控制器的类里面添加方法,后面又导出了这个控制器,在路由里面,指定某个控制器的某个方法处理路由地址的请求
1、在控制器Postcontroller里面
1
2
3
4
5
class PostController {
index(){
return 'list of post'
}
}

module.exports = PostController

2、在路由里面使用这个方法:

1
Route.get('/posts','PostController.index')

3、postman,get这个localhost:3333/posts

资源

理解资源

应用里面的内容,用户,评论,还有商品等等,这些东西都可以看成是应用里的资源Resrouces针对某个资源,我们可以去创建一组惯用的路由,然后再创建一组控制器方法,用它们去处理对资源的请求 。

  • 假设应用上面有有一种资源叫Post,它表示的是应用里的内容,对于这种资源,可以创建一个控制器,叫 PostController。
    在这个控制器里再去创建一些方法来处理对资源的不同的请求。
  • 一般资源的路由地址都是资源的复数形式,比如 User,用户资源,它的地址可以是 users。如果是 Post 资源,那它的地址可以是 posts。
  • index
    直接用 HTTP 的 get 方法请求这个地址,应该返回资源的列表,处理请求的控制器方法的名字应该是 index(这只是一种惯用的命名方式 .. 并不是强制的)
  • store
    如果用 HTTP 的 post 方法请求资源地址的时候,可以使用控制器里的 store 方法来处理,通常这种请求就是去创建新的资源,所以在 store 方法里面,可以把客户端发送过来的数据,保存到应用的数据库里
  • show
    客户端想得到单个资源,它可以使用 get 方法请求资源名,加上资源 id 这种形式的地址,这种请求会用控制器的 show 方法来处理。在方法里,可以根据请求地址里面的 id 这个参数的值,到应用的数据库里,把这个资源找出来,然后再把结果返回给客户端 。
  • update
    修改资源的内容,可以在客户端那里用 PUT 或者 PATCH 这两个 HTTP 方法,去请求资源名加资源 id 这种形式的地址,处理请求用的对应的控制器的方法的名字是 update,表示更新。方法里面可以根据请求地址里的 id 参数的值,还有客户端发送过来的要修改的数据。找到对应的资源,再把资源的更新之后的数据放到数据库里。
  • delete
    客户端那里也可以发送删除资源的请求,请求用的方法是 DELETE ,请求的地址就是资源的名字,加上要删除的资源的 id 号, 处理这种请求用的控制器的方法的名字是 destroy。
  • create 与 edit
    在创建还有编辑资源的时候,你可能需要给用户提供一个创建还有编辑资源用的表单,显示创建资源用的表单,可以用 http 的 get 方法请求资源名,加上 create 这个路由地址,这个请求用控制器里的 create 方法来处理。
    返回编辑资源用的表单,路由地址是资源名,加上资源 id ,再加上一个 edit,这个请求可以交给控制器的 edit 方法来处理。

设置路由和绑定方法

控制器:app-controllers-http-posts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PostController {
index(){
return 'list of post'
}
store(){
return 'post has been created'
}
show({params}){
return `you are watching ${params.id} . `
}
update({params}){
return `post ${params.id} has been updated. `
}
destroy({params}){
return `post ${params.id} has been removed . `
}
create(){
return `create post . `
}
edit({params}){
return `Editing post ${params.id}. `
}

设置路由:starts-route.js

1
2
3
4
5
6
7
8
9
10
11
12
13
Route.get('/posts','PostController.index')

Route.post('/posts','PostController.store')

Route.get('/posts/create','PostController.create')

Route.get('/posts/:id','PostController.show')

Route.patch('/posts/:id','PostController.update')

Route.delete('/posts/:id','PostController.destroy')

Route.get(‘/posts/:id/edit','PostController.edit')

更简单的方法 resource方法

1
2
3
4
Route
.resource('posts','PostController')
.apiOnly() //只需要提供接口,定义资源路由,去掉创建和编辑资源的路由
.only([‘index','show',]) //只提供某些方法

其他

路由命名:as的方法

1
2
3
Route
.get('/users',()=>'list of users.')
.as('users.index') //get的方法是地址,as命名路由的名字

改变html,连接的到路由的名字

1
2
<a style="color:#fff" href="{{ route('users.index') }}">List of users</a>

后面改get的地址,还是不会变,因为连接到的是路由的名字

1
.get('/list-of-users',()=>'list of users.')

路由格式:

比如json,html等,判断用户请求的路由格式,然后根据不同的格式,做出不同的响应.
Route.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Route.get('/users',({ request }) => {
switch (request.format()) {
case 'json':
return [
{name:'sunwei'},
{name:'sunwei2'}
]
default:
return `
-sunwei
-sunwei2
`
}
})
.formats(['json'])

//如果访问localhost:3333/users.json 就是json格式的数据,如果强制请求就是:
.formats(['json','html'],true)

  1. 访问:localhost:3333/users

路由群组:

如果有一些路由,需要共享一些特性,比如使用同样的地址前缀,中间件,域名,或者格式。我们可以把这些路由放在一个群组里,然后统一去设置这组路由的特性

1
2
3
4
5
6
Route
.group(()=>{
Route.get('users',()=>'manage users')
Route.get('posts',()=>'manage posts')
})
.prefix('admin')

有个前缀admin
访问localhost:3333/admin/users

单页面应用的路由:

如果我们打算为应用再配一个前端应用,那我们的后端应用可以只负责接口部分,就是只为前端应用提供数据或者前端发过来的数据.
这样应用的路由部分是在前端那里处理的,所以后端这块只定义接口的路由就行了,这样我们可以定义一个 wildcard route:通配类型的路由

1
Route.any('*',({view})=>view.render('视图名字')) //比如*表示所有的东西。没有定义的路由地址,返回的就是这个welcome(视图名字)的视图

node.js 请求与相应

准备

  1. 得到请求里面的主体内容,先得给应用安装一个 body-parser中间件
    如果是完整版的话就不需要,不然执行:

    1
    adonis install @adonisjs/bodyparser
  2. 安装完后,注册一个provider,在start的app.js:

    1
    '@adonisjs/bodyparser/providers/BodyParserProvider'
  3. 在kernel.js里面也要注册全局,在globalMiddleware:

    1
    'Adonis/Middleware/BodyParser'
  4. 定义一个路由:

    1
    2
    3
    Route
    .get('/posts',({request})=>{
    })

    查询参数

    查询参数

    查询参数会作为请求地址的一部分,地址里面的问号后面的东西就是查询参数,还有它们的值,不同的查询参数之间可以使用 & 符号连接到一块儿

    1
    localhost:3333/posts?status=pulish&orderby=create_at

获得查询参数:request.get()

设置路由,route.js

1
2
3
4
Route
.get('/posts',({request})=>{
console.log(request.get());
})

访问:localhost:3333/posts?status=pulish&orderby=create_at
返回:
1
{ status: 'pulish', orderby: 'create_at' }

直接返回得到的值再响应给客户端:
1
2
3
Route
.get('/posts',({request})=>request.get()
)

访问后:localhost:3333/posts?status=pulish&orderby=create_at

请求数据

请求中的数据

  • 从客户端那里可以向应用的服务端发送数据,服务端收到客户端发送过来的数据,可以决定怎么去处理这些数据,比如把它们保存在应用的数据库里。
  • 一般我们会在应用的服务端定义可以处理客户端发送过来的数据用的路由,然后在应用的前端,或者移动端,可以准备一个表单,让用户在上面填写一些内容,用户提交了表单,会把表单里的数据发送给后端应用。就是带着表单里的数据,去请求支持处理发送数据用的路由的地址。
  • 请求用的 HTTP 方法一般是 POST,PUT 或者 PATCH ,在这些请求里面,可以包含一个请求的主体, 这个主体里的东西就是要发送给后端应用的数据 。
    这个主体内容有不同的格式,比如表单,或者JSON 等等。 在服务端那里应该知道怎么去处理这些使用不同格式发送过来的数据。

获取请求中的数据

发送了一个数据请求,又返回来,实际可以保存在数据库之中。

1
2
3
4
5
6
Route.post('/posts',({request})=>request.post())
//
Route.post('/posts',({request})=>request.all())//获得查询参数
Route.post('/posts',({request})=>request.only(['title','content']))//单独只获得一组数据
Route.post('/posts',({request})=>request.except(['title','content']))//除了某组数据
Route.post('/posts',({request})=>request.input('status','draft'))//获得某个具体的数据,比如status,如果没有的话,值就是第二个参数。

备注:
请求的数据是body,json格式的,注意最后一个没有,
{
​ “title”:”name”,
​ “content”:”sunwei”,
​ “status”:”pulish”
}

  1. 返回的就是所有的body的json值
  2. 访问localhost:3333/posts?redirect=home,返回的就是所有的body的json值,加上查询的参数值
  3. 只有title和content的值
  4. 除了title和content的值
  5. 只查询status的值,第二个参数是默认的,如果没有其值的话

请求中的集合数据:request.collect

1
Route.post('/posts',({request})=>request.collect(['title','content']))

集合form数据128c5e493e540cec5fe9447d06da7dd8.png不能用requese.only的方法,用request.collect的方法
如果用request.only的话,就是返回这样的数据,无法使用

1
2
3
4
5
6
7
8
9
10
{
"title": [
"sunwei",
"sunwei2"
],
"content": [
"name",
"sex"
]
}

collect返回的数据就是:

1
2
3
4
5
6
7
8
9
[
{
"title": "sunwei",
"content": "name"
},
{
"title": "sunwei2",
"content": "sex"
}

头部信息

头部信息,它是客户端与服务端沟通交流的一种方式 . 客户端请求的时候可以带一些头部信息,服务端响应的时候也可以带一些头部信息 。
有的是自动设置的,有的时候我们也会自己额外添加,比如jwt的身份验证。

获取头部信息:headers()方法

get的headers的方法,请求回来的值就是所有的头部信息

1
Route.get('/posts',({request})=>request.headers()) 

具体的一条头部信息:
1
Route.get('/posts',({request})=>request.header('user-agent'))

设置响应头部信息:response.header

1
2
3
Route.get('/posts',({request})=>{
return '<h1>List of posts.</h1>'
})

这时候响应的是html;可以在控制台network上看到
设置:

1
2
3
4
5
Route.get('/posts',({request,response})=>{
// response.header('Content-type','text/plain'),respense.header方法设置响应的头部信息,后面是两个参数的值。
response.type('text/plain')//这是简单的方法
return '<h1>List of posts.</h1>'
})

这样就是纯文本信息了。查询用MIME type查询

Cookies

服务端在客户端那里存储的一小块数据 ,在客户端那里存储数据,除了 cookie,我们还可以使用 Storage API ,比如 localStorage,sessionStorage ,还有 Indexed DB 。
设置和获取cookies

1
2
3
Route.get('/posts',({request,response})=>{
return request.headers()
})

请求:localhost:3333/posts
可以看到头部信息里面cookies的数据,在application里面可以找到cookies的数据,
1、读取cookie数据,可以用request.cookies的方法.
2、设置cookie可以用response的cookie方法,第一个是cookie的名字,第二个是cookie的值
3、具体某个值可以用cookie(‘’)的方法,第二个参数是默认的值
4、删除cookie:response.clearCookie(‘sunwei’)

1
2
3
4
5
6
Route.get('/posts',({request,response})=>{
response.cookie('sunwei','nb')
response.clearCookie('sunwei')
// return request.cookies()
return request.cookie('sunwei','light')
})

响应

  • 客户端对服务端发出各种请求,服务端可以根据请求响应回对应的数据 做出响应的时候除了带着具体的请求需要的数据,还可以设置响应的头部信息,Cookies ,还有状态码这些东西。在客户端那里可以根据这些信息来判断到底应该怎么使用响应回来的数据
  • Get的方法,响应数据用response.send()方法。
    1
    2
    3
    4
    5
    6
    7
    Route.get('/posts',({response})=>{
    // response.send('list of post')
    // return 'list of posts.'
    return {
    title:'list of posts.'
    }
    })
    这边访问后,是text的格式
    如果返回的是一个{},那就是自动成为json格式的数据

异步响应:async,await

对请求做出响应的时候,有可能不会马上得到要响应的数据。我们可以使用 async,await,这就需要执行的操作必须返回的是 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const delay = (data,time) =>{
return new Promise((resolve,reject) => {
setTimeout(()=>{
resolve(data)
},time)
})
}

Route.get('/posts',async ({response})=>{
const data =await delay(
'list of posts',
3000 )//一个是数据,一个是响应时间
return data
})

重定向:Route.get('/list-of-posts',({response})=>{
response.redirect('/posts')
})

Route.get('/posts',()=>{
return 'List of posts'
})
Route.get('/list-of-posts',({response})=>{
response.redirect('/posts')
})

Route.get('/posts',()=>{
return 'List of posts'
})

重定向

访问一个地址,再把请求重新定向到另一个地址:
get方法,response.redirect()方法

1
2
3
重定向:Route.get('/list-of-posts',({response})=>{
response.redirect('/posts',true,301)//重定向带头部信息查询参数,第二个地址为true,状态码为第三个参数 301
})

使用路由名称和response.route方法重定向以及带参:
1
2
3
4
5
6
7
8
9
Route.get('/list-of-posts',({response})=>{
response.route('list-of-posts',{catagory:food} )
})//第二个参数是catagory

Route
.get('/posts/:catagory?',({params})=>{
return `List of ${params.catagory || 'default'} posts`
})
.as('list-of-posts')

视图

准备与介绍

模版引擎:Edge.js
app.js 的viewprodiver
设置一个资源的路由:(文章,评论,用户

1
Route.resource('/posts','PostController')

cmd创建一个一个视图文件:
adonis make:view posts.index
然后在index.edge里面搞一个h1
访问: localhost:3333/posts
传递与使用值
在视图里,绑定输出传递过来的数据, 用的是两组大括号,里面添加一个 pageTitle
控制器里:
1
2
3
4
5
6
class PostController {
index({view}){
const pageTitle = 'list of posts '
return view.render('posts.index',{pageTitle})
}
}

view视图里面
1
<h1>{{ pageTitle || 'Untitled' }} </h1>

如果前面加@。则不会渲染两组大括号
如果要渲染控制器的html标签 则是3组大括号

条件 @if

判断条件,可以使用一个 @if ,在 Edge 模板引擎里面,这些用 @ 符号开头的东西一般叫 Tags , if 是个标签的名字 ,endif结尾,其他用elseif来,或者else
index.edge:

1
2
3
4
5
@if(user.name)
{{user.name}}
@else
login/signup
@endif

控制器:
1
2
3
4
5
6
7
8
9
class PostController {
index({view}){
const pageTitle = 'list of posts '
const user ={
name:'sunwei'
}
return view.render('posts.index',{pageTitle,user})
}
}

迭代:Iteration

用@each方法
index.edge

1
2
3
4
5
6
7
8
@each(entity in entities)
<div>
<small>
{{$loop.index+1}}.
</small>
{{entity.title}}:{{entity.content}}
</div>
@endeach

布局:

就是在视图里面可以重复使用的页面的模板
放在views——layouts——main.js
在index.edge中

1
@layout(‘layouts.main')

设置section
如果没有设置内容,就显示section