koa搭建本地服务器和静态资源访问并创建注册登录和验证token等接口

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}`);
})

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值