koa与node.js开发实践

雨燕双飞 提交于 2020-01-25 08:32:42

node.js入门

使用 Node.js 搭建一个 HTTP Server

const http = require (’ http ’ );

npm

koa 基础(在基础视频中补充)

koa 的安装与搭建

Context对象

Koa将node.js的request(请求)和Response(响应)对象封装到
Context对象中,所以也可以把Context对象称为一次对话的上下文,通过加工Context对象,就可以控制返回给用户的内容。

ctx.request get请求

  • url: ctx. request. url, //获取请求URL
  • query: ctx.request.query,//获取解析的查询字符串
  • querystring : ctx.request . querystring //获取原始查询字符串

post请求获取参数

 const koa = require ( ' koa
 const app = new koa(); 
 app.use(async (ctx) => { 
    let postdata = ''; 
    ctx.req.on('data', (data) => { 
        postdata += data ; 
    });
    ctx.req.on ('end', ()=>{
        console.log(postdata);
    });
    ctx.body='hello koa2'
 })
 app.listen(3000);
 
 ctx.request.method === 'POST') { //判断是否为POST请求
 ctx.request.path !== '/' 判断请求路径

ctx.response

  • ctx.response.status=200;//设置请求的状态码为200
  • ctx.response.type = ‘json’ // 设置相应数据类型
  • ctx.response.body = {// 设置响应体内容
    data: ‘data’
    }

ctx.state

ctx.state是推荐命名空间,用于通过中间件传递信息和前端视图。类koa-views些渲染View层的中间件也会默认把ctx.state面的属性作为View的上下文传入ctx.state .user = yield User .find(id) 上述代码是把user属性存放ctx.state象里,以便能够被另一个中间件读取

ctx.cookies

ctx . cookies . get (name , [options J ) ; //获取Cookie
ctx .cookies .set(name, value , [options]); //设置Cookie

ctx.throw

ctx.throw用于抛出错误,把错误信息返给用户,

app .use(async (ctx) => { 
ctx.throw(500); 
}); 运行这段示例代码,会在页面上看到个状态码为500的错误页

koa 的中间件洋葱模型

app.use(async (ctx, next) => {
    console.log('one')
    await next()
    console.log('one end')
})
app.use(async (ctx, next) => {
    console.log('two')
    await next()
    console.log('two end')
})
app.use(async (ctx, next) => {
    console.log('three')
    await next()
    console.log('three end')
})

如果将多个中间件组合成一个单一的中便于重用或导出,可以使用koa-compose

const compose = require ('koa-compose')
async function func1(ctx, next){
    // dosomeing
    await next()
}
async function func2(ctx, next){
    // dosomeing
    await next()
}
async function func3(ctx, next){
    // dosomeing
    await next()
}
const all = compose([func1 , func2, func3])
app.use(all) ; 

常用Koa中间件介绍

koa-bodyparser中间件

koa-bodyparser中间件可以把POST请求的参数解析到ctx.request.body
koa-bodyparser中间件最终解析出来的参数是一个对象。
安装
npm install --save koa-bodyparser

使用
const bodyParser = require('koa-bodyparser')
router.post('/news', async (ctx, next)=>{
    ctx.body = ctx.request.body
})
app.use(bodyParser())
    .use(router.routes()); //作用:启动路由
    .use(router.allowedMethods()); // 作用: 这是官方文档的推荐用法,我们可以

---

app.use(bodyParser())
app.use(router.routes()); //作用:启动路由
app.use(router.allowedMethods()); // 作用: 这是官方文档的推荐用法,我们可以

koa-router中间件

安装
npm install --save koa -router 
使用
const router = require('koa-router')();
或者
const Router = require('koa-router')
const router = new Router() //初始化koa-router中间件

koa-router 的安装和介绍

npm install koa-router -save

restful规范

表现层状态转移
CRUD 增删改查
restful 只提供一个uri =

https//api.test.com/users        //POST 方法 请求发送新增用户信息
https//api.test.com/users/:id    //DELETE 方法,用户 ID URI 的一部分
https//api.test.com/users/:id    // PUT 方法,请求发送用户 信息 ID UR 一部
https//api.test.com/users/:id    //GET 方法,用户 ID URI 的一部分

GitHub v4 的 API 使用了全新的设计风格 GraphQL

router 
  .post('/users', (ctx, next) => { 
    ctx .body ='新增了一位用户'
  }) 
  .del('/users/:id', (ctx, next) => { 
    ctx .body ='删除了用户编号为 id 的用户'
  }) 
 .put('/users/:id', (ctx, next) => { 
  ctx .body ='修改了用户编号为 id 的用户信息'
}) 
 .get('/users/:id', (ctx, next) => { 
    ctx .body ='我是编号为工 的用户信息'
  }) 

koa-router 用法

基本用法

router 
  .post('/users', async (ctx, next) => { 
    ctx.body ='新增了一位用户'
  }) 
  .del('/users/:id', async (ctx, next) => { 
    ctx.body ='删除了用户编号为 id 的用户'
  }) 
 .put('/users/:id', async (ctx, next) => { 
    ctx .body ='修改了用户编号为 id 的用户信息'
 }) 
 .get('/users/:id', async (ctx, next) => { 
    ctx.body ='我是编号为工 的用户信息'
  })
  .all('/users/:id', async (ctx, next) => { 
    ctx .body ='我是编号为工 的用户信息'
  })
  
  //上述代码中还有all()方法。如果一条路由请求在all()方法和其他方法中同时命中,只有执行了 await next(),这条路由请求才会在all()方法和其他方法中都起作用,
  
 

all方法设置跨域 await next()

 在项目中, all()方法一般用来设置请求头,如设置过期时间、 CORS (Cross-Origin Resource Sharing, 跨域资源共享)
router.all('/*',async (ctx, next) => { //符号*代表允许来自所有域名的请求
    ctx.set ("Access-Control-Allow-Origin","https: //www.cctalk.com"); 
    await next () ; 
});

其他特征

命名路由

  router.get('user','/users/:id', (ctx, next) => { // 命名路由
     // dosomething
  })
  router.url('user', 3) //通过调用路由的名称 user ,生成路由 --- '/users/3'
  router.url('user', {id: 3})
多中间件

koa-router 支持单个路由多个中间件的处理。能够为一个路由添加特殊的
中间件,也可以把 个路由要做 事情拆分 多个步骤去实现。当路由处理函数中有异步
操作时 这种写法的可读性和可维护性更高,代码如下

  router.get('users/:id', (ctx, next) => { 
      return User.findOne(ctx.params.id).then(function(user) { //异步操作,首先读取用户的信息,假设用户为{ id l7 name :” Alex
        ctx.user = user; //控制权传递,调用下 个中间件
        next(); 
      }) ; 
    }, 
  (ctx , next) => { //在这个中间件中再对用户信息做一些处理
        console .log(ctx.user);
        // => { id : 17 , name :”Alex”) 
  }
 );

嵌套路由

 const forums = new Router (); 
 const posts = new Router (); 
 posts.get ('/', function (ctx, next) { 
   //
 })
 posts.get ('/:pid', function (ctx, next) { 
   //
 })
 forums.use('/forums/:fid/posts', posts.routes() , posts.allowedMethods()) ; //获取互联网版块列表的接口
 // ” / forums/ :fid/posts”=>”/forums/123/posts” B //获取互联网版块下某篇文章的接口
 //" /forums/:fid/posts/:pid”=> "/forums/123/posts/123”
app.use(forums.routes());

路由前缀

通过 prefix 参数,可以为一组路由添加统 的前缀 和嵌套路由类似,这样做有利于管
理路由及简化路由的写法, 代码如下:

 let router = new Router ( { 
   prefix:'/users'
 } ) ; 
 //匹配路由”/users"
 router.get ('/', 
  //
 ); 
 //匹配路由"/users/:id"
 router . get ('/users/:id',
  //
 ) ;

与嵌套路由不同的是,路由前缀是一个固定的字符串,不能添加动态参数

URL参数

koa-router 也支持参数,参数会被添加到 ctx.params 中。参数可以是一个正则表达式,这个功能的实现是通过 path-to-regexp 来实现的。原理是把 URL 字符串转化成正则对象,然后再进行正则匹配,之前的例子中的 * 通配符就是一种正则表达式。

router.get('/:category/:title', function (ctx, next) {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' } 
});

通过koa-router 来实现接口权限控制(JWT)

校验并不依赖于服务器端的Session机制,通过koa-jwt中间件来验证token,JWT会解码井校验Token。

http

完整的url

假设这是一个url地址http://localhost:8080/a/b/c?a=1&b=2#abc,里面包含的部分:
protocol: 'http:',//协议
host: 'localhost:8080',
port: '8080',//端口
hostname: 'localhost',域名
fagment: '#abc', // 定位描点
search: '?a=1&b=2',
query: 'a=1&b=2',
pathname: '/a/b/c',
path: '/a/b/c?a=1&b=2',
href: 'http://localhost:8080/a/b/c?a=1&b=2#abc'

常用的HTTP状态码

HTTP状态码主要包括1**(消息),2**(成功), 3**(重定向), 4**(请求错误), 5** 和6**(服务器错误)

9种http请求方法

GET:获取资源
POST:传输实体文本
HEAD:获得报文首部
PUT:传输文件
DELETE:删除文件
OPTIONS:询问支持的方法
TRACE:追踪路径
CONNECT:要求用隧道协议连接代理

常用的HTTP首部字段(待补)

querystring模块

querystring模块的使用

引入

const querystring = require('querystring')

第一种方法 escape 对传入的字符串进行编码

querystring .escape ("id=1") // 返回id%3D1

第二种方法 unescape对传入的字符串进行解码

querystring .unescape ("id%3Dl") // 返回id = 1

第三种方法 parse, 将传入的字符串反序列化为对象

querystring.parse("type=l&status=0")  // 返回 { type: '1', status: '0'}

第三种方法 stringify, 将传入的字符串反序列化为对象

querystring.stringify( { type: '1', status: '0'})  // 返回type=l&status=0

koa-router中的querystring

console.log(ctx.request.query);  // query 对象
console.log(ctx.request.querystring) ; //querystring字符串

构建koa Web应用

koa 中的MVC

分离 router

路由部分的代码可以分离成一个独立的文件,并根据个人喜好放置于项目根目录下,或独立放置于 router 文件夹中。在这里,我们将它命名为 router.js并将之放置于根目录下。
router.js

const router = require('koa-router')()
  const HomeController = require('./controller/home')
  module.exports = (app) => {
    router.get( '/', HomeController.index )

    router.get('/home', HomeController.home)

    router.get('/home/:id/:name', HomeController.homeParams)

    router.get('/user', HomeController.login)

    router.post('/user/register', HomeController.register)

    app.use(router.routes())
      .use(router.allowedMethods())
  }

app.js

  const Koa = require('koa')
  const bodyParser = require('koa-bodyparser')
  const app = new Koa()
  const router = require('./router')

  app.use(bodyParser())

  router(app)

  app.listen(3000, () => {
    console.log('server is running at http://localhost:3000')
  })

分离controller

module.exports = {
    index: async(ctx, next) => {
      ctx.response.body = `<h1>index page</h1>`
    },
    home: async(ctx, next) => {
      console.log(ctx.request.query)
      console.log(ctx.request.querystring)
      ctx.response.body = '<h1>HOME page</h1>'
    },
    homeParams: async(ctx, next) => {
      console.log(ctx.params)
      ctx.response.body = '<h1>HOME page /:id/:name</h1>'
    },
    login: async(ctx, next) => {
      ctx.response.body =
        `
        <form action="/user/register" method="post">
          <input name="name" type="text" placeholder="请输入用户名:ikcamp"/> 
          <br/>
          <input name="password" type="text" placeholder="请输入密码:123456"/>
          <br/> 
          <button>GoGoGo</button>
        </form>
      `
    },
    register: async(ctx, next) => {
      let {
        name,
        password
      } = ctx.request.body
      if (name == 'ikcamp' && password == '123456') {
        ctx.response.body = `Hello, ${name}!`
      } else {
        ctx.response.body = '账号信息错误'
      }
    }
  }

目前的代码结构已经比较清晰了,适用于以 node 作为中间层、中转层的项目。如果想要把 node 作为真正的后端去操作数据库等,建议再分出一层 service,用于处理数据层面的交互,比如调用 model 处理数据库,调用第三方接口等,而controller 里面只做一些简单的参数处理。

分离 service 层

  module.exports = {
    register: async(name, pwd) => {
      let data 
      if (name == 'ikcamp' && pwd == '123456') {
        data = `Hello, ${name}!`
      } else {
        data = '账号信息错误'
      }
      return data
    }
  }
  
  修改 controller/home.js

// 引入 service 文件
const HomeService = require('../service/home')
module.exports = {
  // ……省略上面代码
  // 重写 register 方法 
  register: async(ctx, next) => {
    let {
      name,
      password
    } = ctx.request.body
    let data = await HomeService.register(name, password)
    ctx.response.body = data
  }
}

数据库

Sequelize

降低开发成本但是不高效

安装

npm install sequelize -save

链接数据库


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!