当前位置: 首页 > 后端技术 > Node.js

koa原理分析

时间:2023-04-03 14:12:01 Node.js

koa原理分析选用版本为koa2原文链接koa源码由四个文件组成'洋槐');constapp=newKoa();app.use(asyncctx=>{ctx.body='HelloWorld';});app.listen(3000);initialserver使用http模块创建服务器constapp=http.createServer((req,res)=>{...})app.listen(3000)实际上,koa将这些包装在它的listen方法listen(...args){debug('listen');constserver=http.createServer(this.callback());返回server.listen(...args);}显然this.callback()返回如下函数(req,res)=>{}contextctxcallback方法如下callback(){constfn=compose(this.middleware);if(!this.listeners('error').length)this.on('error',this.onerror);consthandleRequest=(req,res)=>{constctx=this.createContext(req,res);返回this.handleRequest(ctx,fn);};返回句柄请求;}ctx其实是一个包装了request和response的Objects,从createContext可以看出,继承自contextcreateContext(req,res){constcontext=Object.create(this.context);constrequest=context.request=Object.create(this.request);constresponse=context.response=Object.create(this.response);context.app=request.app=response.app=this;context.req=request.req=response.req=req;context.res=request.res=response.res=res;request.ctx=response.ctx=上下文;请求.响应=响应;response.request=请求;context.originalUrl=request.originalUrl=req.url;context.cookies=newCookies(req,res,{keys:this.keys,secure:request.secure});request.ip=request.ips[0]||req.socket.remoteAddress||'';context.accept=request.accept=接受(请求);context.state={};返回上下文;}可以看到ctx.request继续接受request,ctx.response继续接受response,查看response和request可以看到里面大部分是set和get方法(获取query,设置header)等并和ctx代理了ctx.request和ctx.response的方法,在源代码中可以看到delegate(proto,'response').method('attachment').method('redirect').method('remove')。方法('vary').method('set').method('append').method('flushHeaders').access('status').access('message').access('body').access('length').access('type').access('lastModified').access('etag').getter('headerSent').getter('writable');/***请求委托。*/delegate(proto,'request').method('acceptsLanguages').method('acceptsEncodings').method('acceptsCharsets').method('accepts').method('get').method('is').access('querystring').access('idempotent').access('socket').access('search').access('method').access('query').access('path').access('url').getter('origin').getter('href').getter('subdomains').getter('protocol').getter('host').getter('hostname').getter('URL').getter('header').getter('headers').getter('secure').getter('stale').getter('fresh').getter('ips').getter('ip');所以我们可以直接写ctx.url相当于ctx.request.url中间件我们再看回调函数,发现compose模块很神奇,我暂且称之为iterator,它实现了顺序执行中间件constfn=compose(this.middleware);打印fn如下function(context,next){//最后调用的中间件#letindex=-1returndispatch(0)functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()被多次调用'))index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.解决()尝试{返回承诺。resolve(fn(context,functionnext(){returndispatch(i+1)}))}catch(err){returnPromise.reject(err)}}}刚开始接触koa的时候,我很纳闷为什么我写了ctx.body='helloworld'并且没有像ctx.response.end()这样的方法。其实,koa已经帮我们做了处理。在handleRequest方法中,consthandleResponse=()=>respond(ctx);//fnMiddleware就是上面compose之后返回的fnfnMiddleware(ctx).then(handleResponse).catch(onerror)fnMiddleware是promisse,在中间件编译完成后在respond中最后去处理ctx.bodyfunctionrespond(ctx){//允许绕过koaif(false===ctx.respond)return;constres=ctx.res;如果(!ctx.writable)返回;让body=ctx.body;constcode=ctx.status;//忽略正文if(statuses.empty[code]){//剥离标题ctx.body=null;返回res.end();}if('HEAD'==ctx.method){if(!res.headersSent&&isJSON(body)){ctx.length=Buffer.byteLength(JSON.stringify(body));}返回res.end();}//状态正文if(null==body){body=ctx.message||字符串(代码);如果(!res.headersSent){ctx.type='text';ctx.length=Buffer.byteLength(body);}返回res.end(body);}//响应if(Buffer.isBuffer(body))returnres.end(body);if('string'==typeofbody)returnres.end(body);if(bodyinstanceofStream)returnbody.pipe(res);//主体:jsonbody=JSON.stringify(body);if(!res.headersSent){ctx.length=Buffer.byteLength(正文);}res.end(body);}错误处理(非head)中间件层处理(我自己瞎创建的)对于每个中间件可能出现的错误,app.use可以直接在中间件中捕获((ctx,next)=>{try{...}catch(err){...}})(header)中间件层处理其实我们只需要在第一个中间件中加入try...catch...,错误的可以捕获整个中间件组(应用层)顶层处理app.on('error',(err)={})当上面的中间件执行时,koa会自动帮我们捕获错误,处理如下try{returnPromise.resolve(fn(context,functionnext(){returndispatch(i+1)}))}catch(err){//捕获错误returnPromise.reject(err)}//在ctx中处理constonerror.onerror=err=>ctx.onerror(err);fnMiddleware(ctx).then(handleResponse).catch(onerror)我们查看ctx.onerror,发现其实是触发了app监听的error事件onerror(err){//delegatethis.app.emit('error',err,这);如果我们不定义错误回调怎么办,koa也定义了默认的错误处理函数回调方法让我们做Callback(){...if(!this.listeners('error').length)this.on('错误',this.onerror);...}文末