• Koa1 框架


    安装创建项目:

    1.一定要全局安装(koa1.2和koa2都己经支持)

    npm install koa-generator -g 

    2.
    koa1 生成一个test项目,切到test目录并下载依赖

    koa1创建项目

    koa test
    cd test 
    npm install
    运行:npm start
    访问:http://localhost:3000

    Koa是一个类似于Express的Web开发框架,创始人也是同一个人。它的主要特点是,使用了ES6的Generator函数,进行了架构的重新设计。也就是说,Koa的原理和内部结构很像Express,但是语法和内部结构进行了升级。

    官方faq有这样一个问题:”为什么koa不是Express 4.0?“,回答是这样的:”Koa与Express有很大差异,整个设计都是不同的,所以如果将Express 3.0按照这种写法升级到4.0,就意味着重写整个程序。所以,我们觉得创造一个新的库,是更合适的做法。“

    Koa应用

    一个Koa应用就是一个对象,包含了一个middleware数组,这个数组由一组Generator函数组成。这些函数负责对HTTP请求进行各种加工,比如生成缓存、指定代理、请求重定向等等。

    1 var koa = require('koa');
    2 var app = koa();
    3 
    4 app.use(function *(){
    5   this.body = 'Hello World';
    6 });
    7 
    8 app.listen(3000);
    要安装koa才能测试

    上面代码中,变量app就是一个Koa应用。它监听3000端口,返回一个内容为Hello World的网页。

    app.use方法用于向middleware数组添加Generator函数。

    listen方法指定监听端口,并启动当前应用。它实际上等同于下面的代码

    1 var http = require('http');
    2 var koa = require('koa');
    3 var app = koa();
    4 http.createServer(app.callback()).listen(3000);

    中间件

    下面是一个两个中间件级联的例子

    1 app.use(function *() {
    2   this.body = "header
    ";
    3   yield saveResults.call(this);
    4   this.body += "footer
    ";
    5 });
    6 
    7 function *saveResults() {
    8   this.body += "Results Saved!
    ";
    9 }

    上面代码中,第一个中间件调用第二个中间件saveResults,它们都向this.body写入内容。最后,this.body的输出如下。

     1 header 2 Results Saved! 3 footer 

    只要有一个中间件缺少yield next语句,后面的中间件都不会执行,这一点要引起注意。

    如果想跳过一个中间件,可以直接在该中间件的第一行语句写上return yield next

    1 app.use(function* (next) {
    2   if (skip) return yield next;
    3 })

    路由

    可以通过this.path属性,判断用户请求的路径,从而起到路由作用。

     1 app.use(function* (next) {
     2   if (this.path === '/') {
     3     this.body = 'we are at home!';
     4   } else {
     5     yield next;
     6   }
     7 })
     8 
     9 // 等同于
    10 
    11 app.use(function* (next) {
    12   if (this.path !== '/') return yield next;
    13   this.body = 'we are at home!';
    14 })

    下面是多路径的例子。

     1 let koa = require('koa')
     2 
     3 let app = koa()
     4 
     5 // normal route
     6 app.use(function* (next) {
     7   if (this.path !== '/') {
     8     return yield next
     9   }
    10 
    11   this.body = 'hello world'
    12 });
    13 
    14 // /404 route
    15 app.use(function* (next) {
    16   if (this.path !== '/404') {
    17     return yield next;
    18   }
    19 
    20   this.body = 'page not found'
    21 });
    22 
    23 // /500 route
    24 app.use(function* (next) {
    25   if (this.path !== '/500') {
    26     return yield next;
    27   }
    28 
    29   this.body = 'internal server error'
    30 });
    31 
    32 app.listen(8080)

    上面代码中,每一个中间件负责一个路径,如果路径不符合,就传递给下一个中间件。

    复杂的路由需要安装koa-router插件。

     1 var app = require('koa')();
     2 var Router = require('koa-router');
     3 
     4 var myRouter = new Router();
     5 
     6 myRouter.get('/', function *(next) {
     7   this.response.body = 'Hello World!';
     8 });
     9 
    10 app.use(myRouter.routes());
    11 
    12 app.listen(3000);

    上面代码对根路径设置路由。

    Koa-router实例提供一系列动词方法,即一种HTTP动词对应一种方法。典型的动词方法有以下五种。

    • router.get()
    • router.post()
    • router.put()
    • router.del()
    • router.patch()

    这些动词方法可以接受两个参数,第一个是路径模式,第二个是对应的控制器方法(中间件),定义用户请求该路径时服务器行为。

    1 router.get('/', function *(next) {
    2   this.body = 'Hello World!';
    3 });

    上面代码中,router.get方法的第一个参数是根路径,第二个参数是对应的函数方法。

    注意,路径匹配的时候,不会把查询字符串考虑在内。比如,/index?param=xyz匹配路径/index

    有些路径模式比较复杂,Koa-router允许为路径模式起别名。起名时,别名要添加为动词方法的第一个参数,这时动词方法变成接受三个参数。

    1 router.get('user', '/users/:id', function *(next) {
    2  // ...
    3 });
    上面代码中,路径模式users:id的名字就是user。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,
    结合给定的参数,生成具体的路径。

    Koa-router允许为路径统一添加前缀。
    1 var router = new Router({
    2   prefix: '/users'
    3 });
    4 
    5 router.get('/', ...); // 等同于"/users"
    6 router.get('/:id', ...); // 等同于"/users/:id"

    路径的参数通过this.params属性获取,该属性返回一个对象,所有路径参数都是该对象的成员。

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

    param方法可以针对命名参数,设置验证条件。

     1 router
     2   .get('/users/:user', function *(next) {
     3     this.body = this.user;
     4   })
     5   .param('user', function *(id, next) {
     6     var users = [ '0号用户', '1号用户', '2号用户'];
     7     this.user = users[id];
     8     if (!this.user) return this.status = 404;
     9     yield next;
    10   })

    上面代码中,如果/users/:user的参数user对应的不是有效用户(比如访问/users/3),param方法注册的中间件会查到,就会返回404错误。

    redirect方法会将某个路径的请求,重定向到另一个路径,并返回301状态码。

    1 router.redirect('/login', 'sign-in');
    2 
    3 // 等同于
    4 router.all('/login', function *() {
    5   this.redirect('/sign-in');
    6   this.status = 301;
    7 });

    redirect方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。

    错误处理机制

    1 app.use(function *() {
    2   try {
    3     yield saveResults();
    4   } catch (err) {
    5     this.throw(400, '数据无效');
    6   }
    7 });

    上面代码自行部署了try…catch代码块,一旦产生错误,就用this.throw方法抛出。该方法可以将指定的状态码和错误信息,返回给客户端。

    对于未捕获错误,可以设置error事件的监听函数。

    1 app.on('error', function(err){
    2   log.error('server error', err);
    3 });

    this.throw方法用于向客户端抛出一个错误。

     1 this.throw(403);
     2 this.throw('name required', 400);
     3 this.throw(400, 'name required');
     4 this.throw('something exploded');
     5 
     6 this.throw('name required', 400)
     7 // 等同于
     8 var err = new Error('name required');
     9 err.status = 400;
    10 throw err;

    this.throw方法的两个参数,一个是错误码,另一个是报错信息。如果省略状态码,默认是500错误。

    this.assert方法用于在中间件之中断言,用法类似于Node的assert模块

    1 this.assert(this.user, 401, 'User not found. Please login!');

    上面代码中,如果this.user属性不存在,会抛出一个401错误。

    cookie的读取和设置。

    1 this.cookies.get('view');
    2 this.cookies.set('view', n);

    get和set方法都可以接受第三个参数,表示配置参数。其中的signed参数,用于指定cookie是否加密。如果指定加密的话,必须用app.keys指定加密短语。

    1 app.keys = ['secret1', 'secret2'];
    2 this.cookies.set('name', '张三', { signed: true });

    this.cookie的配置对象的属性如下。

    • signed:cookie是否加密。
    • expires:cookie何时过期
    • path:cookie的路径,默认是“/”。
    • domain:cookie的域名。
    • secure:cookie是否只有https请求下才发送。
    • httpOnly:是否只有服务器可以取到cookie,默认为true。

    session

     1 var session = require('koa-session');
     2 var koa = require('koa');
     3 var app = koa();
     4 
     5 app.keys = ['some secret hurr'];
     6 app.use(session(app));
     7 
     8 app.use(function *(){
     9   var n = this.session.views || 0;
    10   this.session.views = ++n;
    11   this.body = n + ' views';
    12 })
    13 
    14 app.listen(3000);
    15 console.log('listening on port 3000');
     1 可以把session存到mysql中
     2 安装npm install koa-generic-session --save-dev
     3 2.app.js中
     4 var session = require('koa-generic-session');
     5 
     6 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
     7 app.use(session());
     8 
     9 3.
    10 this.session.loginbean
    11 
    12 方法二:
    13 session映射到mysql
    14 1.加安装
    15 npm install mysql --save-dev
    16 npm install koa-mysql-session --save-dev
    17 
    18 app.js中:
    19 var session = require('koa-generic-session');
    20 const mysql = require('mysql');
    21 const MysqlStore = require('koa-mysql-session');
    22 
    23 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
    24 app.use(session({store:new MysqlStore({
    25   host: 'localhost',       //主机  
    26   user: 'root',               //MySQL认证用户名  
    27   password: 'root',        //MySQL认证用户密码  
    28   database: 'kameng',  
    29   port: '3306',                   //端口号 
    30   acquireTimeout:0
    31 })}));

    Request对象

    Request对象表示HTTP请求。

    (1)this.request.header

    返回一个对象,包含所有HTTP请求的头信息。它也可以写成this.request.headers

    (2)this.request.method

    返回HTTP请求的方法,该属性可读写。

    (3)this.request.length

    返回HTTP请求的Content-Length属性,取不到值,则返回undefined。

    (4)this.request.path

    返回HTTP请求的路径,该属性可读写。

    (5)this.request.href

    返回HTTP请求的完整路径,包括协议、端口和url。

    1 this.request.href
    2 // http://example.com/foo/bar?q=1

    (6)this.request.querystring

    返回HTTP请求的查询字符串,不含问号。该属性可读写。

    (7)this.request.search

    返回HTTP请求的查询字符串,含问号。该属性可读写。

    (8)this.request.host

    返回HTTP请求的主机(含端口号)。

    (9)this.request.hostname

    返回HTTP的主机名(不含端口号)。

    (10)this.request.type

    返回HTTP请求的Content-Type属性

    1 var ct = this.request.type;
    2 // "image/png"

    (11)this.request.charset

    返回HTTP请求的字符集。

    1 this.request.charset
    2 // "utf-8

    路由

    可以通过this.path属性,判断用户请求的路径,从而起到路由作用。

    app.use(function* (next) {
      if (this.path === '/') {
        this.body = 'we are at home!';
      } else {
        yield next;
      }
    })
    
    // 等同于
    
    app.use(function* (next) {
      if (this.path !== '/') return yield next;
      this.body = 'we are at home!';
    })
    

    下面是多路径的例子。

    let koa = require('koa')
    
    let app = koa()
    
    // normal route
    app.use(function* (next) {
      if (this.path !== '/') {
        return yield next
      }
    
      this.body = 'hello world'
    });
    
    // /404 route
    app.use(function* (next) {
      if (this.path !== '/404') {
        return yield next;
      }
    
      this.body = 'page not found'
    });
    
    // /500 route
    app.use(function* (next) {
      if (this.path !== '/500') {
        return yield next;
      }
    
      this.body = 'internal server error'
    });
    
    app.listen(8080)
    

    上面代码中,每一个中间件负责一个路径,如果路径不符合,就传递给下一个中间件。

    复杂的路由需要安装koa-router插件。

    var app = require('koa')();
    var Router = require('koa-router');
    
    var myRouter = new Router();
    
    myRouter.get('/', function *(next) {
      this.response.body = 'Hello World!';
    });
    
    app.use(myRouter.routes());
    
    app.listen(3000);
    

    上面代码对根路径设置路由。

    Koa-router实例提供一系列动词方法,即一种HTTP动词对应一种方法。典型的动词方法有以下五种。

    • router.get()
    • router.post()
    • router.put()
    • router.del()
    • router.patch()

    这些动词方法可以接受两个参数,第一个是路径模式,第二个是对应的控制器方法(中间件),定义用户请求该路径时服务器行为。

    router.get('/', function *(next) {
      this.body = 'Hello World!';
    });
    

    上面代码中,router.get方法的第一个参数是根路径,第二个参数是对应的函数方法。

    注意,路径匹配的时候,不会把查询字符串考虑在内。比如,/index?param=xyz匹配路径/index

    有些路径模式比较复杂,Koa-router允许为路径模式起别名。起名时,别名要添加为动词方法的第一个参数,这时动词方法变成接受三个参数。

    router.get('user', '/users/:id', function *(next) {
     // ...
    });
    

    上面代码中,路径模式users:id的名字就是user。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,结合给定的参数,生成具体的路径。

    router.url('user', 3);
    // => "/users/3"
    
    router.url('user', { id: 3 });
    // => "/users/3"
    

    上面代码中,user就是路径模式的名称,对应具体路径/users/:id。url方法的第二个参数3,表示给定id的值是3,因此最后生成的路径是/users/3

    Koa-router允许为路径统一添加前缀。

    var router = new Router({
      prefix: '/users'
    });
    
    router.get('/', ...); // 等同于"/users"
    router.get('/:id', ...); // 等同于"/users/:id"
    

    路径的参数通过this.params属性获取,该属性返回一个对象,所有路径参数都是该对象的成员。

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

    param方法可以针对命名参数,设置验证条件。

    router
      .get('/users/:user', function *(next) {
        this.body = this.user;
      })
      .param('user', function *(id, next) {
        var users = [ '0号用户', '1号用户', '2号用户'];
        this.user = users[id];
        if (!this.user) return this.status = 404;
        yield next;
      })
    

    上面代码中,如果/users/:user的参数user对应的不是有效用户(比如访问/users/3),param方法注册的中间件会查到,就会返回404错误。

    redirect方法会将某个路径的请求,重定向到另一个路径,并返回301状态码。

    router.redirect('/login', 'sign-in');
    
    // 等同于
    router.all('/login', function *() {
      this.redirect('/sign-in');
      this.status = 301;
    });
    

    redirect方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。

    context对象

    中间件当中的this表示上下文对象context,代表一次HTTP请求和回应,即一次访问/回应的所有信息,都可以从上下文对象获得。context对象封装了request和response对象,并且提供了一些辅助方法。每次HTTP请求,就会创建一个新的context对象。

    app.use(function *(){
      this; // is the Context
      this.request; // is a koa Request
      this.response; // is a koa Response
    });
    

    context对象的很多方法,其实是定义在ctx.request对象或ctx.response对象上面,比如,ctx.type和ctx.length对应于ctx.response.type和ctx.response.length,ctx.path和ctx.method对应于ctx.request.path和ctx.request.method。

    context对象的全局属性。

    • request:指向Request对象
    • response:指向Response对象
    • req:指向Node的request对象
    • res:指向Node的response对象
    • app:指向App对象
    • state:用于在中间件传递信息。
    this.state.user = yield User.find(id);
    

    上面代码中,user属性存放在this.state对象上面,可以被另一个中间件读取。

    context对象的全局方法。

    • throw():抛出错误,直接决定了HTTP回应的状态码。
    • assert():如果一个表达式为false,则抛出一个错误。
    this.throw(403);
    this.throw('name required', 400);
    this.throw('something exploded');
    
    this.throw(400, 'name required');
    // 等同于
    var err = new Error('name required');
    err.status = 400;
    throw err;
    

    assert方法的例子。

    // 格式
    ctx.assert(value, [msg], [status], [properties])
    
    // 例子
    this.assert(this.user, 401, 'User not found. Please login!');
    

    以下模块解析POST请求的数据。

    • co-body
    • https://github.com/koajs/body-parser
    • https://github.com/koajs/body-parsers
    var parse = require('co-body');
    
    // in Koa handler
    var body = yield parse(this);
    

    错误处理机制

    Koa提供内置的错误处理机制,任何中间件抛出的错误都会被捕捉到,引发向客户端返回一个500错误,而不会导致进程停止,因此也就不需要forever这样的模块重启进程。

    app.use(function *() {
      throw new Error();
    });
    

    上面代码中,中间件内部抛出一个错误,并不会导致Koa应用挂掉。Koa内置的错误处理机制,会捕捉到这个错误。

    当然,也可以额外部署自己的错误处理机制。

    app.use(function *() {
      try {
        yield saveResults();
      } catch (err) {
        this.throw(400, '数据无效');
      }
    });
    

    上面代码自行部署了try…catch代码块,一旦产生错误,就用this.throw方法抛出。该方法可以将指定的状态码和错误信息,返回给客户端。

    对于未捕获错误,可以设置error事件的监听函数。

    app.on('error', function(err){
      log.error('server error', err);
    });
    

    error事件的监听函数还可以接受上下文对象,作为第二个参数。

    app.on('error', function(err, ctx){
      log.error('server error', err, ctx);
    });
    

    如果一个错误没有被捕获,koa会向客户端返回一个500错误“Internal Server Error”。

    this.throw方法用于向客户端抛出一个错误。

    this.throw(403);
    this.throw('name required', 400);
    this.throw(400, 'name required');
    this.throw('something exploded');
    
    this.throw('name required', 400)
    // 等同于
    var err = new Error('name required');
    err.status = 400;
    throw err;
    

    this.throw方法的两个参数,一个是错误码,另一个是报错信息。如果省略状态码,默认是500错误。

    this.assert方法用于在中间件之中断言,用法类似于Node的assert模块。

    this.assert(this.user, 401, 'User not found. Please login!');
    

    上面代码中,如果this.user属性不存在,会抛出一个401错误。

    由于中间件是层级式调用,所以可以把try { yield next }当成第一个中间件。

    app.use(function *(next) {
      try {
        yield next;
      } catch (err) {
        this.status = err.status || 500;
        this.body = err.message;
        this.app.emit('error', err, this);
      }
    });
    
    app.use(function *(next) {
      throw new Error('some error');
    })

    CSRF攻击

    CSRF攻击是指用户的session被劫持,用来冒充用户的攻击。

    koa-csrf插件用来防止CSRF攻击。原理是在session之中写入一个秘密的token,用户每次使用POST方法提交数据的时候,必须含有这个token,否则就会抛出错误。

     1 var koa = require('koa');
     2 var session = require('koa-session');
     3 var csrf = require('koa-csrf');
     4 var route = require('koa-route');
     5 
     6 var app = module.exports = koa();
     7 
     8 app.keys = ['session key', 'csrf example'];
     9 app.use(session(app));
    10 
    11 app.use(csrf());
    12 
    13 app.use(route.get('/token', token));
    14 app.use(route.post('/post', post));
    15 
    16 function* token () {
    17   this.body = this.csrf;
    18 }
    19 
    20 function* post() {
    21   this.body = {ok: true};
    22 }
    23 
    24 app.listen(3000);

    POST请求含有token,可以是以下几种方式之一,koa-csrf插件就能获得token。

    • 表单的_csrf字段
    • 查询字符串的_csrf字段
    • HTTP请求头信息的x-csrf-token字段
    • HTTP请求头信息的x-xsrf-token字段
     
  • 相关阅读:
    LeetCode
    LeetCode
    控制反转(Ioc)
    KMP算法
    *&m与m的区别
    函数指针与函数指针数组的使用方法
    C++四种类型转换
    内存分配:堆内存,栈内存
    汇编 基础
    i++,++i 作为参数
  • 原文地址:https://www.cnblogs.com/yu-hailong/p/7628794.html
Copyright © 2020-2023  润新知