koa搭建本地服务器
koa搭建本地服务器
1、npm init -y
生成package.json
文件
2、安装koa和koa-router — npm i koa koa-router --save
3、修改入口文件 app.js
并新建 app.js 文件
{
"name": "koa-app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start":"node app.js",
"nodemon":"nodemon app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"koa": "^2.13.1",
"koa-router": "^10.0.0"
}
}
app.js
const Koa = require('koa');
const Router = require('koa-router');
// 实例化koa
const app = new Koa();
const router = new Router();
// 路由 这里的ctx 是koa自带的上下文对象,request和response都存在这里面
router.get('/',async ctx=>{
console.log(ctx); // 打印看下ctx里面有什么内容
ctx.body= {msg:'hello koa'}
});
// 配置路由
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port =process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
使用mlab 数据库
推荐数据库配置 详细教程使用Atlas云MongoDB
项目中安装mongoose —— npm i mongoose --save
新建config文件key.js
key.js
module.exports = {
mongoURL : "mongodb+srv://fqniu:xxxxxx@cluster0.igzbx.mongodb.net/test",
secretKey:"secret"
}
app.js
const Koa = require('koa');
const Router = require('koa-router');
const Mongoose = require('mongoose');
// 实例化koa
const app = new Koa();
const router = new Router();
// 路由
router.get('/',async ctx=>{
ctx.body= {msg:'hello koa'}
});
// config
const db = require('./config/key').mongoURL
// 连接数据库
Mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(()=>{
console.log("mongodb connected ...");
}).catch((err)=>{
console.log(err);
})
// 配置路由
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port =process.env.PORT || 5000;
// 监听端口号
app.listen(port,()=>{
console.log(`server started on ${port}`);
})
新建routes文件 下的api文件 user.js
const Router = require('koa-router');
const router = new Router();
// 引入 User
const User = require("../../models/User")
/**
* @route GET api/users/test
* @desc 测试接口
* @access 接口是公开的
*
*/
router.get("/test", async ctx => {
ctx.status = 200;
ctx.body = {
msg: 'user works...'
};
})
module.exports = router.routes()
新建models文件下的User.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// 实例化数据模板
const UserSchema = new Schema({
name: {
type: String,
require: true
},
email: {
type: String,
require: true
},
password: {
type: String,
require: true
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now
},
})
module.exports = User = mongoose.model("users", UserSchema);
获取前端传递的数据 koa-bodyparser
npm i koa-bodyparser --save
在app.js中使用
const Koa = require('koa');
const Router = require('koa-router');
const Mongoose = require('mongoose');
var bodyParser = require('koa-bodyparser');
// 实例化koa
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// 路由
router.get('/', async ctx => {
ctx.body = {
msg: 'hello koa'
}
});
// 引入user.js
const users = require('./routes/api/use')
// config
const db = require('./config/keys').mongoURL
// 连接数据库
Mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log("mongodb connected ...");
}).catch((err) => {
console.log(err);
})
// 配置路由地址 localhost:5000/api/users
router.use("/api/users", users)
// 配置路由
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
对密码加密 bcryptjs
npm i bcryptjs --save
头像处理 gravatar
npm i gravatar --save
注意必须要有gravatar的email才可以,自行注册
数据库中添加进去的数据
解决数据库中注册接口明文加密问题
新建tools.js文件
const bcrypt = require("bcryptjs");
const tools = {
enbcrypt(password){
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync(password, salt);
return hash
}
}
module.exports = tools
在api文件的user.js使用
// 引入 tools
const tools = require("../../config/tools")
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
注册接口
const Router = require('koa-router');
const router = new Router();
const gravatar = require('gravatar');
// 引入 tools
const tools = require("../../config/tools")
// 引入 User
const User = require("../../models/User")
/**
* @route POST api/users/register
* @desc 注册接口地址
* @access 接口是公开的
*
*/
router.post("/register", async ctx => {
console.log(ctx.request.body);
// 存储到数据库
const findResult = await User.find({
email: ctx.request.body.email
})
console.log(findResult);
if (findResult.length > 0) {
ctx.status = 500;
ctx.body = {
"email": "邮箱已被占用"
};
} else {
// 没查到
const avatar = gravatar.url(ctx.request.body.email, {s: '200', r: 'pg', d: 'mm'}); //pg格式,mm默认头像
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
// console.log(newUser);
// 存储到数据库
await newUser.save().then((user) => {
ctx.body = user;
}).catch((error) => {
console.log(error);
})
// 返回json数据
ctx.body = newUser;
}
})
module.exports = router.routes()
返回token 使用 jsonwebtoken
npm i jsonwebtoken --save
登录接口
const Router = require('koa-router');
const router = new Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
// 引入 tools
const tools = require("../../config/tools")
// 引入 User
const User = require("../../models/User")
// 引入key
const keys = require("../../config/keys")
/**
* @route POST api/users/register
* @desc 注册接口地址
* @access 接口是公开的
*
*/
router.post("/register", async ctx => {
// console.log(ctx.request.body);
// 存储到数据库
const findResult = await User.find({
email: ctx.request.body.email
})
console.log(findResult);
if (findResult.length > 0) {
ctx.status = 500;
ctx.body = {
"email": "邮箱已被占用"
};
} else {
// 没查到
const avatar = gravatar.url(ctx.request.body.email, { s: '200', r: 'pg', d: 'mm' }); //pg格式,mm默认头像
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
// console.log(newUser);
// 存储到数据库
await newUser.save().then((user) => {
ctx.body = user;
}).catch((error) => {
console.log(error);
})
// 返回json数据
ctx.body = newUser;
}
})
/**
* @route POST api/users/login
* @desc 登录接口地址 返回token
* @access 接口是公开的
*
*/
router.post("/login", async ctx => {
// 查询
const findResult = await User.find({ email:ctx.request.body.email });
const user = findResult[0]
const password = ctx.request.body.password;
// 判断是否查到
if(findResult.length == 0){
ctx.status = 404;
ctx.body = {'email':"用户不存在"}
}else{
// 查到后 验证密码
var result = await bcrypt.compareSync(password, user.password);
// 验证通过
if(result) {
// 返回token
const payload = { id:user.id, name:user.name, avatar:user.avatar }
// 签名sign
const token = jwt.sign(payload, keys.secretKey, { expiresIn:3600 })
ctx.status = 200;
ctx.body = { success:true, token:"Bearer "+ token }; //注意这里的格式 Bearer 后面有个空格
} else {
ctx.status = 400,
ctx.body = { password:"密码错误" };
}
}
})
module.exports = router.routes()
验证token 使用 koa-passport
npm i koa-passport --save
注意:登录后验证
在入口app.js中引入使用
const passport = require('koa-passport')
// 注意放置的位置在配置路由上面、数据库下面
app.use(passport.initialize())
app.use(passport.session())
// 回调 config 文件 passport.js
require('./config/passport')(passport)
passport-jwt 和koa-passport 一同使用
在config文件下新建passport.js
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {};
const keys = require('./keys');
//对应User.js中
const mongoose = require('mongoose')
const User = mongoose.model('users')
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretKey;
module.exports = passport => {
// console.log(passport);
passport.use(new JwtStrategy(opts, async function (jwt_payload, done) {
// console.log(jwt_payload);
const user = await User.findById(jwt_payload.id);
if(user){
return done(null, user)
}else{
return done(null, false)
}
}));
}
api文件下user.js
/**
* @route GET api/users/current
* @desc 用户信息地址 返回用户信息
* @access 接口是私密的
*
*/
router.get("/current", passport.authenticate('jwt', { session: false }), async ctx => {
// ctx.body = { success:true }
ctx.body = {
id:ctx.state.user.id,
name:ctx.state.user.name,
email:ctx.state.user.email,
avatar:ctx.state.user.avatar,
}
})
以上 app.js代码如下
const Koa = require('koa');
const Router = require('koa-router');
const Mongoose = require('mongoose');
var bodyParser = require('koa-bodyparser');
const passport = require('koa-passport')
// 实例化koa
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// 路由
router.get('/', async ctx => {
ctx.body = {
msg: 'hello koa'
}
});
// 引入user.js
const users = require('./routes/api/user')
// config
const db = require('./config/keys').mongoURL
// 连接数据库
Mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log("mongodb connected ...");
}).catch((err) => {
console.log(err);
})
app.use(passport.initialize())
app.use(passport.session())
// 回调 config 文件 passport.js
require('./config/passport')(passport)
// 配置路由地址 localhost:5000/api/users
router.use("/api/users", users)
// 配置路由
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
以上 api文件中 user.js 代码如下:
const Router = require('koa-router');
const router = new Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('koa-passport')
// 引入 tools
const tools = require("../../config/tools")
// 引入 User
const User = require("../../models/User")
// 引入key
const keys = require("../../config/keys")
/**
* @route GET api/users/test
* @desc 测试接口
* @access 接口是公开的
*
*/
router.get("/test", async ctx => {
ctx.status = 200;
ctx.body = {
msg: 'user works...'
};
})
/**
* @route POST api/users/register
* @desc 注册接口地址
* @access 接口是公开的
*
*/
router.post("/register", async ctx => {
// console.log(ctx.request.body);
// 存储到数据库
const findResult = await User.find({
email: ctx.request.body.email
})
console.log(findResult);
if (findResult.length > 0) {
ctx.status = 500;
ctx.body = {
"email": "邮箱已被占用"
};
} else {
// 没查到
const avatar = gravatar.url(ctx.request.body.email, { s: '200', r: 'pg', d: 'mm' }); //pg格式,mm默认头像
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
// console.log(newUser);
// 存储到数据库
await newUser.save().then((user) => {
ctx.body = user;
}).catch((error) => {
console.log(error);
})
// 返回json数据
ctx.body = newUser;
}
})
/**
* @route POST api/users/login
* @desc 登录接口地址 返回token
* @access 接口是公开的
*
*/
router.post("/login", async ctx => {
// 查询
const findResult = await User.find({ email:ctx.request.body.email });
const user = findResult[0]
const password = ctx.request.body.password;
// 判断是否查到
if(findResult.length == 0){
ctx.status = 404;
ctx.body = {'email':"用户不存在"}
}else{
// 查到后 验证密码
var result = await bcrypt.compareSync(password, user.password);
// 验证通过
if(result) {
// 返回token
const payload = { id:user.id, name:user.name, avatar:user.avatar }
// 签名sign
const token = jwt.sign(payload, keys.secretKey, { expiresIn:3600 })
ctx.status = 200;
ctx.body = { success:true, token:"Bearer "+ token }; //注意这里的格式 Bearer 后面有个空格
} else {
ctx.status = 400,
ctx.body = { password:"密码错误" };
}
}
})
/**
* @route GET api/users/current
* @desc 用户信息地址 返回用户信息
* @access 接口是私密的
*
*/
router.get("/current", passport.authenticate('jwt', { session: false }), async ctx => {
// ctx.body = { success:true }
ctx.body = {
id:ctx.state.user.id,
name:ctx.state.user.name,
email:ctx.state.user.email,
avatar:ctx.state.user.avatar,
}
})
module.exports = router.routes()
验证有效邮箱和密码 validator
npm i validator --save
新建validation文件下register.js、loign.js、isEmpty.js
register.js
const Validator = require('validator');
const isEmpty = require('./isEmpty')
module.exports = function validationRegIpt(data){
let errors = {};
data.name = !isEmpty(data.name) ? data.name : '';
data.email = !isEmpty(data.email) ? data.email : '';
data.password = !isEmpty(data.password) ? data.password : '';
data.password2 = !isEmpty(data.password2) ? data.password2 : '';
if(!Validator.isLength(data.name, { min:2, max:16 })){
errors.name = "名字的长度不能小于2位且不能大于16位";
}
// 这个isEmpty是Validator的方法
if(Validator.isEmpty(data.name)){
errors.name = "名字不能为空"
}
if(!Validator.isEmail(data.email)){
errors.email= "邮箱不合法"
}
if(Validator.isEmpty(data.email)){
errors.email = "邮箱不能为空"
}
if(Validator.isEmpty(data.password)){
errors.password = "密码不能为空"
}
if(!Validator.isLength(data.password, { min:6, max:16 })){
errors.password = "名字的长度不能小于6位且不能大于16位"
}
if(Validator.isEmpty(data.password2)){
errors.password2 = "密码不能为空"
}
if(!Validator.equals(data.password, data.password2)){
errors.password2 = "两次密码不一致"
}
return {
errors: errors,
// 注意这个isEmpty 是自己写的方法
isValid: isEmpty(errors),
}
}
login.js
const Validator = require('validator');
const isEmpty = require('./isEmpty')
module.exports = function validationLogIpt(data){
let errors = {};
data.name = !isEmpty(data.name) ? data.name : '';
data.email = !isEmpty(data.email) ? data.email : '';
data.password = !isEmpty(data.password) ? data.password : '';
data.password2 = !isEmpty(data.password2) ? data.password2 : '';
if(Validator.isEmpty(data.email)){
errors.email = "邮箱不能为空"
}
if(!Validator.isEmail(data.email)){
errors.email= "邮箱不合法"
}
if(Validator.isEmpty(data.password)){
errors.password = "密码不能为空"
}
return {
errors: errors,
// 注意这个isEmpty 是自己写的方法
isValid: isEmpty(errors),
}
}
isEmpty.js
const isEmpty = value => {
return value == undefined
|| value === null
|| (typeof value == "object") && Object.keys(value).length === 0
|| (typeof value == "string" && value.trim().length === 0 )
}
module.exports = isEmpty
api文件下的user.js
const Router = require('koa-router');
const router = new Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('koa-passport')
// 引入 tools
const tools = require("../../config/tools")
// 引入 User
const User = require("../../models/User")
// 引入key
const keys = require("../../config/keys")
// 引入验证
const validationRegIpt = require('../../validation/register')
const validationLogIpt = require('../../validation/login')
/**
* @route POST api/users/register
* @desc 注册接口地址
* @access 接口是公开的
*
*/
router.post("/register", async ctx => {
// console.log(ctx.request.body);
const { errors, isValid } = validationRegIpt(ctx.request.body);
// 判断是否验证码通过
if(!isValid){
ctx.status = 400;
ctx.body = errors;
return;
}
// 存储到数据库
const findResult = await User.find({
email: ctx.request.body.email
})
console.log(findResult);
if (findResult.length > 0) {
ctx.status = 500;
ctx.body = {
"email": "邮箱已被占用"
};
} else {
// 没查到
const avatar = gravatar.url(ctx.request.body.email, { s: '200', r: 'pg', d: 'mm' }); //pg格式,mm默认头像
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
// console.log(newUser);
// 存储到数据库
await newUser.save().then((user) => {
ctx.body = user;
}).catch((error) => {
console.log(error);
})
// 返回json数据
ctx.body = newUser;
}
})
/**
* @route POST api/users/login
* @desc 登录接口地址 返回token
* @access 接口是公开的
*
*/
router.post("/login", async ctx => {
const { errors, isValid } = validationLogIpt(ctx.request.body);
// 判断是否验证码通过
if(!isValid){
ctx.status = 400;
ctx.body = errors;
return;
}
// 查询
const findResult = await User.find({ email:ctx.request.body.email });
const user = findResult[0]
const password = ctx.request.body.password;
// 判断是否查到
if(findResult.length == 0){
ctx.status = 404;
ctx.body = {'email':"用户不存在"}
}else{
// 查到后 验证密码
var result = await bcrypt.compareSync(password, user.password);
// 验证通过
if(result) {
// 返回token
const payload = { id:user.id, name:user.name, avatar:user.avatar }
// 签名sign
const token = jwt.sign(payload, keys.secretKey, { expiresIn:3600 })
ctx.status = 200;
ctx.body = { success:true, token:"Bearer "+ token }; //注意这里的格式 Bearer 后面有个空格
} else {
ctx.status = 400,
ctx.body = { password:"密码错误" };
}
}
})
/**
* @route GET api/users/current
* @desc 用户信息地址 返回用户信息
* @access 接口是私密的
*
*/
router.get("/current", passport.authenticate('jwt', { session: false }), async ctx => {
// ctx.body = { success:true }
ctx.body = {
id:ctx.state.user.id,
name:ctx.state.user.name,
email:ctx.state.user.email,
avatar:ctx.state.user.avatar,
}
})
module.exports = router.routes()
总结
项目结构如下
package.json安装的包
{
"name": "koa-app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js",
"server": "nodemon app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"gravatar": "^1.8.1",
"jsonwebtoken": "^8.5.1",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-passport": "^4.1.4",
"koa-router": "^10.0.0",
"mongoose": "^5.12.2",
"passport-jwt": "^4.0.0",
"validator": "^13.5.2"
}
}
整理代码如下
app.js
const Koa = require('koa');
const Router = require('koa-router');
const Mongoose = require('mongoose');
var bodyParser = require('koa-bodyparser');
const passport = require('koa-passport')
// 实例化koa
const app = new Koa();
const router = new Router();
app.use(bodyParser());
// 路由
router.get('/', async ctx => {
ctx.body = {
msg: 'hello koa'
}
});
// 引入user.js
const users = require('./routes/api/user')
// config
const db = require('./config/keys').mongoURL
// 连接数据库
Mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log("mongodb connected ...");
}).catch((err) => {
console.log(err);
})
app.use(passport.initialize())
app.use(passport.session())
// 回调 config 文件 passport.js
require('./config/passport')(passport)
// 配置路由地址 localhost:5000/api/users
router.use("/api/users", users)
// 配置路由
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
routes中api中user.js
const Router = require('koa-router');
const router = new Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('koa-passport')
// 引入 tools
const tools = require("../../config/tools")
// 引入 User
const User = require("../../models/User")
// 引入key
const keys = require("../../config/keys")
// 引入验证
const validationRegIpt = require('../../validation/register')
const validationLogIpt = require('../../validation/login')
/**
* @route GET api/users/test
* @desc 测试接口
* @access 接口是公开的
*
*/
router.get("/test", async ctx => {
ctx.status = 200;
ctx.body = {
msg: 'user works...'
};
})
/**
* @route POST api/users/register
* @desc 注册接口地址
* @access 接口是公开的
*
*/
router.post("/register", async ctx => {
// console.log(ctx.request.body);
const { errors, isValid } = validationRegIpt(ctx.request.body);
// 判断是否验证码通过
if(!isValid){
ctx.status = 400;
ctx.body = errors;
return;
}
// 存储到数据库
const findResult = await User.find({
email: ctx.request.body.email
})
console.log(findResult);
if (findResult.length > 0) {
ctx.status = 500;
ctx.body = {
"email": "邮箱已被占用"
};
} else {
// 没查到
const avatar = gravatar.url(ctx.request.body.email, { s: '200', r: 'pg', d: 'mm' }); //pg格式,mm默认头像
const newUser = new User({
name: ctx.request.body.name,
email: ctx.request.body.email,
avatar:avatar,
password: tools.enbcrypt(ctx.request.body.password),
});
// console.log(newUser);
// 存储到数据库
await newUser.save().then((user) => {
ctx.body = user;
}).catch((error) => {
console.log(error);
})
// 返回json数据
ctx.body = newUser;
}
})
/**
* @route POST api/users/login
* @desc 登录接口地址 返回token
* @access 接口是公开的
*
*/
router.post("/login", async ctx => {
const { errors, isValid } = validationLogIpt(ctx.request.body);
// 判断是否验证码通过
if(!isValid){
ctx.status = 400;
ctx.body = errors;
return;
}
// 查询
const findResult = await User.find({ email:ctx.request.body.email });
const user = findResult[0]
const password = ctx.request.body.password;
// 判断是否查到
if(findResult.length == 0){
ctx.status = 404;
ctx.body = {'email':"用户不存在"}
}else{
// 查到后 验证密码
var result = await bcrypt.compareSync(password, user.password);
// 验证通过
if(result) {
// 返回token
const payload = { id:user.id, name:user.name, avatar:user.avatar }
// 签名sign
const token = jwt.sign(payload, keys.secretKey, { expiresIn:3600 })
ctx.status = 200;
ctx.body = { success:true, token:"Bearer "+ token }; //注意这里的格式 Bearer 后面有个空格
} else {
ctx.status = 400,
ctx.body = { password:"密码错误" };
}
}
})
/**
* @route GET api/users/current
* @desc 用户信息地址 返回用户信息
* @access 接口是私密的
*
*/
router.get("/current", passport.authenticate('jwt', { session: false }), async ctx => {
// ctx.body = { success:true }
ctx.body = {
id:ctx.state.user.id,
name:ctx.state.user.name,
email:ctx.state.user.email,
avatar:ctx.state.user.avatar,
}
})
module.exports = router.routes()
models中User.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// 实例化数据模板
const UserSchema = new Schema({
name: {
type: String,
require: true
},
email: {
type: String,
require: true
},
password: {
type: String,
require: true
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now
},
})
module.exports = User = mongoose.model("users", UserSchema);
config中key.js、passport.js、tools.js
key.js
module.exports = {
mongoURL : "mongodb+srv://fqniu:xxxxxx@cluster0.igzbx.mongodb.net/test",
secretKey:"secret"
}
passport.js
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {};
const keys = require('./keys');
//对应User.js中
const mongoose = require('mongoose')
const User = mongoose.model('users')
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretKey;
module.exports = passport => {
// console.log(passport);
passport.use(new JwtStrategy(opts, async function (jwt_payload, done) {
// console.log(jwt_payload);
const user = await User.findById(jwt_payload.id);
if(user){
return done(null, user)
}else{
return done(null, false)
}
}));
}
tools.js
const bcrypt = require("bcryptjs");
const tools = {
enbcrypt(password){
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync(password, salt);
return hash
}
}
module.exports = tools
validation中login.js、register.js、isEmpty.js
login.js
const Validator = require('validator');
const isEmpty = require('./isEmpty')
module.exports = function validationLogIpt(data){
let errors = {};
data.name = !isEmpty(data.name) ? data.name : '';
data.email = !isEmpty(data.email) ? data.email : '';
data.password = !isEmpty(data.password) ? data.password : '';
data.password2 = !isEmpty(data.password2) ? data.password2 : '';
if(Validator.isEmpty(data.email)){
errors.email = "邮箱不能为空"
}
if(!Validator.isEmail(data.email)){
errors.email= "邮箱不合法"
}
if(Validator.isEmpty(data.password)){
errors.password = "密码不能为空"
}
return {
errors: errors,
// 注意这个isEmpty 是自己写的方法
isValid: isEmpty(errors),
}
}
register.js
const Validator = require('validator');
const isEmpty = require('./isEmpty')
module.exports = function validationRegIpt(data){
let errors = {};
data.name = !isEmpty(data.name) ? data.name : '';
data.email = !isEmpty(data.email) ? data.email : '';
data.password = !isEmpty(data.password) ? data.password : '';
data.password2 = !isEmpty(data.password2) ? data.password2 : '';
if(!Validator.isLength(data.name, { min:2, max:16 })){
errors.name = "名字的长度不能小于2位且不能大于16位";
}
// 这个isEmpty是Validator的方法
if(Validator.isEmpty(data.name)){
errors.name = "名字不能为空"
}
if(!Validator.isEmail(data.email)){
errors.email= "邮箱不合法"
}
if(Validator.isEmpty(data.email)){
errors.email = "邮箱不能为空"
}
if(Validator.isEmpty(data.password)){
errors.password = "密码不能为空"
}
if(!Validator.isLength(data.password, { min:6, max:16 })){
errors.password = "名字的长度不能小于6位且不能大于16位"
}
if(Validator.isEmpty(data.password2)){
errors.password2 = "密码不能为空"
}
if(!Validator.equals(data.password, data.password2)){
errors.password2 = "两次密码不一致"
}
return {
errors: errors,
// 注意这个isEmpty 是自己写的方法
isValid: isEmpty(errors),
}
}
isEmpty.js
const isEmpty = value => {
return value == undefined
|| value === null
|| (typeof value == "object") && Object.keys(value).length === 0
|| (typeof value == "string" && value.trim().length === 0 )
}
module.exports = isEmpty
接口成功如下图
登录
注册
token验证
补充讲解
静态资源的访问
1、安装 koa-static —— npm i koa-static --save
2、新建 static 文件夹 中img或者js或者css 用于测试
3、app.js中
// 静态资源
const path = require('path');
const static = require('koa-static');
// 实例化koa
const app = new Koa();
// 静态路径 只允许访问这个路径下的 url路径直接访问即可,不用加 static
// 例如 http://localhost:5000/css/index.css 、http://localhost:5000/img/生活.jpg
// 注:如果想加 可以 这样写const staticPath = './' 去掉static , 注意需要在路径中加上static
// 例如 http://localhost:5000/static/css/index.css 、http://localhost:5000/static/img/生活.jpg
const staticPath = './static';
app.use(static(path.join(__dirname, staticPath)));
// 测试
app.use(async ctx => {
ctx.body = "hello koa";
});
讲解get和post请求
get请求
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;
//从上下文中直接获取
let ctx_query = ctx.query;
let ctx_querystring = ctx.querystring;
ctx.body={
url,
req_query,
req_querystring,
// ctx_query,
// ctx_querystring
}
});
// 端口号
const port = process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
http://127.0.0.1:5000?id=7528&name=fqniu&age=25 来进行访问。可以在网页中可以得到一串JSON字符串,这是不是很想后端传给我们的接口。
总结: 获得GET请求的方式有两种,如下:
- 第一种是从request中获得;
- 第二种是一直从上下文中获得;
- 获得的格式也有两种:query 和 querystring。
post请求
获取Post请求的步骤:
1、解析上下文ctx中的原生 node.js 对象 req
2、将POST表单数据解析成 query string - 字符串 (例如:user=fqniu&age=25)
3、将字符串转换成JSON格式
ctx.request和ctx.req的区别:
1、ctx.request
:是Koa2中context经过封装的请求对象,它用起来更直观和简单。
2、ctx.req
:是context提供的node.js原生HTTP请求对象。这个虽然不那么直观,但是可以得到更多的内容。
Koa2中提供了ctx.method属性,可以轻松的得到请求的类型,然后根据请求类型编写不同的相应方法,根据请求类型获得不同的页面内容。GET请求时得到表单填写页面,POST请求时,得到POST处理页面。
app.use(async(ctx)=>{
//当请求时GET请求时,显示表单让用户填写
if(ctx.url==='/' && ctx.method === 'GET'){
let html =`
<h1>Koa request post</h1>
<form method="POST" action="/">
<p>userName</p>
<input name="userName" /> <br/>
<p>password</p>
<input name="password" /> <br/>
<div>
<button type="submit">submit</button>
</div>
</form>
`;
ctx.body = html;
//当请求时POST请求时
}else if(ctx.url==='/' && ctx.method === 'POST'){
ctx.body = '接收到POST请求';
}else{
//其它请求显示404页面
ctx.body = '<h1>404页面</h1>';
}
})
post请求全部代码如下
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
//当请求时GET请求时,显示表单让用户填写
if(ctx.url==='/' && ctx.method === 'GET'){
let html =`
<h1>Koa request post</h1>
<form method="POST" action="/">
<p>userName</p>
<input name="userName" /> <br/>
<p>password</p>
<input name="password" /> <br/>
<div>
<button type="submit">submit</button>
</div>
</form>
`;
ctx.body = html;
//当请求时POST请求时
}else if(ctx.url==='/' && ctx.method === 'POST'){
let pastData = await parsePostData(ctx);
ctx.body = pastData;
}else{
//其它请求显示404页面
ctx.body = '<h1>404页面</h1>';
}
})
function parsePostData(ctx){
return new Promise((resolve,reject) => {
try{
let postdata="";
ctx.req.on('data',(data) => {
postdata += data
})
ctx.req.addListener("end", function(){
let parseData = parseQueryStr(postdata)
resolve(parseData);
})
}catch(error){
reject(error);
}
});
}
function parseQueryStr(queryStr){
let queryData = {};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for( let [index,queryStr] of queryStrList.entries() ){
let itemList = queryStr.split('=');
console.log(itemList);
// decodeURIComponent 处理 "//" 这种不是乱码
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
}
return queryData
}
// 端口号
const port = process.env.PORT || 5000;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
对于POST请求的处理,koa-bodyparser
中间件可以把koa2上下文的formData数据解析到ctx.request.body中。可以直接使用这个中间件来处理post请求参数;
安装中间件 使用npm进行安装,
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 =`
<h1>Koa request post</h1>
<form method="POST" action="/">
<p>userName</p>
<input name="userName" /> <br/>
<p>password</p>
<input name="password" /> <br/>
<div>
<button type="submit">submit</button>
</div>
</form>
`;
ctx.body = html;
//当请求时POST请求时
}else if(ctx.url==='/' && ctx.method === 'POST'){
let postData = ctx.request.body;
ctx.body = postData;
}else{
//其它请求显示404页面
ctx.body = '<h1>404页面</h1>';
}
})
路由实现
const Koa = require('koa');
const fs = require('fs');
const app = new Koa();
function render(page) {
return new Promise((resolve, reject) => {
// 页面路径是相对路径
let pageUrl = `../page/${page}`;
fs.readFile(pageUrl, "binary", (err, data) => {
console.log('data');
if (err) {
reject(err)
} else {
resolve(data);
}
})
})
}
async function route(url){
let page = '404.html';
switch(url){
case '/':
page ='index.html';
break;
case '/index':
page ='index.html';
break;
case '/mine':
page = 'mine.html';
break;
case '/404':
page = '404.html';
break;
default:
break;
}
let html = await render(page);
return html;
}
app.use(async(ctx)=>{
let url = ctx.request.url;
let html = await route(url);
ctx.body = html;
})
// 端口号
const port = process.env.PORT || 5003;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
对于路由 使用中间件 koa-router
来完成路由的配置,koa的中间件生态环境足够强大,路由的中间件不只一种,通过koa-router学习更了解router机制。
安装koa-router中间件 : npm install --save koa-router
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";
});
// 配置路由 allowedMethods
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5003;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
设置前缀
有时候想把所有的路径前面都再加入一个级别,比如原来我们访问的路径是http://127.0.0.1:5003/index
,现在我们希望在所有的路径前面都加上一个 api 层级,把路径变成http://127.0.0.1:5003/api/index
,这时候就可以使用层级来完成这个功能。路由在创建的时候是可以指定一个前缀的,这个前缀会被至于路由的最顶层,也就是说,这个路由的所有请求都是相对于这个前缀的。
const router = new Router({
prefix:'/api'
})
写上这句代码,这时候你的访问路径就加了一个层级api。可以在浏览器中输入http://127.0.0.1:5003/api/index
测试一下。
const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router');
// http://localhost:5004/home/one
let home = new Router();
home.get('/one',async(ctx)=>{
ctx.body="Home One";
}).get('/two',async(ctx)=>{
ctx.body ='Home Two';
})
// http://localhost:5004/page/one
let page = new Router();
page.get('/one',async(ctx)=>{
ctx.body="Page One";
}).get('/two',async(ctx)=>{
ctx.body ='Page Two';
})
// 装载所有子路由
let router = new Router();
router.use('/home', home.routes(), home.allowedMethods());
router.use('/page', page.routes(), page.allowedMethods());
// 端口号
const port = process.env.PORT || 5004;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
路由的传递参数
get请求
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/', function (ctx, next) {
ctx.body = ctx.query;
});
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5005;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
post请求
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.post('/', function (ctx, next) {
ctx.body = ctx.request.body;
});
app.use(router.routes()).use(router.allowedMethods());
// 端口号
const port = process.env.PORT || 5005;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
cookie的使用
例如登录时 十天免登陆 这种就是使用cookie来实现的,保留有效性;
写入Cookie操作:
基本的写入cookie。
const Koa = require('koa');
const app = new Koa();
app.use(async(ctx)=>{
if(ctx.url=== '/index'){
ctx.cookies.set(
'MyName','fqniu'
);
ctx.body = 'cookie is ok';
}else{
ctx.body = 'hello world'
}
});
// 端口号
const port = process.env.PORT || 5006;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
Cookie选项
比如要存储用户名,保留用户登录状态时,可以选择7天内不用登录,也可以选择30天内不用登录。这就需要在写入是配置一些选项:
- 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', 'fqniu', {
domain: '127.0.0.1', // 写cookie所在的域名
path: '/index', // 写cookie所在的路径
maxAge: 1000 * 60 * 60 * 24, // cookie有效时长
expires: new Date('2018-12-31'), // cookie失效时间
httpOnly: false, // 是否只用于http请求中获取
overwrite: false // 是否允许重写
}
);
ctx.body = 'cookie is ok';
} else {
ctx.body = 'hello world'
}
});
// 端口号
const port = process.env.PORT || 5006;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
读取Cookie:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
if (ctx.url === '/index') {
ctx.cookies.set(
'MyName', 'fqniu', {
domain: '127.0.0.1', // 写cookie所在的域名
path: '/index', // 写cookie所在的路径
maxAge: 1000 * 60 * 60 * 24, // cookie有效时长
expires: new Date('2021-04-06'), // 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';
}
}
});
// 端口号
const port = process.env.PORT || 5006;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})
koa的模板引擎ejs
开发中不可能把所有的html代码全部卸载JS里,这显然不现实,也没办法完成大型web开发。必须借用模板机制来帮助开发;
在koa2中使用模板机制必须依靠中间件,这里选择koa-views中间件;
npm安装koa-views npm install --save koa-views
npm安装ejs npm install --save ejs
新建view文件下index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<p>EJS Welcome to <%= title %></p>
</body>
</html>
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 World'
await ctx.render('index', {
title
})
});
// 端口号
const port = process.env.PORT || 5009;
// 监听端口号
app.listen(port, () => {
console.log(`server started on ${port}`);
})