目录
5.1context 将node中的request和response 封装到一个对象中,并提供一些新的api提供给用户进行操作;
5.2.1koa会把ctx.request上的属性直接挂载到ctx上如:
5.2.2同样也会把ctx.response上的属性直接挂载到ctx上如:
6.3Koa-router推荐使用RESTful架构API。
1.课堂目标及知识点
课堂目标
- npm包管理器使用
- koa使用
- 了解koa中的applition对象及context对象
- koa-views使用
- koa-static使用
- 使用koa-router中间件搭建路由
- 了解常见http状态码
本节知识点
- http模块实现模板加载及静态文件处理
- koa的安装及使用
- Application及context对象介绍
- 常用http状态码介绍
- 路由中间件koa-router介绍
- 中间件koa-views介绍
2.koa介绍
- koa是express原班人马打造的轻量级、健壮性、富有表现力的nodejs的框架。
- 目前koa有koa1和koa2两个版本;
- koa2依赖Node.js 7.6.0或者更高版本;
- koa不在内核方法中绑定任何中间件,它仅仅是一个轻量级的函数库,几乎所有功能都必须通过第三方插件来实现。
3.koa使用
3.1koa安装
$ npm i koa
3.2一个简单的koa服务器
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
3.3Koa 利用中间件 控制"上游",调用"下游“;
- koa是包含一组中间件函数的对象;可以将app.use里的函数理解成中间件
- ctx即context上下文,是原来node.js的req和res的合体分别对应ctx.req、ctx.res,context会将res和req封装成ctx.request和ctx.respones;
//这里的middleWare函数就是一个中间件
let middleWare = async (ctx,next)=>{
console.log("first middleWare");
ctx.body = "hello world";
}
app.use(middleWare);
- 通过next()将控制转交给另一个中间件;有next()函数才会执行下一个中间件。next()调用后才会执行中间件,不调用则不会执行
- 一般都是使用第三方提供的中间件。
- 中间件中如果有异步逻辑,通过async await处理(await后跟的一定是promise对象);async await同步写法实现异步功能
- 上述过程也可以通过"洋葱模型“来解释中间件执行顺序
中间件执行示例:有next()函数才会执行下一个中间件。如此处next()调用后才会执行中间件,不调用则不会执行
//node.js的框架koa
const Koa = require("koa");
const app = new Koa();
const middleWare3 = require("./m3");
//中间件
let middleWare1 = (cxt, next) => {
console.log("one start......");
cxt.body = "hello world11111";
//当前表示中间件转交给下一个中间件,有next()函数才会执行下一个中间件。如此处next()调用后才会执行中间件,不调用则不会执行
next();
console.log("one end......");
}
let middleWare2 = (cxt, next) => {
console.log("two start......");
cxt.body = "hello world22222";
next();
console.log("two end......");
}
app.use(middleWare1);
app.use(middleWare2);
// 中间件也可以是引入方式
app.use(middleWare3);
/*
中间件执行结果:
one start......
two start......
three start........
three end ..........
two end......
one end......
*/
// app.on("error",(err)=>{
// console.log(err);
// });
app.listen(4000);
中间件执行结果:
one start......
two start......
three start........
three end ..........
two end......
one end......
4.Application对象
- application是koa的实例 简写app
- app.use 将给定的中间件方法添加到此应用程序,分为同步和异步,异步:通过es7中的async和await来处理
- app.listen设置服务器端口;
- app.on 错误处理;
5.上下文context对象常用属性及方法
5.1context 将node中的request和response 封装到一个对象中,并提供一些新的api提供给用户进行操作;
- ctx.app:应用程序实例引用,等同于app;
- ctx.req:Node 的
request
对象. - ctx.res:Node 的
response
对象. - ctx.request:koa中的Request对象;
- ctx.response:koa中的response对象;
- ctx.state:对象命名空间,通过中间件传递信息;
- ctx.throw:抛出错误;
错误处理:
// 错误处理
app.on("error",(err)=>{
console.log(err);
});
5.2request及response别名
5.2.1koa会把ctx.request上的属性直接挂载到ctx上如:
ctx.header
//头信息;ctx.headers
ctx.method
ctx.method=
ctx.request.methodctx.url
ctx.url=ctx
.request.url
5.2.2同样也会把ctx.response上的属性直接挂载到ctx上如:
ctx.body
ctx.body=ctx.response.body
ctx.status
ctx.status=ctx.response.status
5.2.3ctx.status 获取响应状态。
默认情况下,response.status
设置为 404
而不是像 node 的 res.statusCode
那样默认为 200
。
原生js和原生node.js中可以通过ctx.setHeader和ctx.writeHead()进行设置
5.2.4http状态码
-
http状态码:1xx(消息)、2xx(成功)、3xx(重定向)、4xx(请求错误)、5xx和6xx(服务器错误)
-
常见http状态码 (302 location 跳转)
HTTP状态码 描述 100 继续。继续响应剩余部分,进行提交请求 200 成功 301 永久移动。请求资源永久移动到新位置 302 临时移动。请求资源零时移动到新位置 304 未修改。请求资源对比上次未被修改,响应中不包含资源内容 401 未授权,需要身份验证 403 禁止。请求被拒绝 404 未找到,服务器未找到需要资源 500 服务器内部错误。服务器遇到错误,无法完成请求 503 服务器不可用。零时服务过载,无法处理请求 - 设置状态码
一般在获取成功或失败数据。如{info:"请求成功",status:0}以外,还需要给浏览器给出状态码,才更符合浏览器的规则
router.get("/getData", async (cxt,next)=>{
//cxt.status设置状态码
//302临时跳转,会跳转到设置的地址
cxt.status = 302;
// 设置头部
cxt.set("location","http://www.baidu.com");
cxt.body = {
name:'lmf',
age:23
};
});
6.koa常用中间件——koa-router路由
-
路由是引导匹配之意,是匹配url到相应处理程序的活动。
6.1koa-router安装
npm i koa-router -S
6.2Koa-router使用
- 请求方式:get/post/put/delete等;
- 使用localhost:5000和localhost:5000/地址是一样的
- koa框架中cxt.body会将对象数据直接转换为json数据格式
- 使用app.use(router.routes());将app(koa框架)和router进行关联
- koa-router中加载文件必须使用异步才能加载到文件,async await时同步写法实现异步功能,await后必须返回的是promise对象,此处await后,cxt.render()底层有返回promise对象
//中间件koa-router
const Koa = require("koa");
const Router = require("koa-router");
let app = new Koa();
let router = new Router();
//请求方式:get / post /put /delete 等
//直接打印"/"和什么都不输入一样
router.get("/",async (cxt,next)=>{
cxt.body = "hello world";
});
//传输对象格式数据(cxt.body会将对象数据直接转换为json数据)
//{"name":"lmf","age":23}
router.get("/getData", async (cxt,next)=>{
//cxt.status设置状态码
//302临时跳转,会跳转到设置的地址
cxt.status = 302;
cxt.set("location","http://www.baidu.com");
// koa会直接将对象转为json
cxt.body = {
name:'lmf',
age:23
};
});
// 通过此句将app和router进行关联
app.use(router.routes());
app.listen("5000");
6.3Koa-router推荐使用RESTful架构API。
Restful的全称是Representational State Transfer 即表现层转移。
- RESTful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。基于这个风格设计可以更简洁,更有层次;
- 非RESTful架构api:
- 使用RESTful架构设计api
REST设计一般符合如下条件:
- 程序或者应用的事物都应该被抽象为资源
- 每个资源对应唯一的URI(uri是统一资源标识符)
- 使用统一接口对资源进行操作
- 对资源的各种操作不会改变资源标识
- 所有操作都是无状态的(如session等)
一般的请求接口:
- www.test.com:/90/addUser 增加
- www.test.com:/90/deleteUser 删除
- www.test.com:/90/updateUser 修改
- www.test.com:/90/getUser 获取
REST设计的请求接口:接口地址都一样,而是通过请求方式的不同跳转到不同的方法。
- www.test.com:/90/user 增加 post
- www.test.com:/90/user 删除 delele
- www.test.com:/90/user 修改 put
- www.test.com:/90/user 获取 get
好处:
- 这种方式更符合浏览器规则;
- 在做多终端(PC端,安卓,ios等)时请求都可以通用;
- 接口抽象出来;
- 有统一的标识符
- 无状态原则,session等会话状态是不会保存的。所以这些就只能通过前端通过cookie进行保存,或者现有的解决方法jwt(json web token)
7.koa常用中间件——koa-views加载页面
- koa-views用于加载模板文件;
- 可以是任何类型的文件:html,也可以是模块文件(pug或nunjucks)
- 使用pug模板文件时,必须加载文件后缀名。如index.pug。
- nunjucks使用时不使用koa-views模块而是使用koa-nunjucks-2模块
7.1安装 koa-views
npm i koa-views -S
7.2使用koa-views
koaviews.js:
注意:可以是任何类型的文件:html,也可以是模块文件(pug或nunjucks),nunjucks使用时不使用koa-views模块而是使用koa-nunjucks-2模块
koa-router中加载文件必须使用异步才能加载到文件,async await同步写法实现异步功能,await后必须返回的是promise对象,此处await后,cxt.render()底层有返回promise对象。
//koa-view 渲染页面的中间件
const Koa = require("koa");
const Router = require("koa-router");
// 注意是koa-views不是koa-view
const views = require("koa-views");
let router = new Router();
let app = new Koa();
//koa-views设置文件路径和文件类型(路径views一定要和项目中页面文件路径一致)
app.use(views(__dirname+"/views"),{
//可以是任何类型的文件:html,也可以是模块文件(pug或nunjucks),nunjucks使用时不使用koa-views模块而是使用koa-nunjucks-2模块
map:{
html:"pug"
}
});
//koa-router中加载文件必须使用异步才能加载到文件,async await同步写法实现异步功能,await后必须返回的是promise对象,此处await后,cxt.render()底层有返回promise对象
router.get("/",async (cxt,next)=>{
// 注意使用pug时,一定要有pug后缀
await cxt.render("index.pug");
});
app.use(router.routes());
app.listen(8989);
index.pug:
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title Document
body
h1 pug页面
8.koa常用中间件——koa-static
-
koa-static 是用于加载静态资源的中间件,通过它可以加载css、js等静态资源;
-
安装 koa-static
npm i koa-static
-
使用koa-static
//koa-static 加载静态文件
const Koa = require("koa");
const Router = require("koa-router");
const views = require("koa-views");
const static = require("koa-static");
let router = new Router();
let app = new Koa();
//koa-view设置设置文件路径和文件类型
app.use(views(__dirname+"/views"),{
map:{
html:'pug'
}
});
//koa-static加载静态文件(注意static不需要再写)
app.use(static(__dirname+"/static"));
//koa-router中加载文件(必须使用异步才能加载到文件)
router.get("/",async (cxt,next)=>{
await cxt.render("index.pug");
});
app.use(router.routes());
app.listen("7000");
index.css:
h1 {
color:red;
}
index.pug:
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title Document
link(rel="stylesheet", href="/css/index.css")
body
h1 pug页面
9.使用koa框架重构新闻列表页面
注意点:
- 使用static后,在页面再引入静态页面,css,js,img等时路劲就不需要再写到static级;
- 使用cxt.render时,必须使用async await异步加载页面;
- 在node.js版本5.1之前通过命令:npm i 模块名 -S时,会自动创建package.json文件,但5.1之后只会自动创建package-lock.json文件(这个文件比package.json文件中的描述内容更加详尽),所以每次创建项目后,必须手动通过npm i在项目路径下创建package.json文件。创建之后,再通过命令 npm i 模块名 -S 安装某个模块时,才会将描述信息覆盖到package.json文件中。且-S会自动写入dependencies ,-D会自动写入devDependencies。另外使用cnpm命令创建模块时只会自动创建package.json文件,不会创建package-lock.json文件,但是不推荐使用cnpm命令。
koa框架重构新闻列表页面完整示例:
index.js:注意需要路由页面时,需要将所有页面需要的数据推送过去
const Koa = require("koa");
const Router = require("koa-router");
const views = require("koa-views");//注意是koa-views
const static = require("koa-static");
const url = require("url");
//注意要使用pug,必须导入pug
const pug = require("pug");
//cheerio实现类JQuery功能
const cheerio = require("cheerio");
let data = require("./data/data.json");
let app = new Koa();
let router = new Router();
//可以再views时设置,对应的页面后缀(注意此处设置模板也是views方法的参数)
app.use(views(__dirname + "/views", {
map: {
html: 'pug'
}
}));
// 使用static后,在页面再引入静态页面,css,js,img等时路劲就不需要再写到static级
app.use(static(__dirname + "/static/css"));
app.use(static(__dirname + "/static/img"));
//新闻列表显示
//注意使用cxt.render时,必须使用async await异步加载页面
router.get("/news/index", async (cxt, next) => {
//分页
let pageNum = url.parse(cxt.url,true).query.p || 1;
//通过pageNum进行分页
let pageSize = 5;
let pageTotal = Math.ceil(data.length / pageSize);
// 0-4 5-10 ((pageNum-1)*pageSize,pageNum*pageSize)
let curData = data.slice((pageNum-1)*pageSize,pageNum*pageSize);
//render是方法,不是属性,且文件必须写后缀
await cxt.render("viewlist.pug", {
curData,
pageTotal,
pageNum
});
});
//新闻详细也显示
router.get("/news/detail", async (cxt, next) => {
//分页
let id = url.parse(cxt.url,true).query.id || 1;
let detailData = data.find(item=>id == item.id);
//render是方法,不是属性,且文件必须写后缀
await cxt.render("detail.pug", {
detailData
});
});
app.use(router.routes());
app.listen("8989");
viewlist.pug:
meta(charset='UTF-8')
meta(name='viewport', content='width=device-width,initial-scale=1')
meta(http-equiv='X-UA-Compatible', content='ie=edge')
link(rel='stylesheet', href='../index.css')
title 文章信息展示
.wrap
ul.news-list
each item in curData
li.news
a(href='javascript:;')
img(src='../img.png', alt)
div
h3
a(href='/news/detail?id='+item.id) #{item.title}
.info
span.tips
span #{item.publisher}
// <span class="line"></span>
span.time | #{item.time}
.pagination
- let p = parseInt(pageNum);
a.prev(href=`/news/index?p=${Math.max(1,p-1)}`) ⌜
- for(let i=1;i<=pageTotal;i++)
- if(p == i)
a(href='/news/index?p='+i class="active") #{i}
- else
a(href='/news/index?p='+i) #{i}
a.next(href=`/news/index?p=${Math.min(pageTotal,p+1)}`) ⌝
detail.pug:
meta(charset='UTF-8')
meta(name='viewport', content='width=device-width,initial-scale=1')
meta(http-equiv='X-UA-Compatible', content='ie=edge')
title Document
style.
.text{
width: 640px;
margin: 0 auto;
}
.article-info{
color:#999;
font-size: 14px;
}
p{
font-size: 16px;
line-height: 30px;
}
.text
h1.title #{detailData.title}
.article-info 类型:#{detailData.publisher} 时间:#{detailData.title}
p.content.
#{detailData.title}
data.json:数据作相应更改
[
{
"id": 1,
"title": "13岁少年成暴徒,这才是社会的灾难",
"publisher": "海外网",
"time": "今天 17:08"
},
{
"id": 2,
"title": "举行新闻发布会(全文实录)",
"publisher": "中国网",
"time": "今天 16:59"
},
{
"id": 3,
"title": "XX学校禁读《哈利·波特》 称咒语召唤邪灵",
"publisher": "澎湃新闻",
"time": "今天 16:56"
},
{
"id": 4,
"title": "XXXXXXX滚回去",
"publisher": "海外网",
"time": "今天 16:54"
},
{
"id": 5,
"title": "西XX职位空缺190天后,迎来XX王浩",
"publisher": "上游新闻",
"time": "今天 16:54"
},
{
"id": 6,
"title": "中国游客在日本突然昏迷 2名韩国消防员及时相救",
"publisher": "海外网",
"time": "今天 16:53"
},
{
"id": 7,
"title": "XX零售雪上加霜 奢侈品牌普拉达将关闭在XX最大门店",
"publisher": "观察者网",
"time": "今天 16:46"
},
{
"id": 8,
"title": "拖了18年,XX为何此时从XXX?",
"publisher": "海外网",
"time": "今天 16:41"
},
{
"id": 9,
"title": "XXXXX?XXXX:系误读",
"publisher": "环球网",
"time": "今天 16:37"
},
{
"id": 10,
"title": "被男议员骂“不生孩子没尽国家责任” 韩国55岁女学者懵了",
"publisher": "海外网",
"time": "今天 16:29"
},
{
"id": 11,
"title": "中国XXXXX被提起公诉",
"publisher": "XXX察院",
"time": "今天 16:29"
},
{
"id": 12,
"title": "XXX假装尿急翻墙逃出营区 10天后在网吧被抓",
"publisher": "海外网",
"time": "今天 16:22"
},
{
"id": 13,
"title": "XXX“罢课演讲”出糗:成语连说了3遍都没对",
"publisher": "海外网",
"time": "今天 16:21"
},
{
"id": 14,
"title": "XXX、西安市XXX",
"publisher": "澎湃新闻网",
"time": "今天 16:09"
},
{
"id": 15,
"title": "XXX:少数暴徒目的在于搞乱XX 进而XXXX",
"publisher": "新京报网",
"time": "今天 16:00"
},
{
"id": 16,
"title": "XXXXX",
"publisher": "新京报即时新闻",
"time": "今天 15:53"
},
{
"id": 17,
"title": "XX“双普选”:必须符合XX政治地位",
"publisher": "新京报即时新闻",
"time": "今天 15:46"
},
{
"id": 18,
"title": "XX是否认为现在XX局势适用紧急法?XXX回应",
"publisher": "中国网",
"time": "今天 15:41"
},
{
"id": 19,
"title": "XX是否对XX事态设最后期限?XX回应",
"publisher": "新京报即时新闻",
"time": "今天 15:35"
},
{
"id": 20,
"title": "XX发言人:已到维护“XXX”底线的重要关头",
"publisher": "新京报即时新闻",
"time": "今天 15:18"
},
{
"id": 21,
"title": "商务部:上周猪肉批发价格上涨8.9%",
"publisher": "新京报即时新闻",
"time": "今天 15:14"
},
{
"id": 22,
"title": "XXX出糗:这个成语连说三遍都没对",
"publisher": "海外网",
"time": "今天 14:54"
},
{
"id": 23,
"title": "两高:高考等4类考试组织作弊属犯罪 最高判7年",
"publisher": "新京报即时新闻",
"time": "今天 14:33"
}
]
index.css:
body {
margin: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
a {
text-decoration: none;
color: #404040;
}
.wrap{
width: 600px;
margin: 0 auto;
}
.news-list {
width: 600px;
}
.news {
width: 100%;
display: flex;
justify-content: space-between;
padding: 15px 0;
border-bottom: 1px solid #999;
}
.info {
display: flex;
width: 170px;
justify-content: space-between;
font-size: 12px;
color: #888;
}
.tips {
display: flex;
width: 100px;
justify-content: space-between;
}
.news-list li:nth-child(5) {
border-bottom: none;
}
.pagination{
display: flex;
width: 210px;
text-align: center;
background-color: rgb(252, 238, 238);
border-radius: 25px;
overflow: hidden;
margin: 0 auto;
justify-content: center;
}
.pagination a{
width: 30px;
line-height: 30px;
color: #404040;
}
.pagination a:nth-child(1) {
transform: rotate(-45deg) ;
}
.next {
transform: rotate(45deg) ;
}
.pagination a:hover{
color: rgb(247, 73, 73);
}
.news div{
width:420px;
}
.pagination .active {
color: rgb(247, 73, 73);
}