Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。
Koa与Express风格相似,不一样在于默认异步解决方案和采用洋葱圈模型的中间件。javascript
Koa没有绑定任何中间件,简单的同时也缺失了不少Web程序基础的功能,如今咱们实现这些基础的功能:css
实现路由功能咱们使用到三个中间件,分别是koa-router
、koa-body
、koa-parameter
:html
koa-router
来实现最基础的路由功能,将不一样的url分发到相应的处理函数中;koa-body
对post请求的参数进行处理,将处理结果解析到ctx.request.body
中,koa-body
也可以处理上传文件,文件会被解析到ctx.request.files
中;koa-parameter
对传参进行校验,get请求会对query
进行校验,post请求则对body
进行校验,校验方法基于parameter。在server/index.js
中引用中间件:java
const Koa = require('koa') const app = new Koa() app.use(require('koa-body')({ multipart: true, formidable: { maxFileSize: 200*1024*1024 // 设置上传文件大小最大限制,默认2M } })) require('koa-parameter')(app) app.use(require('./api')) app.listen(3000)
在server/api.js
中分发请求进行相应的处理:node
const fs = require('fs') const path = require('path') const Router = require('koa-router') const api = new Router({ prefix: '/api' }) api.post('/test', ctx => { // 使用koa-parameter对参数进行校验 ctx.verifyParams({ name: { type: "string", required: true } }) // koa-body会将参数解析到ctx.request.body ctx.body = ctx.request.body }) api.post('/upload', ctx => { // 文件在ctx.request.files中以对象的形式保存,若是多个文件的key相同,则value是一个File对象组成的数组,结构{ key: <File|File[]>value } Object.keys(ctx.request.files).forEach(key => { const file = ctx.request.files[key] const reader = fs.createReadStream(file.path) const upStream = fs.createWriteStream(path.join(__dirname, '../dist/' + file.name)) reader.pipe(upStream) }) ctx.body = 'upload success' }) module.exports = api.routes()
执行命令node ./server/index.js
,运行程序,使用postman访问路由,访问/api/test
时,若是没有参数name,状态码为422,提示Validation Failedgit
而咱们带上参数name,返回结果为咱们请求的参数github
使用postman模拟文件上传,调用/api/upload
接口,上传成功显示upload success,咱们项目中的dist文件夹也多出了上传的文件(dist文件夹须要先建立,否则程序会报错)。web
为了方便咱们调试程序,咱们使用nodemon
启动程序,首先运行yarn run nodemon --dev
,而后在package.json
中添加命令"dev": "nodemon ./server/index.js",以后咱们启动程序只须要运行yarn run dev
便可,若是项目进行了修改,程序自动会自动从新运行。mongodb
咱们使用koa-static
来实现静态资源的访问;生成页面通常会使用koa-views
+相应的模板引擎的方式来实现,可是我准备使用atr-tempate
来生成页面,根据官网的说明咱们使用koa-art-template
便可:数据库
const static = require('koa-static') app.use(static(path.resolve(__dirname, '../dist'))) const render = require('koa-art-template') render(app, { root: path.join(__dirname, 'view'), extname: '.art', debug: process.env.NODE_ENV !== 'production' })
新建页面和样式文件,而且添加路由:
<!-- server/view/index.art --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> </head> <body> <div id="app"> Hello World </div> <link rel="stylesheet" href="style.css"> </body> </html>
/* dist/style.css */ #app { color: red; font-size: 24px; }
// server/routes.js const Router = require('koa-router') const page = new Router() page.get('/index',async ctx => { await ctx.render('index', { title: 'Hello' }) }) module.exports = page.routes()
数据库用于持久化保存数据,服务端的开发每每离不开数据库的使用。MongoDB 是一个基于分布式文件存储的开源数据库系统。在node.js
中咱们能够经过mongoose
操做MongoDB数据库。
首先咱们参考菜鸟教程安装好MongoDB数据库,而后为项目添加mongoose
。
在项目中操做数据库,第一步先链接数据库,新建dbconnect.js文件:
const mongoose = require('mongoose') const db = require('../config/db') mongoose.connect(db.dbname, {useNewUrlParser: true, useUnifiedTopology: true}, err => { if (err) { log.fatal({msg: '[Mongoose] database connect failed!', err}) } else { console.log('[Mongoose] database connect success!') } }) module.exports = mongoose
Mongoose
中全部东西都是从Schema
,Schema
相似于MySQL中的数据结构,Schema
约束了MongoDB中每一个集合的字段结构,限制程序随意修改数据库;Model
是根据Schema
定义的结构编译生成的高级构造函数,Model
的实例被称为Document
,Model
负责从底层MongoDB数据库中建立和读取文档;关于Mongoose
的其余概念,还有Schema
、Model
、Document
三者相关的接口能够查看Mongoose官方文档。
在项目中新建model/User.js
:
const { Schema, model } = require('mongoose') const UserSchema = new Schema({ username: { type: String, require: true, unique: true }, password: { type: String, require: true } }) module.exports = model('User', UserSchema)
在这个文件中,咱们定义了Schema
,而后生成Model
导出使用,要操做数据库,咱们只须要使用Model
相应的方法便可。
Passport是Node.js的身份验证中间件。 Passport极其灵活和模块化,能够绝不费力地放入任何基于Express的Web应用程序中。一套全面的策略支持使用用户名和密码,Facebook,Twitter等进行身份验证。
咱们准备使用jwt进行身份验证,暂时不使用第三方受权登陆,若是想要了解第三方受权登陆或者Passport更多的信息能够阅读官方文档;在项目安装koa-passport
和jwt对应的策略passport-jwt
,新建auth.js
:
const keys = require('../config/keys') const User = require('./model/User') const JwtStrategy = require('passport-jwt').Strategy const ExtractJwt = require('passport-jwt').ExtractJwt const opts = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: keys.secretOrkey } module.exports = passport => { passport.use(new JwtStrategy(opts, (jwt_payload, done) => { User.findById(jwt_payload._id).then(user => { if (user) { done(null, user) } else { done(null, false) } }) })) }
而后在项目中使用:
const passport = require('koa-passport') require('./auth')(passport) app.use(passport.initialize())
新建接口,并使用passport验证身份信息:
api.post('/auth', passport.authenticate('jwt', { session: false }), async ctx => { ctx.body = 'auth' })
结果提示Unauthorized,证实身份验证中间件已经生效。
接下来咱们来实现用户的注册和登陆,完善身份验证的整个流程,注册和登陆咱们会用到bcrypt
和jsonwebtoken
,bcrypt
用于密码的加密和比较,jsonwebtoken
用于生成token;新增login和register接口:
api.post('/login', async ctx => { ctx.verifyParams({ username: { type: "string", required: true }, password: { type: "string", required: true }, }) const { username, password } = ctx.request.body const user = await User.findOne({ username }) if (user && bcrypt.compareSync(password, user.password)) { const token = jwt.sign({ _id: user._id, username }, keys.secretOrkey, { expiresIn: 3600 }) ctx.status = 200 ctx.body = { token: 'Bearer ' + token } } else if (user) { ctx.status = 500 ctx.body = { error: '密码错误' } } else { ctx.status = 500 ctx.body = { error: '用户名不存在' } } }) api.post('/register', async ctx => { const { username, password } = ctx.request.body const users = await User.find({ username }) if (users.length > 0) { ctx.status = 500 ctx.body = { error: '用户名已被占用' } } else { await User.create({ username, password: bcrypt.hashSync(password, keys.salt) }).then(user => { ctx.body = user }).catch(err => { ctx.status = 500 ctx.body = { error: err } }) } })
调用这两个接口获取用户登陆的token,再次访问auth接口,状态码200,正常返回访问信息。
log4js
是Node.js的日志工具,它提供丰富的日志功能,详细的功能的使用能够查看log4js 彻底讲解和官方文档,咱们如今只实现最简单的路由访问日志打印功能:
const log4js = require("log4js"); const logger = log4js.getLogger(); logger.level = "info"; app.use(async (ctx, next) => { const start = new Date() await next() const ms = new Date() - start logger.info(`${ctx.method} ${ctx.url} - ${ms}ms`) })
访问接口控制台会打印简单的访问日志
至此一个简单koa项目的基础功能咱们就实现好了,具体代码实现能够查看项目地址。