• 技术文章 >web前端 >前端问答

    nodejs中的koa是什么

    青灯夜游青灯夜游2021-10-29 15:00:12原创515

    koa指的是一个类似于Express的基于Node实现的web框架,致力于成为web应用和API开发领域中的一个更小、更富有表现力、更健壮的基石。Koa并没有捆绑任何中间件,而是提供了一套优雅的方法,帮助用户快速而愉快地编写服务端应用程序。

    本教程操作环境:windows7系统、nodejs 12.19.0&&koa2.0版、Dell G3电脑。

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

    Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

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

    1 Koa应用

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

    var koa = require('koa');
    var app = koa();
    
    app.use(function *(){
      this.body = 'Hello World';
    });
    
    app.listen(3000);
    var http = require('http');
    var koa = require('koa');
    var app = koa();
    http.createServer(app.callback()).listen(3000);

    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,要等到下一个中间件返回结果,才会继续往下执行。

    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函数是可以接受参数的。

    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参数,则传入一个空函数。

    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('/', 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方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。

    5 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对象的全局属性。

    this.state.user = yield User.find(id);

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

    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;

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

    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的配置对象的属性如下。

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

    【推荐学习:《nodejs 教程》】

    以上就是nodejs中的koa是什么的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:nodejs koa
    上一篇:javascript中filter的用法是什么 下一篇:nodejs模块是什么
    千万级数据并发解决方案

    相关文章推荐

    • 怎样删除nodejs• nodejs怎么设置成员• 如何安装配置nodejs• nodejs安装后npm报错怎么办• nodejs怎么删文件夹
    1/1

    PHP中文网