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

说说koa

时间:2023-04-03 13:47:48 Node.js

的目标本文主要通过一个简单的例子来讲解koa的内部原理。koa的一个简单例子constKoa=require('koa');constapp=newKoa();app.use(asyncctx=>{ctx.body='HelloWorld';});app.listen(3000);koa内部文件组成application.js包含Application类和一些辅助方法context.js主要作用是携带上下层信息,并封装了处理上下文信息的操作request.js封装了处理请求信息响应的基本操作.js将处理响应信息的基本操作封装在koa中。在文章开头的例子中,当执行constapp=newkoa()时,实际上是构造了一个Application实例。在application的构造方法中,创建了context.js和request。.js、response.js代码运行实例。下面规定请求值request.js代码运行实例,response指response.js运行实例,context指context.js运行实例。构造函数(){超级();this.proxy=false;这个.middleware=[];this.subdomainOffset=2;this.env=process.env.NODE_ENV||'发展';this.context=Object.create(context);this.request=Object.create(请求);this.response=Object.create(response);如果(util.inspect.custom){这个[util.inspect.custom]=this.inspect;}}callapp.listen(3000)方法监听3000端口的http请求,listen方法内部创建一个http.Server对象,调用http.Server的listen方法。具体代码如下:listen(...args){debug('listen');constserver=http.createServer(this.callback());返回server.listen(...args);}其中this.callback方法源码如下:callback(){constfn=compose(this.middleware);如果(!this.listenerCount('error'))this.on('error',this.onerror);consthandleRequest=(req,res)=>{constctx=this.createContext(req,res);返回this.handleRequest(ctx,fn);};返回句柄请求;}该方法返回一个handleRequest函数作为createServer的参数,当http.Server实例接收到http请求时,会将请求信息和请求响应对象传递给handleRequest函数,具体为http的实例req.IncomingMessage和http.ServerResponse的实例res将传递给handleRequest方法。this.createContext函数源码如下:createContext(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.state={};主要作用是将请求信息和响应信息封装在request.js和response.js的运行实例中,并创建一个context对象context,其中包含对应用实例、请求实例和响应实例的引用。this.callback方法中还有一行非常重要的代码:constfn=compose(this.middleware);从Application的构造方法可以知道this.middleware是一个数组,用来存放传入的app.use方法的中间件方法。Application的use方法的具体代码实现细节如下,其中最关键的一行代码是this.middleware.push(fn)。use(fn){if(typeoffn!=='function')thrownewTypeError('中间件必须是一个函数!');if(isGeneratorFunction(fn)){deprecate('对生成器的支持将在v3中删除。'+'请参阅文档以获取有关如何转换旧中间件的示例'+'https://github.com/koajs/koa/blob/master/docs/migration.md');fn=转换(fn);}debug('使用%s',fn._name||fn.name||'-');这个.middleware.push(fn);归还这个;}compose方法的实现包包含在koa-compose包中,具体代码实现细节如下:functioncompose(middleware){if(!Array.isArray(middleware))thrownewTypeError('Middlewarestackmustbeanarray!')for(constfnofmiddleware){if(typeoffn!=='function')thrownewTypeError('Middlewaremustbecomposedoffunctions!')}returnfunction(context,next){//最后调用的中间件#letindex=-1returndispatch(0)functiondispatch(i){if(i<=index)返回Promise.reject(newError('next()被多次调用'))index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()try{returnPromise.resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}}}compose方法接收this.middleware数组并返回一个匿名函数,这个函数接收两个参数,上下文实例context和一个next函数,执行这个匿名函数就会执行this.middleware数组中的所有中间件函数,然后在Application的回调方法末尾执行传入的next函数,以执行上下文对象和compose方法返回的匿名函数为参数调用this.handleRequest方法。接下来看this.handleRequest方法的细节:handleRequest(ctx,fnMiddleware){constres=ctx.res;res.statusCode=404;constonerror=err=>ctx.onerror(err);consthandleResponse=()=>respond(ctx);onFinished(res,onerror);返回fnMiddleware(ctx).then(handleResponse).catch(onerror);在this.handleRequest方法中,创建了错误处理方法onError和返回响应的方法handleResponse。fnMiddleware是compose方法返回的匿名函数。在this.handleRequest方法的最后执行匿名函数,传入hanldeResponse和onError函数,分别处理正常的请求响应过程和异常情况。返回fnMiddleware(ctx).then(handleResponse).catch(onerror);执行fnMiddleware函数,它在执行前实际上传入了所有的中间件函数。在中间函数中,我们可以得到context对象的引用,通过它我们可以得到封装后的request和response实例,具体形式如下:app.use(asyncctx=>{ctx;//ThisisContextctx.request;//这是koa请求ctx.response;//这是koa响应});在中间件方法中,可以设置响应头信息和响应内容。以及读取数据库,获取html模板等。将需要返回给客户端的数据赋值给context对象context的body属性。响应方法的具体实现如下:functionrespond(ctx){//允许绕过koaif(false===ctx.respond)return;如果(!ctx.writable)返回;constres=ctx.res;让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){if(ctx.req.httpVersionMajor>=2){body=String(code);}else{body=ctx.message||字符串(代码);}if(!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);//主体:json主体=JSON.stringify(正文);如果(!res.headersSent){ctx.length=Buffer.byteLength(body);}res.end(body);}respond方法的主要作用是对返回的内容进行一些处理,然后调用node.js的http.ServerResponse实例的end方法,将具体的内容返回给客户端request.js和response.js在Application的createContext方法中,node.js请求(http.IncomingMessage)和响应对象(http.ServerResponse)分别赋值给request.js和response.js文件代码实例对象。request.js主要包括处理请求的方法(其实都是get方法,或者说getter),获取请求数据,例如:gethost(){constproxy=this.app.proxy;lethost=proxy&&this.get('X-Forwarded-Host');if(!host){if(this.req.httpVersionMajor>=2)host=this.get(':authority');如果(!主机)主机=这个。得到('主机');}如果(!host)返回'';返回host.split(/\s*,\s*/,1)[0];},上面代码中的this.req为http。IncomingMessage实例,包括http请求信息。response.js包含处理请求响应的操作。例如设置响应状态:setstatus(code){if(this.headerSent)return;assert(Number.isInteger(code),'状态码必须是数字');assert(code>=100&&code<=999,`无效状态代码:${code}`);this._explicitStatus=true;this.res.statusCode=代码;如果(this.req.httpVersionMajor<2)this.res.statusMessage=statuses[code];如果(this.body&&statuses.empty[code])this.body=null;}this.res指针http.ServerResponse对图像实例结束~~~~~