第四章 入?yún)⒑万炞C入?yún)?/h2>
之前我們寫了一個接口,把所有的書本數(shù)據(jù)都提供給了api,現(xiàn)在我們再寫一個接口,通過豐富之前的代碼,用于搜索書本。
搜索功能,我們知道,大約是在輸入框中填入某些內(nèi)容,然后接口返回與內(nèi)容相關(guān)的數(shù)據(jù),那么這里就涉及到一個內(nèi)容,就是通過api請求傳入服務端的內(nèi)容,我們稱之為入?yún)?。入?yún)⒃谶M入服務端之前,我們需要驗證它們的格式是否符合我們設定的要求,不符合我們要求的入?yún)⒁?guī)則的請求我們就可以直接返回報錯,不需要處理它們了。
那么驗證入?yún)⑹欠窈细裎覀兙托枰玫揭粋€新的包:Joi
npm install joi --save
tips: Joi 文檔
src/router.js
const Router = require('koa-router');
const router = new Router();
// 這是一個用于驗證入?yún)⑹欠裾_的包
const Joi = require('joi');
// 創(chuàng)建一個函數(shù)用于驗證入?yún)⑹欠裾_
const { validatorInterceptor } = require('./lib');
const { BookController } = require('./controllers/book-controller');
router.get('/', ctx => ctx.body = 'Web API Running Successfully.');
router.get('/api/books', BookController.get);
// 用于驗證入?yún)?const bookSearchSchema = {
query: Joi.object().keys({
// 這里要求入?yún)⒖梢杂忻麨閟earch_text的參數(shù),且必須是字符串
text: Joi.string(),
}),
};
// 第一個參數(shù)依舊是路由地址,第二個參數(shù)我們驗證入?yún)⑹欠窈细竦暮瘮?shù)放在這里,第三個參數(shù)是controller執(zhí)行層
router.get('/api/book/search', validatorInterceptor(bookSearchSchema), BookController.get);
module.exports = router;
上面我們創(chuàng)建了一個用于驗證入?yún)⑹欠窈细竦暮瘮?shù),由于這個函數(shù)為公用方法,那么我們創(chuàng)建一個lib文件夾,并把它寫在index.js里面
src/index.js
// 這是一個在node中非常常用且好用的包,用于處理各種數(shù)據(jù)(如json,數(shù)組等)重組,使用npm install lodash安裝
const _ = require('lodash');
// 這個函數(shù)同樣要使用到Joi,所以依然引入它
const Joi = require('joi');
exports.validatorInterceptor = async (schemas, options) => {
// 這里我們給validatorInterceptor返回一個函數(shù)
return async (ctx, next) => {
try {
// 將傳入的schemas的key全部取出,并循環(huán)它們
for (const key of _.keys(schemas)) {
// 使用validate方法驗證每一個key是否符合要求
await Joi.validate(_.get(ctx.request, key), _.get(schemas, key), options || {});
}
}
// 如果不符合要求,則直接報400錯誤
catch (err) {
ctx.throw(400, `validation failed on: ${err.message}`);
}
// 如果沒有問題,則下一步
await next();
};
}
tips: lodash 文檔
剛才可以發(fā)現(xiàn),搜索api我們使用的還是get方法,那么現(xiàn)在我們就來改裝一下這個方法
src/controllers/book-controller.js
const { BookService } = require('../services/book-service');
class BookController {
static async get(ctx) {
// 我們將入?yún)腸tx.request.query中取出
const searchText = ctx.request.query.text;
const bookService = new BookService();
// 傳入service方法中使用
const res = await bookService.get(searchText);
ctx.body = res;
}
}
module.exports = { BookController };
接著修改service方法
src/services/book-services.js
const bookModel = require('../schemas/book-schema');
class BookService {
// 這個函數(shù)的入?yún)閯偛與ontroller層傳入的搜索內(nèi)容
async get(searchText) {
// 我們設置一個默認的內(nèi)容,就是當接口什么搜索內(nèi)容都沒有傳入的時候,我們默認返回所有書本給接口
const query = {};
// 當有搜索內(nèi)容傳入的時候
if (searchText) {
// 我們希望數(shù)據(jù)庫無論是書本名稱還是作者,有相關(guān)的文字內(nèi)容就可以將其搜索出來
// 這里的$or和后面的內(nèi)容寫法都是mongo搜索時的寫法,有不了解的同學可以查看mongo相關(guān)文檔學習
query.$or = [{name: new RegExp(searchText)}, {author: new RegExp(searchText)}];
}
// 和之前一樣,查詢數(shù)據(jù),然后將結(jié)果返回
const res = await bookModel.find(query);
return res;
}
}
module.exports = { BookService };
接下來,啟動程序,開始驗證我們的代碼編寫成果
node src/server.js
打開瀏覽器
首先無任何參數(shù),我們希望返回所有的書本數(shù)據(jù):http://localhost:3000/api/book/search
當參數(shù)錯誤時:http://localhost:3000/api/book/search?aaa=111
搜索書本名稱,三體:http://localhost:3000/api/book/search?text=三體
搜索作者名稱,村上春樹:http://localhost:3000/api/book/search?text=村上春樹
搜索不完全的書本名稱,挪威:http://localhost:3000/api/book/search?text=挪威
全部成功,我們的搜索接口,就是這么簡單 __
本文已完成電子書《Node零基礎(chǔ)入門到服務端程序》電子書(含教程內(nèi)項目代碼)/ 10元,購買鏈接:https://mianbaoduo.com/o/bread/mbd-Z5WZk5o=
ps:前九章(本書共計十三章)內(nèi)容會在這里陸續(xù)更新。