Koa
Koa
Koa 入门
npm install--save koa
//引入 Koa
const koa=require('koa');
const app=new koa();
//配置中间件 (可以先当做路由)
app.use( async (ctx)=>{
ctx.body='hello koa2'
})
//监听端口
app.listen(3000);
2
3
4
5
6
7
8
9
Koa 路由
npm install --save koa-router
const Koa = require('koa');
const router = require('koa-router')(); //注意:引入的方式
const app = new Koa();
router.get('/', function (ctx, next) {
ctx.body="Hello koa";
})
router.get('/news',(ctx,next)=>{
ctx.body="新闻 page"
});
app.use(router.routes()); //作用:启动路由
app.use(router.allowedMethods()); // 作用: 这是官方文档的推荐用法,我们可以 看到 router.allowedMethods()用在了路由匹配 router.routes()之后,所以在当所有 路由中间件最后调用.此时根据 ctx.status 设置 response 响应头
app.listen(3000,()=>{
console.log('starting at port 3000');
})
2
3
4
5
6
7
8
9
10
11
12
13
14
get 传值
在 koa2 中 GET 传值通过 request 接收,但是接收的方法有两种:query 和 querystring。
- query:返回的是格式化好的参数对象。
- querystring:返回的是请求字符串。
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/', function (ctx, next) {
ctx.body="Hello koa";
})
router.get('/newscontent',(ctx,next)=>{
let url =ctx.url; //从 request 中获取 GET 请求
let request =ctx.request;
let req_query = request.query;
let req_querystring = request.querystring; //从上下文中直接获取
let ctx_query = ctx.query;
let ctx_querystring = ctx.querystring;
ctx.body={
url,
req_query,
req_querystring,
ctx_query,
ctx_querystring
}
});
app.use(router.routes()); //作用:启动路由
app.use(router.allowedMethods()); //作用: 当请求出错时的处理逻辑
app.listen(3000,()=>{
console.log('starting at port 3000');
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
动态路由
//请求方式 http://域名/product/123
router.get('/product/:aid',async (ctx)=>{
console.log(ctx.params); //{ aid: '123' } //获取动态路由的数据
ctx.body='这是商品页面';
});
2
3
4
5
post
原生 Nodejs 获取 post 提交数据
functionparsePostData(ctx){
returnnewPromise((resolve,reject)=>{
try{
letpostdata="";
ctx.req.on('data',(data)=>{
postdata+=data
})
ctx.req.on("end",function(){
resolve(postdata);
})
}catch(error){
reject(error);
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Koa 中 koa-bodyparser 中间件的使用
npm install--savekoa-bodyparser
var Koa=require('koa');
var bodyParser=require('koa-bodyparser');
var app=newKoa();
app.use(bodyParser());
app.use(async ctx=>{
ctx.body=ctx.request.body; // 获取 post 提交的数据
});
2
3
4
5
6
7
中间件
通俗的讲:中间件就是匹配路由之前或者匹配路由完成做的一系列的操作,我们就可以 把它叫做中间件。
在express中间件(Middleware)是一个函数,它可以访问请求对象(requestobject(req)) , 响应对象(responseobject(res)), 和 web 应用中处理请求-响应循环流程中的中间件,一 般被命名为 next 的变量。在 Koa 中中间件和 express 有点类似。
中间件的功能包括:
- 执行任何代码。
- 修改请求和响应对象。
- 终结请求-响应循环。
- 调用堆栈中的下一个中间件。
如果我的 get、post 回调函数中,没有 next 参数,那么就匹配上第一个路由,就不会往下匹 配了。如果想往下匹配的话,那么需要写 next()
应用级中间件
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
app.use(async (ctx,next)=>{
console.log(new Date());
await next();
})
router.get('/', function (ctx, next) {
ctx.body="Hello koa";
})
router.get('/news',(ctx,next)=>{
ctx.body="新闻页面"
});
app.use(router.routes()); //作用:启动路由
app.use(router.allowedMethods()); //作用: 当请求出错时的处理逻辑
app.listen(3000,()=>{
console.log('starting at port 3000');
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
路由中间件
router.get('/', async(ctx, next)=>{
console.log(1)
next()
})
router.get('/', function (ctx) {
ctx.body="Hello koa";
})
2
3
4
5
6
7
错误处理中间
app.use(async (ctx,next)=> {
next();
if(ctx.status==404){
ctx.status = 404;
ctx.body="这是一个 404 页面"
}
});
2
3
4
5
6
7
第三方中间件
// 静态资源中间件
const static = require('koa-static');
const staticPath = './static';
app.use(static(
path.join( __dirname, staticPath)
))
//
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
2
3
4
5
6
7
8
9
Koa 中间件的执行顺序
Koa 的中间件和 Express 不同,Koa 选择了洋葱圈模型。
Koa Cookie
1、Koa 中设置 Cookie 的值
ctx.cookies.set(name,value,[options])
通过options 设置 cookie name 的value:
options名称 | options值 |
---|---|
maxAge | 一个数字表示从 Date.now() 得到的毫秒数 |
expires | cookie 过期的Date |
path | cookie 路径, 默认是'/' |
domain | cookie 域名 |
secure | 安全 cookie 默认 false,设置成 true 表示 只有 https 可以访问 |
httpOnly | 是否只是服务器可访问 cookie, 默认是true |
overwrite | 一个布尔值,表示是否覆盖以前设置的同名 的 cookie(默认是 false). 如果是 true, 在同 一个请求中设置相同名称的所有 Cookie(不 管路径或域)是否在设置此 Cookie 时从 Set-Cookie 标头中过滤掉。 |
2、Koa 中获取 Cookie 的值
ctx.cookies.get('name');
3、Koa 中设置中文 Cookie
console.log(new Buffer('hello, world!').toString('base64'));// 转换成base64字符 串:aGVsbG8sIHdvcmxkIQ==
console.log(new Buffer('aGVsbG8sIHdvcmxkIQ==', 'base64').toString());// 还原base 64字符串:hello, world!
2
Session
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服务器上。
Session 的工作流程
当浏览器访问服务器并发送第一次请求时,服务器端会创建一个session对象,生成一个类似于key,value的键值对, 然后将key(cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带key(cookie),找到对应的session(value)。 客户的信息都保存在session中
koa-session
1.安装 express-session
npm install koa-session --save
2.引入express-session
const session = require('koa-session');
3.设置官方文档提供的中间件
app.keys = ['some secret hurr'];
const CONFIG = {
key: 'koa:sess', //cookie key (default is koa:sess)
maxAge: 86400000, // cookie 的过期时间 maxAge in ms (default is 1 days)
overwrite: true, //是否可以 overwrite (默认 default true)
httpOnly: true, //cookie 是否只有服务器端可以访问 httpOnly or not (default true)
signed: true, //签名默认 true
rolling: false, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
renew: false, //(boolean) renew session when session is nearly expired,
};
app.use(session(CONFIG, app));
2
3
4
5
6
7
8
9
10
11
4.使用
ctx.session.username = "张三"; // 设置值
ctx.session.username // 获取值
2
Cookie 和 Session 区别
- cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
- cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗 考虑到安全应当使用 session。
- session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用 COOKIE。
- 单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。
koa-static 静态资源中间件
const Koa = require('koa')
const static = require('koa-static')
const app = new Koa()
app.use(require('koa-static')(__dirname + '/public'))
app.use(static('views')) // 静态资源中间件
2
3
4
5
koa2-cors 跨域
const Koa = require('koa')
const cors = require('koa2-cors') // 跨域
const app = new Koa()
app.use(cors())
2
3
4
5
koa-json
const Koa = require('koa')
const json = require('koa-json')
const app = new Koa()
app.use(json())
2
3
4
5
koa-logger
const Koa = require('koa')
const logger = require('koa-logger')
const app = new Koa()
app.use(logger())
app.use(async (ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})
2
3
4
5
6
7
8
9
10
11
12
koa-jwt token中间件
const Koa = require('koa')
const jwt = require('koa-jwt')
const app = new Koa()
// jwt token中间件
app.use(jwt({
secret: 'mds'
}).unless({path: [/^\/mds\/login/]}))
2
3
4
5
6
7
8
验证中间件
aap.js
const Koa = require('koa')
const errorHandle = require('./utils/errorHandle')
const sendHandle = require('./utils/sendHandle')
const app = new Koa()
// 验证中间件
app.use(sendHandle())
app.use(errorHandle())
2
3
4
5
6
7
8
9
10
sendHandle.js
const sendHandle = () => {
// 处理请求成功方法
const render = ctx => {
return (data, msg = '请求成功') => {
ctx.set('Content-Type', 'application/json');
ctx.body = {
code: '200',
data,
msg
}
}
}
// 处理请求失败方法
const renderError = ctx => {
return (code, msg = '请求失败') => {
ctx.set('Content-Type', 'application/json');
ctx.body = {
code,
data: null,
msg
}
}
}
return async (ctx, next) => {
ctx.send = render(ctx);
ctx.sendError = renderError(ctx);
await next();
}
}
module.exports = sendHandle;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
errorHandle.js
const jwt = require('jsonwebtoken')
const verify = require('util').promisify(jwt.verify)
const errorHandle = function () {
return async (ctx, next) => {
try {
const token = ctx.header.authorization
if (token) {
let payload
try {
payload = await verify(token.split(' ')[1], 'mds')
ctx.user = payload
} catch (err) {
console.log('token verify fail:', err)
}
}
await next()
} catch (err) {
if (err.status === 401) {
ctx.status = 401
ctx.sendError('401', '页面登录已失效,请重新登录')
} else {
ctx.status = 404
ctx.body = {
code: 404,
msg: '404'
}
}
}
}
}
module.exports = errorHandle
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
sequelize
Sequelize是一个基于promise的关系型数据库ORM框架,这个库完全采用JavaScript开发并且能够用在Node.JS环境中,易于使用,支持多SQL方言(dialect),。它当前支持MySQL,、MariaDB、SQLite、PostgreSQL、Sql Server 数据库。
对象-关系映射(Object/Relation Mapping,简称ORM)
ORM方法论基于三个核心原则:
- 简单性:以最基本的形式建模数据。
- 传达性:数据库结构被任何人都能理解的语言文档化。
- 精确性:基于数据模型创建正确标准化了的结构。
ORM包括以下四部分:
- 一个对持久类对象进行CRUD操作的API;
- 一个语言或API用来规定与类和类属性相关的查询;
- 一个规定mapping metadata的工具;
- 一种技术可以让ORM的实现同事务对象一起进行dirty checking, lazy association fetching以及其他的优化操作。
sequelize 连接mysql
const Sequelize = require('sequelize');
const sequelize = new Sequelize('mds_node','root','root',{
host:'localhost',
port: 3306,
dialect:'mysql',
define: {
timestamps: true
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
})
module.exports = sequelize;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sequelize-auto 生成models
auto.js
var SequelizeAuto = require('sequelize-auto')
var auto = new SequelizeAuto('mds', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
directory: '../models',
port: '3306',
additional: {
timestamps: false,
underscored:true,
}
}
)
auto.run(function (err) {
if (err) throw err;
console.log(auto.tables); // table list
console.log(auto.foreignKeys); // foreign key list
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
生成model关系模型
createModelsExport.js
let fs = require('fs')
let files = fs.readdirSync('../models')
let models = []
// 解析名称做成驼峰命名法
files.forEach(item => {
if (item != 'index.js') {
let names = item.split('.')[0].split('_')
let name = ''
for (let i = 0; i < names.length; i++) {
name += names[i].substring(0,1).toUpperCase() + names[i].substring(1)
}
models.push({name: name, path: './' + item})
}
})
// 文件内容模板
const template = `
const Sequelize = require('sequelize');
const sequelize = require('../db/dbconfig')
// 数据库模型名称及路径
const models =${JSON.stringify(models, null, 4)}
// 数据模型输出
models.forEach(item => {
module.exports[item.name] = require(item.path)(sequelize, Sequelize)
})
`
fs.writeFile("../models/index.js", template, function () {
console.log('创建成功')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
sequelize同步模型到数据库
const sequelize = require('../db/connectDB')
const User = sequelize.import('./user')
const Role = sequelize.import('./role')
// 模型之间的关系
User.belongsToMany(Role, {through: 'sys_user_roles', as:'UserRoles'});
Role.belongsToMany(User, {through: 'sys_user_roles', as:'UserRoles'});
// 同步模型到数据库
sequelize.sync()
exports.User = User
exports.Role = Role
2
3
4
5
6
7
8
9
10
11
12
13
login生成token
controllers层
jsonwebtoken生成token 由三部分组成
bcrypt.hashSync加密
const UserModel = require('../models').User
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const salt = bcrypt.genSaltSync(10);
module.exports = login = async (ctx)=>{
const user = ctx.request.body
const userSigned = await UserModel.findOne({
where: {
username: user.username
}
})
if (!userSigned) {
ctx.body = {
code:500,
msg:'用户不存在'
}
return
} else {
const hashPwd = bcrypt.hashSync(user.password, salt);
console.log(hashPwd) // 加密密码
if (!bcrypt.compareSync(user.password, userSigned.password)) {
ctx.body = {
code: 1000,
msg: '密码不正确'
}
return
} else {
ctx.user = {
userId: userSigned.userId,
username: userSigned.username
}
// jsonwebtoken生成token 由三部分组成
const token = jwt.sign({
exp: Math.floor(Date.now() / 1000) + 60 * 30,
userId: userSigned.userId,
username: userSigned.username
},'mds')
ctx.body = {
code: 0,
msg: 'success',
list: [userSigned],
Authorization: token
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
基本sql语句
sql | orm |
---|---|
select | findAll,findOne,findById,findOrCreate,findAndCountAll |
delete | destroy |
update | update |
insert | create |
SELECT foo, bar ... | Model.findAll({attributes: ['foo', 'bar']}); |
SELECT foo, bar AS baz ... | Model.findAll({attributes: ['foo', ['bar', 'baz']]}); |
SELECT COUNT(hats) AS no_hats ... | Model.findAll({attributes: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']]}); |
SELECT id, foo, bar, quz ... | Model.findAll({attributes: {exclude: ['baz'] }}); |
WHERE
sql | orm |
---|---|
SELECT * FROM post WHERE authorId = 12 AND status = 'active' | Post.findAll({where: { authorId: 2,status: 'active'}}); |
Operators
Post.update({
updatedAt: null,
}, {
where: {
deletedAt: {
$ne: null
}
}
});
// UPDATE post SET updatedAt = null WHERE deletedAt NOT NULL;
Post.findAll({
where: sequelize.where(sequelize.fn('char_length', sequelize.col('status')), 6)
});
// SELECT * FROM post WHERE char_length(status) = 6;
{
rank: {
$or: {
$lt: 1000,
$eq: null
}
}
}
// rank < 1000 OR rank IS NULL
{
createdAt: {
$lt: new Date(),
$gt: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
// createdAt < [timestamp] AND createdAt > [timestamp]
{
$or: [
{
title: {
$like: 'Boat%'
}
},
{
description: {
$like: '%boat%'
}
}
]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
op | define |
---|---|
$and: {a: 5} | AND (a = 5) |
$or: [{a: 5}, {a: 6}] | (a = 5 OR a = 6) |
$gt: 6, | > 6 |
$gte: 6, | >= 6 |
$lt: 10, | < 10 |
$lte: 10, | <= 10 |
$ne: 20, | != 20 |
$between: [6, 10], | BETWEEN 6 AND 10 |
$notBetween: [11, 15], | NOT BETWEEN 11 AND 15 |
$in: [1, 2], | IN [1, 2] |
$notIn: [1, 2], | NOT IN [1, 2] |
$like: '%hat', | LIKE '%hat' |
$notLike: '%hat' | NOT LIKE '%hat' |
$iLike: '%hat' | ILIKE '%hat' (case insensitive) (PG only) |
$notILike: '%hat' | NOT ILIKE '%hat' (PG only) |
$like: { $any: ['cat', 'hat']} | LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike |
$overlap: [1, 2] | && [1, 2] (PG array overlap operator) |
$contains: [1, 2] | @> [1, 2] (PG array contains operator) |
$contained: [1, 2] | <@ [1, 2] (PG array contained by operator) |
$any: [2,3] | ANY ARRAY[2, 3]::INTEGER (PG only) |
$col: 'user.organization_id' | "user"."organization_id", with dialect specific column identifiers, PG in this example --$col取表的字段 |
tabel need primarykey:
如果没有,Sequlize会自己加个自增主键,可能引起错误
findOrCreate:
查到一个,查不到就新建
models.BrandReview.findOrCreate({
where: {
mem_id: mem_id,
brand_id: brand_id
},
defaults: {
score: score //新建的数据
}
})
//返回值为数组,[json,created] 第一位是查询或创建的数据,第二位标识是否新建
2
3
4
5
6
7
8
9
10
update:
返回值为数据,[2],数字代码改动记录数
destroy:
返回数字,代表删除记录数
使用原生sql
官网中代码例子
//1
sequelize.query('SELECT 1', {
logging: console.log,
plain: false,
raw: false,
type: Sequelize.QueryTypes.SELECT
})
//2
sequelize
.query('SELECT * FROM projects', { raw: true })
.then(projects => {
console.log(projects)
})
//3
sequelize.query('SELECT * FROM projects WHERE status = ?',
{ replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
//4
sequelize.query('SELECT * FROM projects WHERE status = :status ',
{ replacements: { status: 'active' }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
sequelize中提供了query函数,用于直接操作原生语句
该函数将返回两个参数 - 结果数组和包含元数据的对象,对于mysql将是返一对象的两个引用。
query函数的第二个参数,是一个对象,对象里面几个常用参数进行说明。
- pain:如果plain为真,那么sequelize只返回第一个 记录结果集。如果为false,则返回所有记录。
- type:正在执行的查询的类型(也可以is hi更新哦,具体哪些去看官网api)。查询类型影响返回结果之前的格式化方式。
- raw:查询对类型是否有模型定义,如果您的查询没有模型定义,请将此设置为true。
- logging: console.log记录查询的函数 是否会为发送的每个SQL查询调用 到服务器。
对于查找条件where后面的字段
- 如果传递数组,?将按它们在数组中出现的顺序进行替换。
- 如果传递了一个对象,:key则将替换该对象中的键。如果对象包含查询中未找到的键,会抛出查询异常。
对于替换where后面的变量,也可以使用in关键字从数组匹配,也可以使用通配符like%等 代码如下:
//in关键字使用官网例子
sequelize.query('SELECT * FROM projects WHERE status IN(:status) ',
{ replacements: { status: ['active', 'inactive'] }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
//like通配符关键字使用官网例子
sequelize.query('SELECT * FROM users WHERE name LIKE :search_name ',
{ replacements: { search_name: 'ben%' }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
console.log(projects)
})
2
3
4
5
6
7
8
9
10
11
12
- 查询的时候还可以直接传递model,如果传递模型,则返回的数据将是该模型的实例,上面其它字段也可以在这使用
sequelize
.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // 如果有任何映射字段,则在这里传递true
})
.then(projects => {
// Each record will now be an instance of Project
})
2
3
4
5
6
7
8
let sqlRank = `SELECT userspk.avatar AS user_avatar,
userspk.gender AS user_gender,
userspk.nickname AS user_nickname,
a.id AS pk_record_id,
a.user_id,
a.answer_record,
a.pk_type,
MAX(score) AS score,
a.create_time
FROM (select * from pkrecord order by score desc,create_time asc) as a
INNER JOIN userspk AS userspk
ON a.user_id = userspk.user_id
WHERE a.status = 1
AND a.pk_type = 'noreal'
AND a.subject_id = :subject_id
GROUP BY user_id
ORDER BY a.score DESC
LIMIT 3;`
let pkRankResult= await ctx.main.query(sqlRank, {
replacements: {
subject_id: subject_id,
},
type: Sequelize.QueryTypes.SELECT }
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24