koa+mongo+mongoose基礎操作

一、初始化項目

npm init //生成package.json
npm i --save koa //安裝koa
npm i

二、hello world

創建index.js:

const Koa = require('koa');
const app = new Koa();

app.use((ctx) => {

  ctx.body = 'Hello World1122';
})

app.listen(3000);

三、修改後自動重啓

安裝nodemon

npm i --save nodemon

啓動:

nodemon index.js

四、koa-router

安裝:

npm i --save koa-router
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();

// koa-router
router.get('/', (ctx) => {
  ctx.body = '這是主頁';
})

//將中間件插入koa實例
app.use(router.routes()); 

app.listen(3000);

路由前綴:

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 路由前綴
const usersRouter = new Router({prefix: '/users'});

// koa-router
router.get('/', (ctx) => {
  ctx.body = '這是主頁';
})
usersRouter.get('/', (ctx) => {
  ctx.body = '這是用戶';
})
usersRouter.get('/:id', (ctx) => {
  ctx.body = `這是用戶 ${ctx.params.id}`;
})

//將中間件插入koa實例
app.use(router.routes()); 
app.use(usersRouter.routes()); 

app.listen(3000);

 

多中間件:

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 路由前綴
const usersRouter = new Router({prefix: '/users'});

// 多中間件
const auth = async(ctx, next) => {
  if(ctx.url !== '/users') {
    ctx.throw(401);
  }
  await next();
}

// koa-router
router.get('/',auth, (ctx) => {
  ctx.body = '這是主頁';
})
usersRouter.get('/',auth, (ctx) => {
  ctx.body = '這是用戶';
})
usersRouter.get('/:id',auth, (ctx) => {
  ctx.body = `這是用戶 ${ctx.params.id}`;
})

//將中間件插入koa實例
app.use(router.routes()); 
app.use(usersRouter.routes()); 

app.listen(3000);

 

 

五、獲取http請求參數

參數形式:(用url傳遞參數有長度限制和敏感信息不安全等缺點)

1.查詢字符串:?q=keyword 

   獲取:ctx.query

2.路由參數:/users/:id (一般是必選的)

   獲取:ctx.params

3.請求體:{name: '李磊'} (兩種形式:json和form,json:application/json, form:application/x-www-form-urlencoded)

   獲取:ctx.request.body(需安裝中間件koa-bodyparser)

 

六、HTTP響應

1.發送status: ctx.status

2.發送body: ctx.body

3.發送header: ctx.set('Allow','GET,POST');

 

七、合理分化路由和控制器

將上方項目中的路由和控制器放到單獨的文件中:

創建app文件夾:創建routes文件夾和controllers文件夾,分別存放路由和控制器

controllers:

home.js:

class HomeCtl {
    index(ctx) {
        ctx.body = '這是首頁1';
    }
}
module.exports = new HomeCtl();

user.js:

class UserCtl {
    find(ctx) {
        ctx.body = '獲取用戶列表';
    }
    findById(ctx) {
        ctx.body = '獲取指定用戶';
    }
    create(ctx) {
        ctx.body = '新建用戶';
    }
    update(ctx) {
        ctx.body = '修改指定用戶';
    }
    delete(ctx) {
        ctx.body = '刪除指定用戶';
    }
}

module.exports = new UserCtl();

 

routes:

home.js:

const Router = require('koa-router');
const router = new Router();
const { index } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首頁';
// })
router.get('/', index);
module.exports = router;

user.js:

const Router = require('koa-router');
const router = new Router({ prefix: '/user' });
const { find, findById, create, update, delete: del} = require('../controllers/users');

router.get('/', find)
// 獲取用戶
router.get('/:id', findById)
// 新建用戶
router.put('/:id', create)
// 修改用戶
router.put('/:id', update)
// 刪除用戶
router.delete('/:id', del);

module.exports = router;

index.js:

// 此文件功能:在app中批量註冊路由
const fs = require('fs');
module.exports = (app) => {
    fs.readdirSync(__dirname).forEach(file => {
        if(file === 'index.js') { return; }
        const route = require(`./${file}`);
        app.use(route.routes()).use(route.allowedMethods());
    })
}

 

app/index.js:

const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
const routing = require('./routes');

app.use(bodyparser()); //解析請求體

// app中註冊路由
// app.use(router.routes());
// app.use(userRouter.routes());
// app.use(userRouter.allowedMethods()); //允許使用options獲取請求方式
routing(app);

app.listen(3333, () => {console.log('程序啓動')});

 

八、錯誤處理

1.狀態碼

(1)運行時錯誤:500

(2)邏輯錯誤:找不到(404)、先決條件失敗(412)、無法處理的實體(參數格式不對422)等

2.koa自帶的錯誤處理

(1)404

(2)412:ctx.throw(412, '先決條件失敗:id大於等於數組長度');

(3)500

3.自定義koa錯誤處理中間件

const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
const routing = require('./routes');

// 自定義錯誤處理中間件(需寫在所有中間件前)
app.use(async(ctx, next) => {
    try {
        await next();
    } catch(err) {
        ctx.status = err.status || err.statusCode || 500;
        ctx.body = {
            message: err.message
        }
    }
})

app.use(bodyparser()); //解析請求體

// app中註冊路由
// app.use(router.routes());
// app.use(userRouter.routes());
// app.use(userRouter.allowedMethods()); //允許使用options獲取請求方式
routing(app);

app.listen(3333, () => {console.log('程序啓動')});

4.用koa-json-error進行錯誤處理

安裝:npm i --save koa-json-error

在app/index.js中引入,寫在所有中間件前:

const error = require('koa-json-error');
app.use(error());

 

在生產環境中不應該出現信息過多的stack字段,則分別設置分生產和開發環境的報錯返回值:

// koa-json-error處理錯誤
app.use(error({
    // 設置報錯信息中的stack不在生產環境返回,只在測試環境返回
    postFormat: (e, {stack, ...rest}) => process.env.NODE_ENV == 'production' ? rest : {stack, ...rest}
}));

測試:

安裝跨平臺設置環境變量插件: cross-env.  npm i --save -dev cross-env

在package.json中設置環境變量:

{
  ......
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "cross-env NODE_ENV=production nodemon app",
    "dev": "cross-env NODE_ENV=development nodemon app"
  },
  ......
}

 

九、校驗請求參數(koa-parameter)

安裝koa-parameter: cnpm i --save koa-parameter 

 在app/index.js中引入koa-parameter:

const parameter = require('koa-parameter');
// 校驗請求參數
app.use(parameter(app));

在app/controllers/users.js中使用,校驗傳入的參數:

create(ctx) {
        ctx.body = '123';
        // 使用koa-parameter校驗
        ctx.verifyParams({
            name: {type: 'string', require: true}
        })
}

 

 十、數據庫(MongoDB Atlas)

1.註冊並創建集羣:https://cloud.mongodb.com

按提示步驟創建:

2.安裝mongoose:cnpm i --save mongoose

創建文件夾存儲數據庫鏈接:app/config.js:

module.exports = {
    connectionStr: 'mongodb+srv://jixueyuandi:[email protected]/test?retryWrites=true&w=majority'
}

// 真正開發時,要用環境變量等方式獲取密碼,不要把密碼寫在代碼裏

使用mongoose連接雲數據庫,app/index.js:

const {connectionStr} = require('./config.js');
// 連接mongoose數據庫
const mongoose = require('mongoose');
mongoose.connect(connectionStr, () => console.log('數據庫連接成功'));
mongoose.connection.on('error', console.error);

3.實現用戶的增刪改:

(1)創建用戶模式及模型

app中新建models/users.js:

// 此文件用來創建用戶模型
const mongoose = require('mongoose');
const {Schema, model} = mongoose;

const userSchema = new Schema({
    name: {type:String,require:true}
});

module.exports = model('User',userSchema); //model(集合名,模式實例)

 

(2)app中新建controllers/home.js、user.js

home.js:

class HomeCtl {
    index(ctx) {
        ctx.body = '這是首頁1';
    }
}
module.exports = new HomeCtl();

 

user.js:

const User = require('../models/users');

class UserCtl {
    async find(ctx) {
        ctx.body = await User.find();
    }
    async findById(ctx) {
        ctx.body = await User.findById(ctx.params.id);
    }
    async create(ctx) {
        // 使用koa-parameter校驗
        ctx.verifyParams({
            name: {type: 'string', require: true},
            password: {type: 'string', require: true}
        })
        // 用戶名不重複使用
        const {name} = ctx.request.body;
        const repeatedUser = await User.findOne({name});
        if (repeatedUser) {
            ctx.throw(409, '用戶名已被佔用');
        }
        const user = await new User(ctx.request.body).save();
        ctx.body = user;
    }
    async update(ctx) {
        ctx.verifyParams({
            name: {
                type: 'string',
                require: false
            },
            password: {type: 'string', require: false}
        })
        const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body);
        if(!user) {
            ctx.throw(404, '用戶不存在')
        }
        ctx.body = user;
    }
    async delete(ctx) {
        const user = await User.findByIdAndRemove(ctx.params.id);
        if (!user) {
            ctx.throw(404, '用戶不存在')
        }
        ctx.status = 204;
    }
}

module.exports = new UserCtl();

 

(3)app中新建routes/home.js、user.js、index.js

home.js:

const Router = require('koa-router');
const router = new Router();
const { index } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首頁';
// })
router.get('/', index);
module.exports = router;

home.js:

const Router = require('koa-router');
const router = new Router({ prefix: '/users' });
const { find, findById, create, update, delete: del} = require('../controllers/users');

router.get('/', find)
// 獲取用戶
router.get('/:id', findById)
// 新建用戶
router.post('/', create)
// 修改用戶
router.patch('/:id', update)
// 刪除用戶
router.delete('/:id', del);

module.exports = router;

 

index.js:

// 此文件功能:在app中批量註冊路由
const fs = require('fs');
module.exports = (app) => {
    fs.readdirSync(__dirname).forEach(file => {
        if(file === 'index.js') { return; }
        const route = require(`./${file}`);
        app.use(route.routes()).use(route.allowedMethods());
    })
}

 

十一、jwt實現登錄認證

1.jwt實現過程:在用戶登錄時實行jwt簽名,返回token到客戶端,客戶端將token存儲到sessionStorage或localStorage中,在需要用戶驗證的接口請求時實行jwt驗證

2. 安裝jsonwebtoken和koa-jwt

cnpm i --save jsonwebtoken
cnpm i --save koa-jwt

3.實現:

在/config.js中設置簽名(真正開發時不能在這設置,應該在不同環境變量中獲取):

module.exports = {
    secret: 'zhihu-jwt-secret',
    connectionStr: 'mongodb://127.0.0.1:27017/myuser' //'mongodb+srv://jixueyuandi:[email protected]/test?retryWrites=true&w=majority'
}

// 真正開發時,要用環境變量等方式獲取密碼,不要把密碼寫在代碼裏

 

在/app/controllers/user.js中寫登錄接口:

const User = require('../models/users');
const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config.js');

class UserCtl {
......
    // 驗證是否是該用戶
    async checkOwner(ctx, next) {
        console.log(ctx.params)
        if(ctx.params.id !== ctx.state.user._id) {
            ctx.throw(403, '沒有權限');
        }
        await next();
    }

    //登錄控制器
    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 = jsonwebtoken.sign({_id,name}, secret, {expiresIn: '1d'})
        ctx.body = token;
    }
}
module.exports = new UserCtl();

 

在/app/routes/user.js中設置登錄接口 並對需認證的接口添加認證控制器和驗證是否是該用戶控制器:

const Router = require('koa-router');
const router = new Router({ prefix: '/users' });
const { find, findById, create, update, delete: del, login, checkOwner} = require('../controllers/users');

// const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config')
const jwt = require('koa-jwt');

// 方法一:用koa-jwt創建token認證中間件
const auth = jwt({secret:secret})

// 方法二:用jsonwebtoken創建token認證中間件
// const auth = async (ctx, next) => {
//     // 獲取客戶端header傳遞過來的authorization中的token
//     const {authorization=''} = ctx.request.header;
//     const token = authorization.replace('Bearer ', '');
//     // 進行token驗證
//     try {
//         const user = jsonwebtoken.verify(token, sercet);
//         ctx.state.user = user;
//         console.log(ctx.state)

//     } catch(err) {
//         ctx.throw(401, err.message);
//     }
//     await next();
// }

router.get('/', find)
// 獲取用戶
router.get('/:id', findById)
// 新建用戶
router.post('/', create)
// 修改用戶
router.patch('/:id', auth, checkOwner, update)
// 刪除用戶
router.delete('/:id', auth, checkOwner, del);

// 用戶登錄
router.post('/login', login);

module.exports = router;

 

十二、上傳圖片

安裝;koa-body

 cnpm i --save koa-body

在app/index.js中引入:

......
const path = require('path');
const koaBody = require('koa-body');
app.use(koaBody({
    multipart: true, //上傳文件
    formidable: {
        uploadDir: path.join(__dirname, 'public/uploads'), //上傳位置
        keepExtensions: true //保留原後綴名
    }
});

在controllers/home.js中引入:

const path = require('path');
class Home{
    ....
    upload(ctx) {
        const file = ctx.request.files.file;
        //ctx.body = {path: file.path}; //返回上傳的本地文件路徑
        //返回鏈接路徑
        const basename = path.basename(file.path);
        ctx.body = {url:`${ctx.origin}/uploads/${basename}`};
    }
}
module.sxports = new Home();

在router/home.js中寫相對應接口:

const Router = require('koa-router');
const router = new Router();
const { index,upload } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首頁';
// })
router.get('/', index);
router.post('/upload',upload)
module.exports = router;

 

將文件轉爲鏈接:

安裝koa-static

在app/index.js中引入並使用:

const koaStatic = require('koa-static');
app.use(koaStatic(path.join(__dirname, 'public'});

圖片路徑:http://127.0.0.1:3333/uploads/upload_926df733d5384256a5446f95eee64858.png

 

html中提交圖片:在public/uploads中創建upload.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <title>Page Title</title>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
</head>
<body>
  <form enctype="multipart/form-data" action="/upload" method="POST">
    <input type="file" name="file" accept="image/*">
    <button type="submit">提交</button>
  </form>
</body>
</html>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章