node中%${}%signed在c语言中是什么意思?

大前端零基础入门到就业:进入学习【相关教程推荐:nodejs视频教程】什么是RPC?RPC:Remote Procedure Call(远程过程调用)是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。服务器和服务器之间的通信RPC vs HTTP相同点都是两台计算机之间的网络通信。ajax是浏览器和服务器之间的通行,RPC是服务器与服务器之间的通行需要双方约定一个数据格式不同点寻址服务器不同ajax 是使用 DNS作为寻址服务获取域名所对应的ip地址,浏览器拿到ip地址之后发送请求获取数据。RPC一般是在内网里面相互请求,所以它一般不用DNS做寻址服务。因为在内网,所以可以使用规定的id或者一个虚拟vip,比如v5:8001,然后到寻址服务器获取v5所对应的ip地址。应用层协议不同ajax使用http协议,它是一个文本协议,我们交互数据的时候文件格式要么是html,要么是json对象,使用json的时候就是key-value的形式。RPC采用二进制协议。采用二进制传输,它传输的包是这样子的[0001 0001 0111 0110 0010],里面都是二进制,一般采用那几位表示一个字段,比如前6位是一个字段,依次类推。这样就不需要http传输json对象里面的key,所以有更小的数据体积。因为传输的是二进制,更适合于计算机来理解,文本协议更适合人类理解,所以计算机去解读各个字段的耗时是比文本协议少很多的。RPC采用二进制有更小的数据体积,及更快的解读速度。TCP通讯方式选择这三种通信方式的哪一种主要考虑的因素是:实现难度和成本。全双工通信是要比半双工通信的成本要高的,在某些场景下还是可以考虑使用半双工通信。ajax是一种半双工通信。http是文本协议,但是它底层是tcp协议,http文本在tcp这一层会经历从二进制数据流到文本的转换过程。理解RPC只是在更深入地理解前端技术。buffer编解码二进制数据包创建bufferbuffer.from: 从已有的数据创建二进制const buffer1 = Buffer.from('geekbang')
const buffer2 = Buffer.from([0, 1, 2, 3, 4])
<Buffer 67 65 65 6b 62 61 6e 67>
<Buffer 00 01 02 03 04>buffer.alloc: 创建一个空的二进制const buffer3 = Buffer.alloc(20)
<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>往buffer里面写东西buffer.write(string, offset): 写入字符串buffer.writeInt8(value, offset): int8表示二进制8位(8位表示一个字节)所能表示的整数,offset开始写入之前要跳过的字节数。buffer.writeInt16BE(value, offset): int16(两个字节数),表示16个二进制位所能表示的整数,即32767。超过这个数程序会报错。const buffer = Buffer.from([1, 2, 3, 4]) // <Buffer 01 02 03 04>
// 往第二个字节里面写入12
buffer.writeInt8(12, 1) // <Buffer 01 0c 03 04>大端BE与小端LE:主要是对于2个以上字节的数据排列方式不同(writeInt8因为只有一个字节,所以没有大端和小端),大端的话就是低位地址放高位,小端就是低位地址放低位。如下:const buffer = Buffer.from([1, 2, 3, 4])
buffer.writeInt16BE(512, 2) // <Buffer 01 02 02 00>
buffer.writeInt16LE(512, 2) // <Buffer 01 02 00 02>RPC传输的二进制如何表示传递的字段PC传输的二进制是如何表示字段的呢?现在有个二进制包[00, 00, 00, 00, 00, 00, 00],我们假定前三个字节表示一个字段值,后面两个表示一个字段的值,最后两个也表示一个字段的值。那写法如下:writeInt16BE(value, 0)
writeInt16BE(value, 2)
writeInt16BE(value, 4)发现像这样写,不仅要知道写入的值,还要知道值的数据类型,这样就很麻烦。不如json格式那么方便。针对这种情况业界也有解决方案。npm有个库protocol-buffers,把我们写的参数转化为buffer:// test.proto 定义的协议文件
message Column {
required float num
= 1;
required string payload = 2;
}
// index.js
const fs = require('fs')
var protobuf = require('protocol-buffers')
var messages = protobuf(fs.readFileSync('test.proto'))
var buf = messages.Column.encode({
num: 42,
payload: 'hello world'
})
console.log(buf)
// <Buffer 0d 00 00 28 42 12 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64>
var obj = messages.Column.decode(buf)
console.log(obj)
// { num: 42, payload: 'hello world' }net建立RPC通道半双工通信服务端代码:const net = require('net')
const LESSON_DATA = {
136797: '01
课程介绍',
136798: '02
内容综述',
136799: '03
Node.js是什么?',
136800: '04
Node.js可以用来做什么?',
136801: '05
课程实战项目介绍',
136803: '06
什么是技术预研?',
136804: '07
Node.js开发环境安装',
136806: '08
第一个Node.js程序:石头剪刀布游戏',
136807: '09
模块:CommonJS规范',
136808: '10
模块:使用模块规范改造石头剪刀布游戏',
136809: '11
模块:npm',
141994: '12
模块:Node.js内置模块',
143517: '13
异步:非阻塞I/O',
143557: '14
异步:异步编程之callback',
143564: '15
异步:事件循环',
143644: '16
异步:异步编程之Promise',
146470: '17
异步:异步编程之async/await',
146569: '18
HTTP:什么是HTTP服务器?',
146582: '19
HTTP:简单实现一个HTTP服务器'
}
const server = net.createServer(socket => {
// 监听客户端发送的消息
socket.on('data', buffer => {
const lessonId = buffer.readInt32BE()
setTimeout(() => {
// 往客户端发送消息
socket.write(LESSON_DATA[lessonId])
}, 1000)
})
})
server.listen(4000)客户端代码:const net = require('net')
const socket = new net.Socket({})
const LESSON_IDS = [
'136797',
'136798',
'136799',
'136800',
'136801',
'136803',
'136804',
'136806',
'136807',
'136808',
'136809',
'141994',
'143517',
'143557',
'143564',
'143644',
'146470',
'146569',
'146582'
]
socket.connect({
host: '127.0.0.1',
port: 4000
})
let buffer = Buffer.alloc(4)
buffer.writeInt32BE(LESSON_IDS[Math.floor(Math.random() * LESSON_IDS.length)])
// 往服务端发送消息
socket.write(buffer)
// 监听从服务端传回的消息
socket.on('data', buffer => {
console.log(buffer.toString())
// 获取到数据之后再次发送消息
buffer = Buffer.alloc(4)
buffer.writeInt32BE(LESSON_IDS[Math.floor(Math.random() * LESSON_IDS.length)])
socket.write(buffer)
})以上半双工通信步骤如下:客户端发送消息 socket.write(buffer)服务端接受消息后往客户端发送消息 socket.write(buffer)客户端接受消息后再次发送消息这样在一个时间端之内,只有一个端往另一个端发送消息,这样就实现了半双工通信。那如何实现全双工通信呢,也就是在客户端往服务端发送消息的同时,服务端还没有消息返回给客户端之前,客户端又发送了一个消息给服务端。全双工通信先来看一个场景:客户端发送了一个id1的请求,但是服务端还来不及返回,接着客户端又发送了一个id2的请求。等了一个之后,服务端先把id2的结果返回了,然后再把id1的结果返回。那如何结果匹配到对应的请求上呢?如果按照时间顺序,那么id1的请求对应了id2的结果,因为id2是先返回的;id2的请求对应了id1的结果,这样就导致请求包和返回包错位的情况。怎么办呢?我们可以给请求包和返回包都带上序号,这样就能对应上。错位处理客户端代码:socket.on('data', buffer => {
// 包序号
const seqBuffer = buffer.slice(0, 2)
// 服务端返回的内容
const titleBuffer = buffer.slice(2)
console.log(seqBuffer.readInt16BE(), titleBuffer.toString())
})
// 包序号
let seq = 0
function encode(index) {
// 请求包的长度现在是6 = 2(包序号) + 4(课程id)
buffer = Buffer.alloc(6)
buffer.writeInt16BE(seq)
buffer.writeInt32BE(LESSON_IDS[index], 2)
seq++
return buffer
}
// 每50ms发送一次请求
setInterval(() => {
id = Math.floor(Math.random() * LESSON_IDS.length)
socket.write(encode(id))
}, 50)服务端代码:const server = net.createServer(socket => {
socket.on('data', buffer => {
// 把包序号取出
const seqBuffer = buffer.slice(0, 2)
// 从第2个字节开始读取
const lessonId = buffer.readInt32BE(2)
setTimeout(() => {
const buffer = Buffer.concat([
seqBuffer,
Buffer.from(LESSON_DATA[lessonId])
])
socket.write(buffer)
// 这里返回时间采用随机的,这样就不会按顺序返回,就可以测试错位的情况
}, 10 + Math.random() * 1000)
})
})客户端把包序号和对应的id给服务端服务端取出包序号和对应的id,然后把包序号和id对应的内容返回给客户端,同时设置返回的时间是随机的,这样就不会按照顺序返回。粘包处理如果我们这样发送请求:for (let i = 0; i < 100; i++) {
id = Math.floor(Math.random() * LESSON_IDS.length)
socket.write(encode(id))
}我们发现服务端接收到的信息如下:<Buffer 00 00 00 02 16 64 00 01 00 02 16 68 00 02 00 02 31 1c 00 03 00 02 3c 96 00 04 00 02 16 68 00 05 00 02 16 5e 00 06 00 02 16 66 00 07 00 02 16 67 00 08 ... 550 more bytes>这是因为TCP自己做的一个优化,它会把所有的请求包拼接在一起,这样就会产生粘包的现象。服务端需要把包进行拆分,拆分成100个小包。那如何拆分呢?首先客户端发送的数据包包括两部分:定长的包头和不定长的包体。包头又分为两部分:包序号及包体的长度。只有知道包体的长度,才能知道从哪里进行分割。let seq = 0
function encode(data) {
// 正常情况下,这里应该是使用 protocol-buffers 来encode一段代表业务数据的数据包
// 为了不要混淆重点,这个例子比较简单,就直接把课程id转buffer发送
const body = Buffer.alloc(4);
body.writeInt32BE(LESSON_IDS[data.id]);
// 一般来说,一个rpc调用的数据包会分为定长的包头和不定长的包体两部分
// 包头的作用就是用来记载包的序号和包的长度,以实现全双工通信
const header = Buffer.alloc(6); // 包序号占2个字节,包体长度占4个字节,共6个字节
header.writeInt16BE(seq)
header.writeInt32BE(body.length, 2);
// 包头和包体拼起来发送
const buffer = Buffer.concat([header, body])
console.log(`包${seq}传输的课程id为${LESSON_IDS[data.id]}`);
seq++;
return buffer;
}
// 并发
for (let i = 0; i < 100; i++) {
id = Math.floor(Math.random() * LESSON_IDS.length)
socket.write(encode({ id }))
}服务端进行拆包const server = net.createServer(socket => {
let oldBuffer = null
socket.on('data', buffer => {
// 把上一次data事件使用残余的buffer接上来
if (oldBuffer) {
buffer = Buffer.concat([oldBuffer, buffer])
}
let packageLength = 0
// 只要还存在可以解成完整包的包长
while ((packageLength = checkComplete(buffer))) {
// 确定包的长度后进行slice分割
const package = buffer.slice(0, packageLength)
// 剩余的包利用循环继续分割
buffer = buffer.slice(packageLength)
// 把这个包解成数据和seq
const result = decode(package)
// 计算得到要返回的结果,并write返回
socket.write(encode(LESSON_DATA[result.data], result.seq))
}
// 把残余的buffer记下来
oldBuffer = buffer
})
})checkComplete 函数的作用来确定一个数据包的长度,然后进行分割:function checkComplete(buffer) {
// 如果包的长度小于6个字节说明只有包头,没有包体,那么直接返回0
if (buffer.length <= 6) {
return 0
}
// 读取包头的第二个字节,取出包体的长度
const bodyLength = buffer.readInt32BE(2)
// 请求包包括包头(6个字节)和包体body
return 6 + bodyLength
}decode对包进行解密:function decode(buffer) {
// 读取包头
const header = buffer.slice(0, 6)
const seq = header.readInt16BE()
// 读取包体
// 正常情况下,这里应该是使用 protobuf 来decode一段代表业务数据的数据包
// 为了不要混淆重点,这个例子比较简单,就直接读一个Int32即可
const body = buffer.slice(6).readInt32BE()
// 这里把seq和数据返回出去
return {
seq,
data: body
}
}encode把客户端想要的数据转化为二进制返回,这个包同样包括包头和包体,包头又包括包需要包序号和包体的长度。function encode(data, seq) {
// 正常情况下,这里应该是使用 protobuf 来encode一段代表业务数据的数据包
// 为了不要混淆重点,这个例子比较简单,就直接把课程标题转buffer返回
const body = Buffer.from(data)
// 一般来说,一个rpc调用的数据包会分为定长的包头和不定长的包体两部分
// 包头的作用就是用来记载包的序号和包的长度,以实现全双工通信
const header = Buffer.alloc(6)
header.writeInt16BE(seq)
header.writeInt32BE(body.length, 2)
const buffer = Buffer.concat([header, body])
return buffer
}当客户端收到服务端发送的包之后,同样也要进行拆包,因为所有的包同样都粘在一起了: <Buffer 00 00 00 00 00 1d 30 36 20 7c 20 e4 bb 80 e4 b9 88 e6 98 af e6 8a 80 e6 9c af e9 a2 84 e7 a0 94 ef bc 9f 00 01 00 00 00 1d 30 36 20 7c 20 e4 bb 80 e4 ... 539 more bytes>因此,客户端也需要拆包,拆包策略与服务端的拆包策略是一致的:let oldBuffer = null
socket.on('data', buffer => {
// 把上一次data事件使用残余的buffer接上来
if (oldBuffer) {
buffer = Buffer.concat([oldBuffer, buffer])
}
let completeLength = 0
// 只要还存在可以解成完整包的包长
while ((completeLength = checkComplete(buffer))) {
const package = buffer.slice(0, completeLength)
buffer = buffer.slice(completeLength)
// 把这个包解成数据和seq
const result = decode(package)
console.log(`包${result.seq},返回值是${result.data}`)
}
// 把残余的buffer记下来
oldBuffer = buffer
})到这里就实现了双全工通行,这样客户端和服务端随时都可以往对方发小消息了。更多node相关知识,请访问:nodejs 教程!以上就是什么是RPC?聊聊node中怎么实现 RPC 通信的详细内容,更多请关注其它相关文章!}
koa指的是一个类似于Express的基于Node实现的web框架,致力于成为web应用和API开发领域中的一个更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);上面代码中,变量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);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函数是可以接受参数的。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()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方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。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.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对象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;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 cookiecookie的读取和设置。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。8 sessionvar 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是什么的详细内容,更多请关注鸿网互联其它相关文章!}

我要回帖

更多关于 signed在c语言中是什么意思 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信