zoukankan      html  css  js  c++  java
  • 使用koa+mongodb构建的仿知乎接口(一)

    之前一直使用koa和express构建过一些小的应用,但是都是没有放到线上去跑。这回,我的想法是把自己那台学生服务器拿来充分利用一下,话不多说,直接直奔主题吧。

    使用的技术栈:

    • nodejs
    • koa2(网络编程框架)
    • mongodb(非关系型数据库)
    • jwt(用于鉴权)
    • pm2(用于跑启动脚本)

    何为REST?何为restful api?

    表现层状态转换(英语:Representational State Transfer,缩写:REST)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种万维网软件架构风格

    restful api:则是符合REST风格的api

    koa 洋葱模型

    app.use(async (ctx, next) => {
      console.log(1)
      await next()
      ctx.body = '1'
    })
    
    app.use(async (ctx, next) => {
      console.log(2)
      await next()
      console.log(3) 
    })
    
    app.use(async (ctx, next) => {
      console.log(4)
    })
    
    // 1 2 4 3 
    

    如何在写一个koa中间件

    const auth = async (ctx,next) => {
      try {
        const {authorization=''} = ctx.request.header
        const token = authorization.replace('Bearer ', '')
        console.log('token',token)
        const user = jsonwebtoken.verify(token, scret)
        ctx.state.user = user
      } catch (error) {
        ctx.throw(401, error.message)
      }
      await next()
    }
    

    开始搭建目录结构

    1. 起一个简单的服务
    const Koa = require('koa')
    const app = new Koa()
    
    const port = 3000 || process.env.port
    app.listen(port, () => {
      console.log(`App is listen on ${port}`)
    })
    
    1. 搭建路由,编写自动读取路由中间件
    // routing
    const fs = require('fs')
    
    module.exports = app => {
      fs.readdirSync(__dirname).forEach(file=> {
        if (file === 'index.js') {return}
        const router = require(`./${file}`)
        app.use(router.routes()).use(router.allowedMethods())
      })
    }
    
    const routing = require('./routes/index')
    routing(app)
    
    1. 解决post请求ctx.request.body为undefined问题
    const KoaBodyPareser = require('koa-bodyparser')
    app.use(KoaBodyPareser())
    
    1. 连接数据库
    const mongoose = require('mongoose')
    const connectionStr = require('./config').connectionStr
    
    // 连接数据库
    mongoose.connect(connectionStr, {
      useNewUrlParser: true,
      useUnifiedTopology: true
    }, () => {
      console.log('数据库连接成功')
    })
    .catch(err => {
      console.log(err)
    })
    
    mongoose.connection.on(error, console.error)
    
    1. 错误处理
    const error = require('koa-json-error')
    app.use(error({
      postFormat: ({stack, ...rest}) => process.env.NODE_ENV === 'production' ? rest : {stack, ...rest}
    }))
    
    1. 参数格式校验
    app.use(parameter(app))
    const parameter = require('koa-parameter')
    

    实现用户接口的增删改查

    1. 定义用户的数据层model
    const mongoose = require('mongoose')
    const {Schema, model} = mongoose
    
    const UserSchema = new Schema({
      name: {
        type: String,
        required: true
      },
      password:{
        type: String,
        required: true,
        select: false
      },
      __v: {
        type: Number,
        select: false
      }
    })
    
    module.exports = model('User', UserSchema)
    
    1. 用户的路由层router
    const KoaRouter = require('koa-router')
    const jwt = require('koa-jwt')
    
    const router = new KoaRouter({prefix: '/users'})
    const {find, findById, create, update, del, login, checkOwer} = require('../controller/users')
    const secret = require('../config').secret
    
    const auth = jwt({secret})
    
    // 获取所有用户
    router.get('/', find)
    // 新建用户
    router.post('/', create)
    // 获取特定用户
    router.get('/:id', findById)
    // 更新用户
    router.patch('/:id',auth, checkOwer, update)
    // 删除用户
    router.delete('/:id',auth, checkOwer, del)
    // 登录
    router.post('/login', login)
    
    module.exports = router
    
    1. 用户的控制器controller
    const jsonwebtoken = require('jsonwebtoken')
    const User = require('../models/users')
    const secret = require('../config').secret
    
    class UsersCtl {
      async find(ctx) {
        ctx.body = await User.find()
      }
      async findById(ctx) {
        const user = await User.findById(ctx.params.id)
        if (!user) {ctx.throw(404, '用户不存在')}
        ctx.body = user
      }
      async create(ctx) {
        ctx.status = 200
        ctx.verifyParams({
          name: {type: 'string', required: true},
          password: {type: 'string', required: true}
        })
        const {name} = ctx.request.body
        const repeatUser = await User.findOne({name})
        if (repeatUser) { ctx.throw(409, '用户已被占用') }
        const user = await User(ctx.request.body).save()
        ctx.body = user
      }
      async update(ctx) {
        ctx.verifyParams({
          name: {type: 'string', required: false},
          password: {type: 'string', required: false}
        })
        const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body)
        if (!user) {ctx.throw(404, '用户不存在')}
        ctx.body = user
      }
      async del(ctx) {
        const user = await User.findByIdAndRemove(ctx.params.id)
        if (!user) {ctx.throw(404, '用户不存在')}
        ctx.body = user
        ctx.status = 204
      }
      async login(ctx) {
        ctx.verifyParams({
          name: {type: 'string', required: true},
          password: {type: 'string', required: true}
        })
        const user = await User.findOne(ctx.request.body)
        if(!user) {ctx.throw(401, '用户名或者密码不正确')}
        const {_id, name} = user
        const token = await jsonwebtoken.sign({_id, name}, secret, {expiresIn: '1d'})
        ctx.body = {
          token
        }
      }
      async checkOwer(ctx, next) {
        if (ctx.params.id !== ctx.state.user._id) {
          ctx.throw(403, '用户没有权限')
        }
        await next()
      }
    }
    
    module.exports = new UsersCtl()
    

    postman的使用

    1. 新建collection

    2. 在collection中新建request

    3. 在登录接口中的Test中设置token为全局变量

    let jsonData = pm.response.json()
    pm.globals.set("token", jsonData.token);
    
    1. 在其他需要验证的接口中Authization使用token

    这就是项目大构建和用户接口的实现了,好记性不如烂笔头,特此总结, 下次将是图片上传几张图片的实践。

  • 相关阅读:
    解惑如何保证数组元素的可见性(yet)
    为什么内存锁在有事务切面的情况下会形同虚设 隔离级别与事务
    小事故合集
    第4种打整包插件,urlfactory already set
    三目运算符与字节码阅读
    servlet application/x-www-form-urlencoded 坑
    多实例重复发邮件
    当动态代理遇到ioc (五)使用cglib切面与自定义类加载器构建独有环境aop日志
    mac笔记本如何重制secureCRT的体验期|试用版本
    npm install出现的错误
  • 原文地址:https://www.cnblogs.com/Jomsou/p/12495262.html
Copyright © 2011-2022 走看看