精华内容
下载资源
问答
  • chaser:koa nodejs 角-源码

    2021-07-13 12:40:22
    追逐者 koa nodejs
  • NodeJSKoa框架

    千次阅读 2019-10-27 18:57:53
    文章目录1 koa简介1.1 Koa应用1.2 中间件1.3 多个中间件的合并1.4 路由1.5 context对象1.6 错误处理机制1.7 cookie1.8 session1.9 Request对象1.9.1 this.request.header1.9.2 this.request.method1.9.3 this....

    1 koa简介

    Koa是一个类似于Express的Web开发框架,创始人也是同一个人。它的主要特点是,使用了ES6的Generator函数,进行了架构的重新设计。也就是说,Koa的原理和内部结构很像Express,但是语法和内部结构进行了升级。
    官方faq有这样一个问题:”为什么koa不是Express 4.0?“,回答是这样的:”Koa与Express有很大差异,整个设计都是不同的,所以如果将Express 3.0按照这种写法升级到4.0,就意味着重写整个程序。所以,我们觉得创造一个新的库,是更合适的做法。“

    1.1 Koa应用

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

    var koa = require('koa');
    var app = koa();
    
    app.use(function *(){
      this.body = 'Hello World';
    });
    
    app.listen(3000);
    
    • 上面代码中,变量app就是一个Koa应用。它监听3000端口,返回一个内容为Hello World的网页。
    • app.use方法用于向middleware数组添加Generator函数
    • listen方法指定监听端口,并启动当前应用。
      它实际上等同于下面的代码。
    var http = require('http');
    var koa = require('koa');
    var app = koa();
    http.createServer(app.callback()).listen(3000);
    

    1.2 中间件

    Koa的中间件很像Express的中间件,也是对HTTP请求进行处理的函数,但是必须是一个Generator函数
    而且,Koa的中间件是一个级联式(Cascading)的结构,也就是说,属于是层层调用,第一个中间件调用第二个中间件第二个调用第三个,以此类推。上游的中间件必须等到下游的中间件返回结果,才会继续执行,这点很像递归。
    中间件通过当前应用的use方法注册。

    app.use(function* (next){
      var start = new Date; // (1)
      yield next;  // (2)
      var ms = new Date - start; // (3)
      console.log('%s %s - %s', this.method, this.url, ms); // (4)
    });
    

    上面代码中,app.use方法的参数就是中间件,它是一个Generator函数最大的特征就是function命令与参数之间,必须有一个星号。Generator函数的参数next,表示下一个中间件。
    Generator函数内部使用yield命令,将程序的执行权转交给下一个中间件,即yield next,要等到下一个中间件返回结果,才会继续往下执行。

    • 上面代码中,Generator函数体内部,第一行赋值语句首先执行,开始计时,
    • 第二行yield语句将执行权交给下一个中间件,当前中间件就暂停执行
    • 等到后面的中间件全部执行完成,执行权就回到原来暂停的地方,继续往下执行,这时才会执行第三行,
    • 计算这个过程一共花了多少时间,第四行将这个时间打印出来。
      下面是一个两个中间件级联的例子。
    app.use(function *() {
      this.body = "header\n";
      yield saveResults.call(this);
      this.body += "footer\n";
    });
    
    function *saveResults() {
      this.body += "Results Saved!\n";
    }
    

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

    header
    Results Saved!
    footer
    

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

    app.use(function *(next){
      console.log('>> one');
      yield next;
      console.log('<< one');
    });
    
    app.use(function *(next){
      console.log('>> two');
      this.body = 'two';
      console.log('<< two');
    });
    
    app.use(function *(next){
      console.log('>> three');
      yield next;
      console.log('<< three');
    });
    

    上面代码中,因为第二个中间件少了yield next语句,第三个中间件并不会执行。
    如果想跳过一个中间件,可以直接在该中间件的第一行语句写上return yield next。

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

    由于Koa要求中间件唯一的参数就是next,导致如果要传入其他参数,必须另外写一个返回Generator函数的函数。

    function logger(format) {
      return function *(next){
        var str = format
          .replace(':method', this.method)
          .replace(':url', this.url);
    
        console.log(str);
    
        yield next;
      }
    }
    app.use(logger(':method :url'));
    

    上面代码中,真正的中间件是logger函数的返回值,而logger函数是可以接受参数的。

    1.3 多个中间件的合并

    由于中间件的参数统一为next(意为下一个中间件),因此可以使用.call(this, next),将多个中间件进行合并。

    function *random(next) {
      if ('/random' == this.path) {
        this.body = Math.floor(Math.random()*10);
      } else {
        yield next;
      }
    };
    
    function *backwards(next) {
      if ('/backwards' == this.path) {
        this.body = 'sdrawkcab';
      } else {
        yield next;
      }
    }
    
    function *pi(next) {
      if ('/pi' == this.path) {
        this.body = String(Math.PI);
      } else {
        yield next;
      }
    }
    
    function *all(next) {
      yield random.call(this, backwards.call(this, pi.call(this, next)));
    }
    app.use(all);
    

    上面代码中,中间件all内部,就是依次调用random、backwards、pi,后一个中间件就是前一个中间件的参数。
    Koa内部使用koa-compose模块,进行同样的操作,下面是它的源码。

    function compose(middleware){
      return function *(next){
        if (!next) next = noop();
    
        var i = middleware.length;
    
        while (i--) {
          next = middleware[i].call(this, next);
        }
    
        yield *next;
      }
    }
    
    function *noop(){}
    

    上面代码中,middleware是中间件数组。前一个中间件的参数是后一个中间件,依次类推。如果最后一个中间件没有next参数,则传入一个空函数。

    1.4 路由

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

    app.use(function* (next) {
      if (this.path === '/') {
        this.body = 'we are at home!';
      }
    })
    
    // 等同于
    
    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方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。

    1.5 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.typectx.length对应于ctx.response.typectx.response.length,ctx.path和ctx.method对应于ctx.request.path和ctx.request.method。
    context对象的全局属性。

    • request:指向Request对象
    • response:指向Response对象
    • req:指向Node的request对象
    • req:指向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;
    

    1.6 错误处理机制

    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');
    })
    

    1.7 cookie

    cookie的读取和设置。

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

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

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

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

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

    1.8 session

    var session = require('koa-session');
    var koa = require('koa');
    var app = koa();
    app.keys = ['some secret hurr'];
    app.use(session(app));
    
    app.use(function *(){
      var n = this.session.views || 0;
      this.session.views = ++n;
      this.body = n + ' views';
    })
    
    app.listen(3000);
    console.log('listening on port 3000');
    

    1.9 Request对象

    Request对象表示HTTP请求。

    1.9.1 this.request.header

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

    1.9.2 this.request.method

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

    1.9.3 this.request.length

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

    1.9.4 this.request.path

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

    1.9.5 this.request.href

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

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

    1.9.6 this.request.querystring

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

    1.9.8 this.request.host

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

    1.9.9 this.request.hostname

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

    1.9.10 this.request.type

    返回HTTP请求的Content-Type属性。

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

    1.9.11 this.request.charset

    返回HTTP请求的字符集。

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

    1.9.12 this.request.query

    返回一个对象,包含了HTTP请求的查询字符串。如果没有查询字符串,则返回一个空对象。该属性可读写。
    比如,查询字符串color=blue&size=small,会得到以下的对象。

    {
      color: 'blue',
      size: 'small'
    }
    

    1.9.13 this.request.fresh

    返回一个布尔值,表示缓存是否代表了最新内容。通常与If-None-Match、ETag、If-Modified-Since、Last-Modified等缓存头,配合使用。

    this.response.set('ETag', '123');
    // 检查客户端请求的内容是否有变化
    if (this.request.fresh) {
      this.response.status = 304;
      return;
    }
    // 否则就表示客户端的内容陈旧了,
    // 需要取出新内容
    this.response.body = yield db.find('something');
    

    1.9.14 this.request.stale

    返回this.request.fresh的相反值。

    1.9.15 this.request.protocol

    返回HTTP请求的协议,https或者http。

    1.9.16 this.request.secure

    返回一个布尔值,表示当前协议是否为https。

    1.9.17 this.request.ip

    返回发出HTTP请求的IP地址。

    1.9.18 this.request.subdomains

    返回一个数组,表示HTTP请求的子域名。
    该属性必须与app.subdomainOffset属性搭配使用。
    app.subdomainOffset属性默认为2,则域名“tobi.ferrets.example.com”返回[“ferrets”, “tobi”],
    如果app.subdomainOffset设为3,则返回[“tobi”]。

    1.9.19 this.request.is(types…)

    返回指定的类型字符串,表示HTTP请求的Content-Type属性是否为指定类型。

    // Content-Type为 text/html; charset=utf-8
    this.request.is('html'); // 'html'
    this.request.is('text/html'); // 'text/html'
    this.request.is('text/*', 'text/html'); // 'text/html'
    
    // Content-Type为s application/json
    this.request.is('json', 'urlencoded'); // 'json'
    this.request.is('application/json'); // 'application/json'
    this.request.is('html', 'application/*'); // 'application/json'
    

    如果不满足条件,返回false;如果HTTP请求不含数据,则返回undefined。

    this.is('html'); // false
    

    它可以用于过滤HTTP请求,比如只允许请求下载图片。

    if (this.is('image/*')) {
      // process
    } else {
      this.throw(415, 'images only!');
    }
    

    1.9.20 this.request.accepts(types)

    检查HTTP请求的Accept属性是否可接受,如果可接受,则返回指定的媒体类型,否则返回false。

    // Accept: text/html
    this.request.accepts('html');
    // "html"
    
    // Accept: text/*, application/json
    this.request.accepts('html');
    // "html"
    this.request.accepts('text/html');
    // "text/html"
    this.request.accepts('json', 'text');
    // => "json"
    this.request.accepts('application/json');
    // => "application/json"
    
    // Accept: text/*, application/json
    this.request.accepts('image/png');
    this.request.accepts('png');
    // false
    
    // Accept: text/*;q=.5, application/json
    this.request.accepts(['html', 'json']);
    this.request.accepts('html', 'json');
    // "json"
    
    // No Accept header
    this.request.accepts('html', 'json');
    // "html"
    this.request.accepts('json', 'html');
    // => "json"
    

    如果accepts方法没有参数,则返回所有支持的类型(text/html,application/xhtml+xml,image/webp,application/xml,/)。
    如果accepts方法的参数有多个参数,则返回最佳匹配。如果都不匹配则返回false,并向客户端抛出一个406”Not Acceptable“错误。
    如果HTTP请求没有Accept字段,那么accepts方法返回它的第一个参数。
    accepts方法可以根据不同Accept字段,向客户端返回不同的字段。

    switch (this.request.accepts('json', 'html', 'text')) {
      case 'json': break;
      case 'html': break;
      case 'text': break;
      default: this.throw(406, 'json, html, or text only');
    }
    

    1.9.21 this.request.acceptsEncodings(encodings)

    该方法根据HTTP请求的Accept-Encoding字段,返回最佳匹配,如果没有合适的匹配,则返回false。

    // Accept-Encoding: gzip
    this.request.acceptsEncodings('gzip', 'deflate', 'identity');
    // "gzip"
    this.request.acceptsEncodings(['gzip', 'deflate', 'identity']);
    // "gzip"
    

    注意,acceptEncodings方法的参数必须包括identity(意为不编码)。
    如果HTTP请求没有Accept-Encoding字段,acceptEncodings方法返回所有可以提供的编码方法。

    // Accept-Encoding: gzip, deflate
    this.request.acceptsEncodings();
    // ["gzip", "deflate", "identity"]
    

    如果都不匹配,acceptsEncodings方法返回false,并向客户端抛出一个406“Not Acceptable”错误。

    1.9.22 this.request.acceptsCharsets(charsets)

    该方法根据HTTP请求的Accept-Charset字段,返回最佳匹配,如果没有合适的匹配,则返回false。

    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
    this.request.acceptsCharsets('utf-8', 'utf-7');
    // => "utf-8"
    
    this.request.acceptsCharsets(['utf-7', 'utf-8']);
    // => "utf-8"
    

    如果acceptsCharsets方法没有参数,则返回所有可接受的匹配。

    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
    this.request.acceptsCharsets();
    // ["utf-8", "utf-7", "iso-8859-1"]
    

    如果都不匹配,acceptsCharsets方法返回false,并向客户端抛出一个406“Not Acceptable”错误。

    1.9.23 this.request.acceptsLanguages(langs)

    该方法根据HTTP请求的Accept-Language字段,返回最佳匹配,如果没有合适的匹配,则返回false。

    // Accept-Language: en;q=0.8, es, pt
    this.request.acceptsLanguages('es', 'en');
    // "es"
    this.request.acceptsLanguages(['en', 'es']);
    // "es"
    

    如果acceptsCharsets方法没有参数,则返回所有可接受的匹配。

    // Accept-Language: en;q=0.8, es, pt
    this.request.acceptsLanguages();
    // ["es", "pt", "en"]
    

    如果都不匹配,acceptsLanguages方法返回false,并向客户端抛出一个406“Not Acceptable”错误。

    1.9.24 this.request.socket

    返回HTTP请求的socket。

    1.9.25 this.request.get(field)

    返回HTTP请求指定的字段。

    1.10 Response对象

    Response对象表示HTTP回应。

    1.10.1 this.response.header

    返回HTTP回应的头信息。

    1.10.2 this.response.socket

    返回HTTP回应的socket。

    1.10.3 this.response.status

    返回HTTP回应的状态码。默认情况下,该属性没有值。该属性可读写,设置时等于一个整数。

    1.10.4 this.response.message

    返回HTTP回应的状态信息。该属性与this.response.message是配对的。该属性可读写。

    1.10.5 this.response.length

    返回HTTP回应的Content-Length字段。该属性可读写,如果没有设置它的值,koa会自动从this.request.body推断。

    1.10.6 this.response.body

    返回HTTP回应的信息体。该属性可读写,它的值可能有以下几种类型。

    • 字符串:Content-Type字段默认为text/html或text/plain,字符集默认为utf-8,Content-Length字段同时设定。
    • 二进制Buffer:Content-Type字段默认为application/octet-stream,Content-Length字段同时设定。
    • Stream:Content-Type字段默认为application/octet-stream。
    • JSON对象:Content-Type字段默认为application/json。
    • null(表示没有信息体)
      如果this.response.status没设置,Koa会自动将其设为200或204。

    1.10.7 this.response.get(field)

    返回HTTP回应的指定字段。

    var etag = this.get('ETag');
    

    注意,get方法的参数是区分大小写的。

    1.10.8 this.response.set()

    设置HTTP回应的指定字段。

    this.set('Cache-Control', 'no-cache');
    set方法也可以接受一个对象作为参数,同时为多个字段指定值。
    
    this.set({
      'Etag': '1234',
      'Last-Modified': date
    });
    

    1.10.9 this.response.remove(field)

    移除HTTP回应的指定字段。

    1.10.10 this.response.type

    返回HTTP回应的Content-Type字段,不包括“charset”参数的部分。

    var ct = this.reponse.type;
    // "image/png"
    

    该属性是可写的。

    this.reponse.type = 'text/plain; charset=utf-8';
    this.reponse.type = 'image/png';
    this.reponse.type = '.png';
    this.reponse.type = 'png';
    

    设置type属性的时候,如果没有提供charset参数,Koa会判断是否自动设置。如果this.response.type设为html,charset默认设为utf-8;但如果this.response.type设为text/html,就不会提供charset的默认值。

    1.10.10 this.response.is(types…)

    该方法类似于this.request.is(),用于检查HTTP回应的类型是否为支持的类型。
    它可以在中间件中起到处理不同格式内容的作用。

    var minify = require('html-minifier');
    
    app.use(function *minifyHTML(next){
      yield next;
    
      if (!this.response.is('html')) return;
    
      var body = this.response.body;
      if (!body || body.pipe) return;
    
      if (Buffer.isBuffer(body)) body = body.toString();
      this.response.body = minify(body);
    });
    

    上面代码是一个中间件,如果输出的内容类型为HTML,就会进行最小化处理。

    1.10.11 this.response.redirect(url, [alt])

    该方法执行302跳转到指定网址。

    this.redirect('back');
    this.redirect('back', '/index.html');
    this.redirect('/login');
    this.redirect('http://google.com');
    

    如果redirect方法的第一个参数是back,将重定向到HTTP请求的Referrer字段指定的网址,如果没有该字段,则重定向到第二个参数或“/”网址。
    如果想修改302状态码,或者修改body文字,可以采用下面的写法。

    this.status = 301;
    this.redirect('/cart');
    this.body = 'Redirecting to shopping cart';
    

    1.10.12 this.response.attachment([filename])

    该方法将HTTP回应的Content-Disposition字段,设为“attachment”,提示浏览器下载指定文件。

    1.10.13 this.response.headerSent

    该方法返回一个布尔值,检查是否HTTP回应已经发出。

    1.10.14 this.response.lastModified

    该属性以Date对象的形式,返回HTTP回应的Last-Modified字段(如果该字段存在)。该属性可写。

    this.response.lastModified = new Date();
    

    1.10.15 this.response.etag

    该属性设置HTTP回应的ETag字段。

    this.response.etag = crypto.createHash('md5').update(this.body).digest('hex');
    

    注意,不能用该属性读取ETag字段。

    1.10.16 this.response.vary(field)

    该方法将参数添加到HTTP回应的Vary字段。

    1.11 CSRF攻击

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

    var koa = require('koa');
    var session = require('koa-session');
    var csrf = require('koa-csrf');
    var route = require('koa-route');
    
    var app = module.exports = koa();
    
    app.keys = ['session key', 'csrf example'];
    app.use(session(app));
    app.use(csrf());
    app.use(route.get('/token', token));
    app.use(route.post('/post', post));
    
    function* token () {
      this.body = this.csrf;
    }
    
    function* post() {
      this.body = {ok: true};
    }
    
    app.listen(3000);
    

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

    • 表单的_csrf字段
    • 查询字符串的_csrf字段
    • HTTP请求头信息的x-csrf-token字段
    • HTTP请求头信息的x-xsrf-token字段

    1.12 数据压缩

    koa-compress模块可以实现数据压缩。

    app.use(require('koa-compress')())
    app.use(function* () {
      this.type = 'text/plain'
      this.body = fs.createReadStream('filename.txt')
    })
    
    展开全文
  • git clone https://github.com/xrr075/Koa2-Demo.git 2. 安装依赖 npm install 3. 开启项目 npm start 4. 浏览器打开 localhost:6001 5. 出现如下图所示,简单的框架就over了!!!✌️ ...

    事先准备

    环境:VScode
    npm
    node
    git

    步骤

    1. 打开终端,克隆项目到本地
    git clone https://github.com/xrr075/Koa2-Demo.git
    
    2. 安装依赖
    npm install
    
    3. 开启项目
    npm start
    
    4. 浏览器打开
    localhost:6001
    
    5. 出现如下图所示,简单的框架就over了!!!✌️

    在这里插入图片描述

    展开全文
  • DBCenter-BE技术栈 nodejs + koa2 + mysql + es6/7相关项目地址:前端项目地址目录说明DBCenter是一个协助前端工程师实现数据持久化存储的后台系统通过本系统,可以创建数据表,对该数据表进行增删改查操作同样,开放的...

    DBCenter-BE

    技术栈 nodejs + koa2 + mysql + es6/7

    相关项目地址:前端项目地址

    目录

    说明

    DBCenter是一个协助前端工程师实现数据持久化存储的后台系统

    通过本系统,可以创建数据表,对该数据表进行增删改查操作

    同样,开放的接口也具备增删改查的功能

    从而轻易地完成简单的数据库操作

    如果对此项目感兴趣,请点击star支持一下 ^_^

    项目运行

    运行前请先安装nodejs和mysql

    clone项目到本地

    git clone https://github.com/wrz199306/DBCenter-BE

    安装依赖

    npm install

    配置

    进入config.json

    完善mysql配置,无需配置msgApi

    系统登陆手机验证码验证代码已被注释, 可输入任意验证码登陆系统

    项目运行

    npm run start

    创建数据表

    登录DBCenter后,可创建数据表

    默认生成ID字段

    数据表字段类型分为3种

    字符串: VARCHAR(255)

    数值: FLOAT(32, 5)

    数据表字段可定义默认值

    数据表字段可定义必填与唯一,请珍重。

    权限认证

    接口调用, 权限认证, 采用请求头传参实现。

    名称

    类型

    是否必须

    说明

    phone

    String

    DBCenter登录所用到的手机号

    secret

    String

    DBCenter登录之后右上角获取

    接口列表

    数据插入接口

    请求方式:POST

    返回类型:JSON

    请求参数(params):

    名称

    类型

    是否必须

    说明

    tableName

    String

    将要操作的数据表名,通过DBCenter可添加数据表

    请求参数(body):

    名称

    类型

    是否必须

    说明

    attrData

    Object

    将要插入的数据,请严格遵守DBCenter中所定义的数据规则

    请求示例:

    var xhr = new XMLHttpRequest();

    // 数据表person

    xhr.open('post',`http://dbcenter.top:8888/data/person`);

    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // 头域中设置手机号和密码

    xhr.setRequestHeader("phone", "185****7810");

    xhr.setRequestHeader("secret", "******");

    // 将要插入数据表中的数据

    var attrData = {

    name: 'Scrat',

    age: 25,

    base: '北京'

    }

    xhr.send(`attrData=${JSON.stringify(attrData)}`);

    xhr.onreadystatechange = function() {

    if (xhr.readyState === 4 && xhr.status === 200) {

    console.log(xhr.responseText);

    }

    }

    正常返回示例:

    字段

    类型

    描述

    code

    Number

    0为正确, >0错误

    data

    Number

    插入的数据ID

    数据删除接口

    请求方式:DELETE

    返回类型:JSON

    请求参数(params):

    名称

    类型

    是否必须

    说明

    tableName

    String

    将要操作的数据表名,通过DBCenter可添加数据表

    请求参数(body):

    名称

    类型

    是否必须

    说明

    id

    Number

    将要删除的数据ID

    请求示例:

    var xhr = new XMLHttpRequest();

    // 数据表person

    xhr.open('delete', `http://dbcenter.top:8888/data/person`);

    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // 头域中设置手机号和密码

    xhr.setRequestHeader("phone", "185****7810");

    xhr.setRequestHeader("secret", "******");

    // 将要删除的数据的ID

    xhr.send(`id=7`);

    xhr.onreadystatechange = function() {

    if (xhr.readyState === 4 && xhr.status === 200) {

    console.log(xhr.responseText);

    }

    }

    正常返回示例:

    字段

    类型

    描述

    code

    Number

    0为正确, >0错误

    data

    Number

    success

    数据更新接口

    请求方式:PUT

    返回类型:JSON

    请求参数(params):

    名称

    类型

    是否必须

    说明

    tableName

    String

    将要操作的数据表名,通过DBCenter可添加数据表

    请求参数(body):

    名称

    类型

    是否必须

    说明

    id

    Number

    将要更新的数据ID

    newAttrData

    Object

    将要更新的数据,请严格遵守DBCenter中所定义的数据规则

    请求示例:

    var xhr = new XMLHttpRequest();

    var attrData = {

    name: '大帅哥'

    }

    // 数据表Person

    xhr.open('put', `http://dbcenter.top:8888/data/person`);

    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // 头域中设置手机号和密码

    xhr.setRequestHeader("phone", "185****7810");

    xhr.setRequestHeader("secret", "******");

    // id: 修改的数据id

    // newAttrData: 新数据的值

    xhr.send(`id=3&newAttrData=${JSON.stringify(attrData)}`);

    xhr.onreadystatechange = function () {

    if (xhr.readyState === 4 && xhr.status === 200) {

    console.log(xhr.responseText);

    }

    }

    正常返回示例:

    字段

    类型

    描述

    code

    Number

    0为正确, >0错误

    data

    Number

    success

    数据查询接口

    请求方式:GET

    返回类型:JSON

    请求参数(params):

    名称

    类型

    是否必须

    说明

    tableName

    String

    将要操作的数据表名,通过DBCenter可添加数据表

    请求参数(query):

    名称

    类型

    是否必须

    说明

    condition

    String

    查询条件,JSON字符串

    请求示例:

    var xhr = new XMLHttpRequest();

    var condition = {

    name: 'Scrat'

    }

    // 数据表Person

    xhr.open('get',`http://dbcenter.top:8888/data/person?condition=${JSON.stringify(condition)}`);

    // 头域中设置手机号和密码

    xhr.setRequestHeader("phone", "185****7810");

    xhr.setRequestHeader("secret", "****");

    xhr.send();

    xhr.onreadystatechange = function () {

    if (xhr.readyState === 4 && xhr.status === 200) {

    console.log(xhr.responseText);

    }

    }

    正常返回示例:

    字段

    类型

    描述

    code

    Number

    0为正确, >0错误

    data

    Number

    数据列表

    展开全文
  • 想使用nodejskoa)搭建一个完整的前后端,完成数据的增删改查,又不想使用数据库,那使用json文件吧。 本文介绍了基于koa的json文件的增、删、改、查。 代码准备 const Koa = require('koa') const bodyParser =...
  • Koa脚手架(Koa-NodeJs Application的Koa的脚手架)。为什么叫这个名字:因为网上基于KOA开头的项目和脚手架很多,但是方便大家好记,我采用了koa-program 。KOA代表KOA项目,PROGRM代表程式。 使用方法 一,安装 ...
  • 基于Node.js Koa2实战开发的一套完整的博客项目网站,使用Koa2二次开发套件适合多端的RESTful API,同时配套完整的后台管理系统,并前端展示既有基于ejs服务端渲染,也有基于Vue.js前瞻分离的2套前端网站。...
  • PDF编辑器 档案.env MAIL_PORT=465 MAIL_USER="xyz@gmail.com" MAIL_PASS="xyz" MAIL_HOST="smtp.google.com" 丰特斯:
  • vue+koa+nodejs 如何部署项目

    千次阅读 2018-11-01 15:38:21
    部署前端vue项目 ... 2.放入tomcat8 webapp Root下面上传linux  3.... 部署后端koa项目 安装Nodejs 1、首先安装wget,对了已经安装了可以跳过该步 yum install -y wget 2、下载nodejs最新的bi...

    部署前端vue项目
            1.webpack打包成静态文件  static 和index.html
            2.放入tomcat8 webapp Root下面上传linux
            3.启动tomcat

     部署后端koa项目

    安装Nodejs

    1、首先安装wget,对了已经安装了可以跳过该步

    yum install -y wget

    2、下载nodejs最新的bin包,可以在下载页面https://nodejs.org/en/download/中找到下载地址。然后执行下载命令下载node包

    wget https://nodejs.org/dist/v9.3.0/node-v9.3.0-linux-x64.tar.xz

    3、下载完毕后解压

    xz -d node-v9.3.0-linux-x64.tar.xz 

    tar -xf node-v9.3.0-linux-x64.tar

    4. 部署bin文件

    先确认你nodejs的路径,我这里的路径为~/node-v9.3.0-linux-x64/bin。如图:

    确认后执行

    ln -s ~/node-v9.3.0-linux-x64/bin/node /usr/bin/node 

    ln -s ~/node-v9.3.0-linux-x64/bin/npm /usr/bin/npm

    5.测试

    node -v 

    npm

    完成!这里遇到过问题 所有又找了个方法

    安装方法2——编译部署

    环境要求
    gcc 4.2
    g++4.2
    python 2.6
    gun make 3.8.1
    libexecinfo
    检查linux版本
    cat /etc/redhat-release 
    rpm -q gcc rpm -q gcc-c++
    //安装gcc
    yum -y install gcc gcc-c++ kernel-devel
    python -V
    yum install -y wget
    cd /usr/src
    wget https://nodejs.org/dist/v0.8.4/node-v0.8.4.tar.gz
    tar -xf node-v0.8.4.tar.gz
    cd node-v0.8.4
    ./configure
    make && make install

    5.测试  node -v    npm

    • 如果正确输出版本号,则部署OK

    这种方式安装,个人觉得比较有点麻烦,还有安装gcc等其他程序,对应新人来说可能比较晕。而且编译比较久,切部署完成后nodejs为分别放在好几个文件夹内:

    • /usr/local/bin –放置nodejs 执行程序

    • /usr/lib –放置了node_modules,即nodejs的各种模块

    • /usr/include –放置了nodejs扩展开发用头文件

    优点是全局安装nodejs模块,直接使用。

    接下来使用koa-generator生成koa2项目(实际上我们已经开发好了koa2项目)

    1、安装koa-generator

    npm install -g koa-generator

    koa2 kkk

      cd /home/kkk

    npm install

    npm install pm2 -g    

     pm2 start app.js

    中间遇到的问题:

    1.权限问题 npm config set registry http://registry.npmjs.org/ 

    2.版本升级 npm install -g n  n stable

    3.pm2 找不到问题  

    ln -s /root/node-v8.9.0-linux-x64/lib/node_modules/pm2/bin/pm2 /usr/local/bin
    展开全文
  • var koa = require('koa');//引用koa框架 var MockServer = require('./server/mockserver');//koa框架服务器模拟文件 var app = koa();//初始化koa //前端模拟服务器设置 var proxyServerConfig = { host: '...
  • nodejs3:koa框架

    2020-05-21 22:55:10
    const Koa = require("koa"); // 实例化koa类 const app = new Koa(); // ctx === context app.use((ctx) => { ctx.body = "hello world"; }); app.listen(8080); 属性说明: ctx:context 上下文对象
  • 基础知识 同源策略 是指 三同:同协议/同域名/同端口 对比双方: 当前页面的地址和Ajax获取数据的地址(这里可能有点抽象,下面我会举例子说明) 常见报错 场景描述 场景一: 我在浏览器地址栏中输入 ... .
  • NodeJs框架 Koa

    2021-03-04 06:57:33
    目录一、简介二、 Koa之hello world三、服务器自动重新部署四、Koa中间件五、Koa路由配置六、设置静态目录Koa-static七、使用模板引擎Koa-views八、Vue SSR(vue服务器渲染)九、跨域设置一、 简介 是一个新的 web ...
  • nodejs+koa2 项目脚手架

    2017-10-10 16:48:45
    基于 nodejs + koa2 搭建的项目脚手架,已经引入了mongodb、redis。npm install后,通过node bin/run 启动项目,启动器请确保已经有对应的mongodb服务、redis服务,如有密码,确保mongodb、redis的密码配置正确
  • Koa与Node.js开发实战 基于Node.js平台的新的一代Web开发框架Koa2,构建真实线上实战项目(iKcamp /着) “ ”
  • 一本介绍web开发框架koa的书,Node.js诞生于2009年,到本书出版时已经有近10个年头。它扩充了JavaScript的应用范围,使JavaScript也能像其他语言一样操作各种系统资源,因此,前端工程化开发的大量工具都开始运行在...
  • const Koa = require('koa'); const prometheus = require('@echo-health/koa-prometheus-exporter'); const app = new Koa(); // middleware is a function that returns the middleware async // function, this...
  • NodeJS-Koa-MongoDB-样板API Node.js,Koa,MongoDB的入门项目 NodeJS Koa MongoDB Boilerplate API结构 ├── apidocs ├── app │ ├── controllers │ ├── helpers │ ├── middlewares │ ├─── ...
  • 本篇文章主要介绍了nodejskoa2请求示例(GET,POST),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • koa-nodejs-应用程序 该存储库由教育项目Hexlet的团队和社区创建和维护。 。 要求 码头工人 Docker撰写 建立 $ make compose-setup 跑 $ make compose # Open localhost:4000
  • 对于后端程序应用来说,日志是必不可少的,在nodeJs当中并没有自带的日志模块。最近正好使用koa框架来做后端服务,需要对日志进行分割处理,特记录下分享给大家。 一.后端代码目录结构 ├── back-end │ ├── ...
  • nodejs KOA 框架 下载xlsx

    2017-04-08 15:39:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • 一、基本概述 Koa框架和Express框架是Node生态圈比较流行的Node Web框架,能够利用这两个框架快速...//Koa版本:利用Koa框架实现路由、静态文件、web服务需要额外引入第三方依赖,Koa框架本身只提供了web服务 const K
  • 此项目是用来自己练习nodejs服务端渲染的
  • 小前端从无到有实战系列 包含前一级数据库及...koa2 nodejs Web开发框架 mongodb数据库 猫鼬数据库模型 木偶爬虫实现 七牛上传 开发模式包含前预期分离和服务端渲染 Linux的ubuntu16 Nginx的 自动化发布部署 笔记:
  • NodeJS web service 中,有时候我们需要将一些 静态文件 放到一个文件夹,允许任意访问,比如 css,js html 等文件,或者是允许用户下载的文件。我们使用 koa-static-server 来实现它。 为什么选用 koa-static-...
  • kylinjs kylinjs 基于 koanodejs服务端框架 整合了如下常用模块: http请求、 后端模板nunjucks、模板helpers 路由routes、 路由过滤器filter、 日志模块logger、 插件系统addons 使用方法
  • nodejs大全,包括了node各种书,koa,express,nodejs大全,包括了node各种书,koa,express,nodejs大全,包括了node各种书,koa,express
  • Koa2套件+猫鼬 前端项目为: 结构说明 app.js负责加载路由器,中间件,连接数据库 路由器层只负责映射路由,调用控制器层,然后响应success数据,不应该引发异常,不应该响应错误的数据 controllerlayer为路由器层...
  • Koa-grace v1中(以下简称:koa-grace):在Gracejs完美支持koa v2的同时,同时优化了虚拟主机匹配和路由匹配的性能,还完善了部分测试用例等多种升级。当然,如果你正在使用koa-grace也不用担心,我们会把Gracejs...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,774
精华内容 3,509
关键字:

koanodejs