用法">一、基本用法
-
koa架设一个HTTP服务
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
koa架设一个HTTP服务
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
``
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
<h3 id="content对象">2、Content对象
*Koa提供一个Context对象,表示一次对话的上下文(包括HTTP 请求和HTTP回复)。通过加工这个对象,就可以控制返回给用户的内容。 Context.response.body属性就是发送给用户的内容
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(main);
app.listen(3000);
- main函数用来设置ctx.response.body。然后app.use方法加载main函数
- ctx.response代表HTTP Response,同样地ctx.request代表HTTP Request.
-
Koa默认的返回类型是text/plain,如果想返回其他类型的内容,可以利用ctx.request.accepts判断一下,客户端希望接受什么数据(根据HTTP Request的Accept字段),然后使用ctx.response.type指定返回类型。
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
if (ctx.request.accepts('xml')) {
ctx.response.type = 'xml';
ctx.response.body = 'Hello World';
} else if (ctx.request.accepts('json')) {
ctx.response.type = 'json';
ctx.response.body = { data: 'Hello World' };
} else if (ctx.request.accepts('html')) {
ctx.response.type = 'html';
ctx.response.body = '
Hello World
';
} else {
ctx.response.type = 'text';
ctx.response.body = 'Hello World';
}
};
app.use(main);
app.listen(3000);
Koa默认的返回类型是text/plain,如果想返回其他类型的内容,可以利用ctx.request.accepts判断一下,客户端希望接受什么数据(根据HTTP Request的Accept字段),然后使用ctx.response.type指定返回类型。
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
if (ctx.request.accepts('xml')) {
ctx.response.type = 'xml';
ctx.response.body = 'Hello World';
} else if (ctx.request.accepts('json')) {
ctx.response.type = 'json';
ctx.response.body = { data: 'Hello World' };
} else if (ctx.request.accepts('html')) {
ctx.response.type = 'html';
ctx.response.body = '
Hello World
';
} else {
ctx.response.type = 'text';
ctx.response.body = 'Hello World';
}
};
app.use(main);
app.listen(3000);
const fs = require('fs');
const Koa = require('koa');
const app = new Koa();
app.use(ctx=>{
ctx.response.type = 'html';
ctx.response.body = fs.createReadStream('./demos/template.html');
})
app.listen(3000)
<h3 id="post请求如何接收">5、POST请求如何接收
<h4 id="获取post请求的步骤">1、获取POST请求的步骤
- 1、解析上下文ctx中的原生node.js对象req。
- 2、将POST表单数据解析成query string-字符串。
- 将字符串转换成JSON格式
- ctx.request:是Koa2中的Context经过封装的请求对象,它用起来更直观和简单。
- ctx.req:是content提供的node.js原生HTTP请求对象。这个虽然不那么直观,但是可以得到更多的内容,适合我们深度编程。
-
Koa2中提供了ctx.method属性,可以轻松的得到请求的类型,然后根据请求类型编写不同的相应方法,这在工作中非常常用。
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
//当请求是get请求时,显示表单让用户填写
if(ctx.url === '/' && ctx.method === 'GET'){
let html = `
Koa2 request post demo
`;
ctx.body = html;
//当请求是post请求时
}else if(ctx.url === '/' && ctx.method === 'POST'){
ctx.body = '接收到post请求'
}else{
//其他页面显示404页面
ctx.body = '404!
'
}
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
注意,POST必须写成POST,post不可以(必须大写,小写不行)
-
声明一个方法,然后用Promise对象进行解析。这里我们使用了ctx.req.on来接收事件。
function parsePostData(ctx){
return new Promise((resolve,reject)=>{
try{
let postdata = '';
ctx.req.addListener('end',function(){
resolve(postdata)
})
ctx.req.on('data',(data)=>{
postdata += data;
})
}catch(error){
reject(error)
}
})
}
-
写一个字符串封装JSON兑现对象的方法。
function parseQueryStr(queryStr) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for (let [index,queryStr] of queryStrList.entries()){
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
let itemList = queryStr.split('=');
console.log(itemList);
//key:value
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
// decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
}
return queryData;
}
-
Koa2中提供了ctx.method属性,可以轻松的得到请求的类型,然后根据请求类型编写不同的相应方法,这在工作中非常常用。
const Koa = require('koa'); const app = new Koa(); app.use(async(ctx)=>{ //当请求是get请求时,显示表单让用户填写 if(ctx.url === '/' && ctx.method === 'GET'){ let html = `
Koa2 request post demo
`; ctx.body = html; //当请求是post请求时 }else if(ctx.url === '/' && ctx.method === 'POST'){ ctx.body = '接收到post请求' }else{ //其他页面显示404页面 ctx.body = '404!
' } })app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
}) 注意,POST必须写成POST,post不可以(必须大写,小写不行)
-
声明一个方法,然后用Promise对象进行解析。这里我们使用了ctx.req.on来接收事件。
function parsePostData(ctx){
return new Promise((resolve,reject)=>{
try{
let postdata = '';
ctx.req.addListener('end',function(){
resolve(postdata)
})
ctx.req.on('data',(data)=>{
postdata += data;
})
}catch(error){
reject(error)
}
})
}
-
写一个字符串封装JSON兑现对象的方法。
function parseQueryStr(queryStr) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for (let [index,queryStr] of queryStrList.entries()){
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
let itemList = queryStr.split('=');
console.log(itemList);
//key:value
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
// decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
}
return queryData;
}
-
声明一个方法,然后用Promise对象进行解析。这里我们使用了ctx.req.on来接收事件。
function parsePostData(ctx){ return new Promise((resolve,reject)=>{ try{ let postdata = ''; ctx.req.addListener('end',function(){ resolve(postdata) }) ctx.req.on('data',(data)=>{ postdata += data; })
}catch(error){ reject(error) } })
}
-
写一个字符串封装JSON兑现对象的方法。
function parseQueryStr(queryStr) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for (let [index,queryStr] of queryStrList.entries()){
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
let itemList = queryStr.split('=');
console.log(itemList);
//key:value
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
// decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
}
return queryData;
}
写一个字符串封装JSON兑现对象的方法。
function parseQueryStr(queryStr) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for (let [index,queryStr] of queryStrList.entries()){
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
let itemList = queryStr.split('=');
console.log(itemList);
//key:value
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
// decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
}
return queryData;
}
全部代码
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
//当请求是get请求时,显示表单让用户填写
if(ctx.url === '/' && ctx.method === 'GET'){
let html = `
Koa2 request post demo
`;
ctx.body = html;
//当请求是post请求时
}else if(ctx.url === '/' && ctx.method === 'POST'){
let postData = await parsePostData(ctx);
ctx.body = postData;
}else{
//其他页面显示404页面
ctx.body = '404!
'
}
})
function parsePostData(ctx){
return new Promise((resolve,reject)=>{
try{
let postdata = '';
ctx.req.addListener('end',function(){
let parseData = parseQueryStr(postdata)
resolve(parseData)
})
ctx.req.on('data',(data)=>{
postdata += data;
})
}catch(error){
reject(error)
}
})
}
function parseQueryStr(queryStr) {
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for (let [index,queryStr] of queryStrList.entries()){
//entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
//迭代对象中数组的索引值作为 key, 数组元素作为 value。
let itemList = queryStr.split('=');
console.log(itemList);
//key:value
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
// decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
}
return queryData;
}
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="get请求的接收">6、get请求的接收
<h4 id="query和querystring区别">query和querystring区别
在koa2中GET请求通过request接收,但是接受的方法有两种:query和querystring。
- query:返回的是格式化好的参数对象。
-
querystring:返回的是请求字符串
const Koa = require('koa'); const app = new Koa();
app.use(async(ctx)=>{
let url = ctx.url;
//从request中接收get请求
let request = ctx.request;
let req_query = request.query;
let req_querystring = request.querystring;ctx.body = { url,req_query,req_querystring }
})
app.listen(3000,()=>{
console.log('[demo] server is starting at port 3000')
})
获取get请求">2.直接从ctx中获取get请求
-
直接在ctx中得到get请求,ctx中也分为query和querystring
const Koa = require('koa'); const app = new Koa();
app.use(async(ctx)=>{
let url = ctx.url;
//从上下文中直接获取
let ctx_query = ctx.query;
let ctx_querystring = ctx.querystring;
ctx.body = {
url,ctx_query,ctx_querystring}
})
app.listen(3000,()=>{
console.log('[demo] server is starting at port 3000')
})
-
网站一般都有多个页面。通过ctx.request.path可以获取用户请求的路径,由此实现简单的路由
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page';
} else {
ctx.response.body = 'Hello World';
}
};
app.use(main);
app.listen(3000);
-
原生路由用起来不太方便,我们可以使用封装好的koa-route模块
const Koa = require('koa');
const route = require('koa-route');
const app = new Koa();
const about = ctx =>{
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page'
};
const main = ctx =>{
ctx.response.body = 'Hello World'
}
app.use(route.get('/',main));
app.use(route.get('/about',about))
app.listen(3000);
-
如果网站提供静态资源(图片、字体、样式、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static模块封装了这部分的请求。
const Koa = require('koa');
const app = new Koa();
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
app.listen(3000);
- npm install --save koa-static
文件夹">2、新建static文件夹
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');
const app = new Koa();
const staticPath = './static';
app.use(static(path.join(__dirname,staticPath)));
app.use(async(ctx)=>{
ctx.body = 'hello static'
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="重定向">4、重定向
-
有些场合,服务器需要重定向(redirect)访问请求。比如,用户登录以后,将他重定向到登录前的页面。ctx.response.redirect()方法可以发出一个302跳转,将用户导向另一个路由
const Koa = require('koa');
const route = require('koa-route');
const app = new Koa();
const redirect = ctx => {
ctx.response.redirect('/');
};
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(route.get('/',main));
app.use(route.get('/redirect',redirect));
app.listen(3000);
-
网站一般都有多个页面。通过ctx.request.path可以获取用户请求的路径,由此实现简单的路由
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page';
} else {
ctx.response.body = 'Hello World';
}
};
app.use(main);
app.listen(3000);
-
原生路由用起来不太方便,我们可以使用封装好的koa-route模块
const Koa = require('koa');
const route = require('koa-route');
const app = new Koa();
const about = ctx =>{
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page'
};
const main = ctx =>{
ctx.response.body = 'Hello World'
}
app.use(route.get('/',main));
app.use(route.get('/about',about))
app.listen(3000);
-
如果网站提供静态资源(图片、字体、样式、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static模块封装了这部分的请求。
const Koa = require('koa');
const app = new Koa();
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
app.listen(3000);
- npm install --save koa-static
文件夹">2、新建static文件夹
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');
const app = new Koa();
const staticPath = './static';
app.use(static(path.join(__dirname,staticPath)));
app.use(async(ctx)=>{
ctx.body = 'hello static'
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="重定向">4、重定向
-
有些场合,服务器需要重定向(redirect)访问请求。比如,用户登录以后,将他重定向到登录前的页面。ctx.response.redirect()方法可以发出一个302跳转,将用户导向另一个路由
const Koa = require('koa');
const route = require('koa-route');
const app = new Koa();
const redirect = ctx => {
ctx.response.redirect('/');
};
const main = ctx => {
ctx.response.body = 'Hello World';
};
app.use(route.get('/',main));
app.use(route.get('/redirect',redirect));
app.listen(3000);
网站一般都有多个页面。通过ctx.request.path可以获取用户请求的路径,由此实现简单的路由
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page';
} else {
ctx.response.body = 'Hello World';
}
};
app.use(main);
app.listen(3000);
-
原生路由用起来不太方便,我们可以使用封装好的koa-route模块
const Koa = require('koa'); const route = require('koa-route'); const app = new Koa();
const about = ctx =>{
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page'
};
const main = ctx =>{
ctx.response.body = 'Hello World'
}app.use(route.get('/',main));
app.use(route.get('/about',about))
app.listen(3000);
-
如果网站提供静态资源(图片、字体、样式、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static模块封装了这部分的请求。
const Koa = require('koa');
const app = new Koa();
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
app.listen(3000);
- npm install --save koa-static
文件夹">2、新建static文件夹
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');
const app = new Koa();
const staticPath = './static';
app.use(static(path.join(__dirname,staticPath)));
app.use(async(ctx)=>{
ctx.body = 'hello static'
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="重定向">4、重定向
如果网站提供静态资源(图片、字体、样式、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static模块封装了这部分的请求。
const Koa = require('koa');
const app = new Koa();
const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);
app.listen(3000);
- npm install --save koa-static
文件夹">2、新建static文件夹
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');
const app = new Koa();
const staticPath = './static';
app.use(static(path.join(__dirname,staticPath)));
app.use(async(ctx)=>{
ctx.body = 'hello static'
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="重定向">4、重定向
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');
const app = new Koa();
const staticPath = './static';
app.use(static(path.join(__dirname,staticPath)));
app.use(async(ctx)=>{
ctx.body = 'hello static'
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
-
有些场合,服务器需要重定向(redirect)访问请求。比如,用户登录以后,将他重定向到登录前的页面。ctx.response.redirect()方法可以发出一个302跳转,将用户导向另一个路由
const Koa = require('koa'); const route = require('koa-route'); const app = new Koa();
const redirect = ctx => {
ctx.response.redirect('/');
};const main = ctx => {
ctx.response.body = 'Hello World';
};app.use(route.get('/',main));
app.use(route.get('/redirect',redirect));
app.listen(3000);
访问 http://127.0.0.1:3000/redirect ,浏览器会将用户导向根路由。
-
要想实现原生路由,需要得到地址栏输入的路径,然后根据路径的不同进行跳转。用ctx.request.url就可以实现
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
let url = ctx.request.url
ctx.body = url
})
app.listen(3000)
功能">1、Logger功能
-
Koa的最大特色,也是最重要的一个设计,就是中间件(middleware)
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
console.log(${Date.now()} ${ctx.request.method} ${ctx.request.url}
);
ctx.response.body = 'Hello World';
};
app.use(main);
app.listen(3000);
- 访问 http://127.0.0.1:3000 ,命令行就会输出日志。
1534751382271 GET /
功能可以拆分成一个独立函数">2、上一个例子里面的Logger功能,可以拆分成一个独立函数
const Koa = require('koa');
const app = new Koa();
const logger = (ctx,next)=>{
console.log(${Date.now()} ${ctx.request.method} ${ctx.request.url}
);
next();
}
const main = ctx =>{
ctx.response.body = '我是中间件'
}
app.use(logger);
app.use(main);
app.listen(3000);
- 像上面代码中的logger函数就叫做'中间件'(middleware),因为它处在HTTP Request和HTTP Response中间,用来实现某种中间功能。app.use()用来加载中间件。
- 基本上,Koa所有的功能都是通过中间件实现的,前面例子里面的main也是中间件。每个中间件默认接受两个参数,第一个参数是Context对象,第一个参数是next函数。只要调用next函数,就可以把执行权转交给下一个中间件。
要想实现原生路由,需要得到地址栏输入的路径,然后根据路径的不同进行跳转。用ctx.request.url就可以实现
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
let url = ctx.request.url
ctx.body = url
})
app.listen(3000)
功能">1、Logger功能
-
Koa的最大特色,也是最重要的一个设计,就是中间件(middleware)
const Koa = require('koa'); const app = new Koa();
const main = ctx => {
console.log(${Date.now()} ${ctx.request.method} ${ctx.request.url}
);
ctx.response.body = 'Hello World';
};app.use(main);
app.listen(3000); - 访问 http://127.0.0.1:3000 ,命令行就会输出日志。
1534751382271 GET /
功能可以拆分成一个独立函数">2、上一个例子里面的Logger功能,可以拆分成一个独立函数
const Koa = require('koa');
const app = new Koa();
const logger = (ctx,next)=>{
console.log(${Date.now()} ${ctx.request.method} ${ctx.request.url}
);
next();
}
const main = ctx =>{
ctx.response.body = '我是中间件'
}
app.use(logger);
app.use(main);
app.listen(3000);
- 像上面代码中的logger函数就叫做'中间件'(middleware),因为它处在HTTP Request和HTTP Response中间,用来实现某种中间功能。app.use()用来加载中间件。
- 基本上,Koa所有的功能都是通过中间件实现的,前面例子里面的main也是中间件。每个中间件默认接受两个参数,第一个参数是Context对象,第一个参数是next函数。只要调用next函数,就可以把执行权转交给下一个中间件。
``
const Koa = require('koa');
const app = new Koa();
const one = (ctx,next) => {
console.log('>> one');
next();
console.log('<< one');
}
const two = (ctx,next) => {
console.log('>> two');
next();
console.log('<< two');
}
const three = (ctx,next) => {
console.log('>> three');
next();
console.log('<< three');
}
app.use(one);
app.use(two);
app.use(three);
app.listen(3000);</code></pre>
-
迄今为止,所有例子的中间件都是同步的,不包含异步操作。如果有异步操作(比如读取数据库),中间件就必须写成async函数
const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa();
const main = async function(ctx,next){
ctx.response.type = 'html';
ctx.response.body = await fs.readFile('./demos/template.html','utf-8')
}
app.use(main);
app.listen(3000);
fs.readFile是一个异步操作,必须写成await fs.readFile(),然后中间件必须写成async函数
-
koa-compose模块可以将多个中间件合成为一个
const Koa = require('koa');
const compose = require('koa-compose');
const app = new Koa();
const logger = (ctx,next) =>{
console.log(${Date.now()} ${ctx.request.method} ${ctx.request.url}
);
next();
}
const main = ctx=>{
ctx.response.body = 'hello world2'
}
const middlewares = compose([logger,main])
app.use(middlewares);
app.listen(3000);
- 对于POST请求的处理,koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中
- 使用npm进行安装,需要注意的是我们这里要用–save,因为它在生产环境中需要使用。
- npm install --save koa-bodyparser@3
- 安装完成后,需要在代码中引入并使用。我们在代码顶部用require进行引入。
- const bodyParser = require('koa-bodyparser');
- 然后进行使用,如果不使用是没办法调用的,使用代码如下。
- app.use(bodyParser());
-
在代码中使用后,直接可以用ctx.request.body进行获取POST请求参数,中间件自动给我们作了解析。
const Koa = require('koa');
const app = new Koa();
const bodyParser = require('koa-bodyparser');
app.use(bodyParser())
app.use(async(ctx)=>{
//当请求是get请求时,显示表单让用户填写
if(ctx.url === '/' && ctx.method === 'GET'){
let html = `
Koa2 request post demo
`;
ctx.body = html;
//当请求是post请求时
}else if(ctx.url === '/' && ctx.method === 'POST'){
let postData = ctx.request.body;
ctx.body = postData;
}else{
//其他页面显示404页面
ctx.body = '404!
'
}
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
错误处理">四、错误处理
错误">1、500错误
-
如果代码运行过程中发生错误,我们需要吧错误信息返回给用户。HTTP协定约定这时要返回500状态码。Koa提供了ctx.throw()方法,用来抛出错误,ctx.throw(500)错误。
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.throw(500);
};
app.use(main);
app.listen(3000);
错误-1">2、404错误
-
如果将ctx.response.status设置成404,就相当于ctx.throw(404),返回404错误
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.response.status = 404;
ctx.response.body = 'Page Not Found--404'
}
app.use(main);
app.listen(3000);
错误的中间件">3、处理错误的中间件
-
为了方便处理错误,最好使用try...catch将其捕获。但是,为每个中间件都写try...catch太麻烦,我们可以让最外层的中间件,负责所有中间件的错误处理
const Koa = require('koa');
const app = new Koa();
const handler = async (ctx,next) => {
try {
await next();
}catch (err){
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.body = {
message: err.message
};
}
};
const main = ctx =>{
ctx.throw(500);
}
app.use(handler);
app.use(main);
app.listen(3000);
-
运行过程中一旦出错,Koa会触发一个error事件。监听这个事件,也可以处理错误
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.throw(500);
};
app.on('error',(err,ctx) => {
console.error('server error',err);
});
app.use(main);
app.listen(3000);
-
需要注意的是,如果被try...catch捕获,就不会触发error事件。这时,必须调用ctx.app.emit(),手动释放error事件,才能让监听函数生效。
const Koa = require('koa');
const app = new Koa();
const handler = async (ctx,next) => {
try {
await next();
}catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.type = 'html';
ctx.response.body = '
Something wrong,please contact administrator
';
ctx.app.emit('error',err,ctx);
}
}
const main = ctx =>{
ctx.throw(500);
}
app.on('error',function(err){
console.log('logging error',err.message);
console.log(err)
})
app.use(handler);
app.use(main);
app.listen(3000);
main函数抛出错误,被handler函数捕获。catch代码块里面使用ctx.app.emit()手动释放error事件,才能让监听函数监听到
功能">五、Web App的功能
-
ctx.cookies 用来读写Cookie
const Koa = require('koa');
const app = new Koa();
const main = function(ctx){
const n = Number(ctx.cookies.get('view') || 0)+1;
ctx.cookies.set('view',n);
ctx.response.body = n + 'views';
}
app.use(main);
app.listen(3000);
- ctx.cookies.get(name,[options]) 读取上下文请求中的cookie
ctx.cookies.set(name,value,[options]) 在上下文中写入cookie
- domain : 写入cookie所在的域名
- path : 写入cookie所在的路径
- maxAge: cookie最大有效时长
- expires: cookie失效时间
- httpOnly: 是否只用http请求中获得
- overwirte: 是否允许重写
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
if (ctx.url === '/index'){
ctx.cookies.set(
'myName','xiaoLiLi',{
domain: '127.0.0.1',//写cookie所在的域名
path: '/index',//写cookie所在的路径
maxAge: 10006060*24,//cookie有效时长(一天)
expires: new Date('2018-12-28'),//cookie失效时间
httpOnly: false,//是否只用于http请求中获取
overwrite: false //是否允许重写
}
)
ctx.body = 'cookie is OK'
}else{
if(ctx.cookies.get('myName')){
ctx.body = ctx.cookies.get('myName');
}else{
ctx.body = 'cookie is none';
}
}
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000');
})
<h3 id="表单">2、表单
-
Web应用离不开处理表单。本质上,表单就是POST方法发送到服务器的键值对。koa-body模块可以用来从POST请求的数据体里面提取键值对。
const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();
const main = async function(ctx) {
const body = ctx.request.body;
if(!body.name) ctx.throw(400,'.name required');
ctx.body = {name: body.name};
}
app.use(koaBody());
app.use(main);
app.listen(3000);
文件上传">3、文件上传
-
const os = require('os');
const path = require('path');
const Koa = require('koa');
const fs = require('fs');
const koaBody = require('koa-body');
const app = new Koa();
const main = async function(ctx) {
const tmpdir = os.tmpdir();
const filePaths = [];
const files = ctx.request.body.files || {};
for (let key in files) {
const file = files[key];
const filePath = path.join(tmpdir,file.name);
const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);
filePaths.push(filePath);
}
ctx.body = filePaths;
};
app.use(koaBody({ multipart: true }));
app.use(main);
app.listen(3000);
- npm install --save koa-views
- npm install --save ejs
-
新建view/index.ejs
<%= title %> http://jspang.com/wp-admin/post.PHP?post=2760&action=edit#
<%= title %>
EJS Welcome to <%= title %>
文件">编写koa文件
const Koa = require('koa');
const views = require('koa-views');
const path = require('path');
const app = new Koa();
//加载模板引擎
app.use(views(path.join(__dirname,'./view'),{
extension: 'ejs'
}))
app.use(async(ctx)=>{
let title = 'hello koa2';
await ctx.render('index',{
title
})
})
app.listen(3000,()=>{
console.log('demo server is starting at port 3000')
})
<h3 id="request-请求">request 请求
<h3 id="request别名">1、Request别名
- ctx.header
- ctx.headers
- ctx.method
- ctx.method=
- ctx.url
- ctx.url=
- ctx.originalUrl
- ctx.origin
- ctx.href
- ctx.path
- ctx.path=
- ctx.query
- ctx.query=
- ctx.querystring
- ctx.querystring=
- ctx.host
- ctx.hostname
- ctx.fresh
- ctx.stale
- ctx.socket
- ctx.protocal
- ctx.secure
- ctx.ip
- ctx.ips
- ctx.subdomains
- ctx.is()
- ctx.accepts()
- ctx.acceptsEncodings()
- ctx.acceptsCharsets()
- ctx.acceptsLanguages()
- ctx.get()
在koa2中GET请求通过request接收,但是接受的方法有两种:query和querystring。
- query:返回的是格式化好的参数对象。
querystring:返回的是请求字符串。
- ctx.body
- ctx.body=
- ctx.status
- ctx.status=
- ctx.message
- ctx.message=
- ctx.length=
- ctx.length
- ctx.type=
- ctx.type
- ctx.headerSent
- ctx.redirect()
- ctx.attachment()
- ctx.set()
- ctx.append()
- ctx.remove()
- ctx.lastModeified=
- ctx.etag=
- Koa Response 对象是在node的vanilla响应对象之上的抽象,提供诸多多HTTP服务器开发有用的功能
- 响应标头对象
- 响应标头对象。别名是response.header
- 请求套接字
代码设置响应状态">通过数组代码设置响应状态
- 100 "continue"
- 101 "switching protocols"
- 102 "processing"
- 200 "ok"
- 201 "created"
- 202 "accepted"
- 203 "non-authoritative information"
- 204 "no content"
- 205 "reset content"
- 206 "partial content"
- 207 "multi-status"
- 208 "already reported"
- 226 "im used"
- 300 "multiple choices"
- 301 "moved permanently"
- 302 "found"
- 303 "see other"
- 304 "not modified"
- 305 "use proxy"
- 307 "temporary redirect"
- 308 "permanent redirect"
- 400 "bad request"
- 401 "unauthorized"
- 402 "payment required"
- 403 "forbidden"
- 404 "not found"
- 405 "method not allowed"
- 406 "not acceptable"
- 407 "proxy authentication required"
- 408 "request timeout"
- 409 "conflict"
- 410 "gone"
- 411 "length required"
- 412 "precondition Failed"
- 413 "payload too large"
- 414 "uri too long"
- 415 "unsupported media type"
- 416 "range not satisfiable"
- 417 "expectation Failed"
- 418 "I'm a teapot"
- 422 "unprocessable entity"
- 423 "locked"
- 424 "Failed dependency"
- 426 "upgrade required"
- 428 "precondition required"
- 429 "too many requests"
- 431 "request header fields too large"
- 500 "internal server error"
- 501 "not implemented"
- 502 "bad gateway"
- 503 "service unavailable"
- 504 "gateway timeout"
- 505 "http version not supported"
- 506 "variant also negotiates"
- 507 "insufficient storage"
- 508 "loop detected"
- 510 "not extended"
- 511 "network authentication required"
- 获取响应的状态信息。默认情况下,response.message与response.status关联。
- 将响应的状态信息设置为给定值
- 将响应的Content-Length设置为给定值
- 以数字返回相应的Content-Length,或者从ctx.body推导出来,或者undefined。
- 获取响应主体
- string 写入
- Buffer 写入
- Stream 管道
- Object || Array JSON-字符串化
- null 无内容响应
- 如果response.status未被设置,koa将会自动设置状态为200或204
- Content-Type默认为text/html或text/plain,同时默认字符串是utf-8。Content-Length字段也是如此
- Content-type默认为application/octet-stream,并且Content-Length字段也是如此
- Content-Type 默认为application/octet-stream
- Content-Type默认为application/json。这包括普通的对象{foo:'bar'}和数组['foo','bar']
- 不区分大小写获取响应头字段值field
const etag = ctx.response.get(ETag);
-
设置响应头field到value
ctx.set('Cache-Control','no-cache')
-
用值val附加额外的标头field
ctx.append('Link',' ');
-
用一个对象设置多个响应标头fields:
ctx.set({
'Etag': '1234','Last-Modified': date
});
- 删除标头field
-
获取响应Content-Type不含参数'charset'
const ct = ctx.type;
-
设置响应Content-Type通过mime字符串或文件扩展名
ctx.type = 'text/plain; charset=utf-8';
ctx.type = 'image/png';
ctx.type = '.png';
ctx.type = 'png';
注意: 在适当的情况下为你选择 charset,比如 response.type = 'html' 将默认是 "utf-8". 如果你想覆盖 charset,使用 ctx.set('Content-Type','text/html') 将响应头字段设置为直接值。
非常类似 ctx.request.is(). 检查响应类型是否是所提供的类型之一。这对于创建操纵响应的中间件特别有用。
-
例如,这是一个中间件,可以削减除流之外的所有HTML响应。
const minify = require('html-minifier');
app.use(async (ctx,next) => {
await next();
if (!ctx.response.is('html')) return;
let body = ctx.body;
if (!body || body.pipe) return;
if (Buffer.isBuffer(body)) body = body.toString();
ctx.body = minify(body);
});
执行 [302] 重定向到 url.
-
字符串 “back” 是特别提供Referrer支持的,当Referrer不存在时,使用 alt 或“/”。
ctx.redirect('back');
ctx.redirect('back','/index.html');
ctx.redirect('/login');
ctx.redirect('http://google.com');
-
要更改 “302” 的默认状态,只需在该调用之前或之后分配状态。要变更主体请在此调用之后:
ctx.status = 301;
ctx.redirect('/cart');
ctx.body = 'Redirecting to shopping cart';
- 将 Content-Disposition 设置为 “附件” 以指示客户端提示下载。(可选)指定下载的 filename。
- 将 Last-Modified 标头返回为 Date,如果存在。
-
将 Last-Modified 标头设置为适当的 UTC 字符串。您可以将其设置为 Date 或日期字符串。
ctx.response.lastModified = new Date();
-
设置包含 " 包裹的 ETag 响应, 请注意,没有相应的 response.etag getter。
ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
- 在 field 上变化。
- 刷新任何设置的标头,并开始主体。