方法設(shè)計 PUT和DELETE ngnix可能會有問題
對象
狀態(tài)碼
返回
//推薦 HTTP/1.1 400 Bad Request Content-Type: application/json { "error": "不合法的附件", "detail": { "uname": "用戶名為必填項" } }
解決跨域: npm i koa2-cors
var Koa = require('koa');var cors = require('koa2-cors');var app = new Koa();app.use(cors());
參考文檔:理解resful架構(gòu)、 resful API最佳實踐
配置:./routes/users.js
const upload = require("koa-multer")({ dest: "./public/images" });router.post("/upload", upload.single("file"), ctx => { console.log(ctx.req.file); // 注意數(shù)據(jù)存儲在原始請求中 console.log(ctx.req.body); // 注意數(shù)據(jù)存儲在原始請求中 ctx.body = "上傳成功";});
調(diào)用接口,./public/upload-avatar.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <link rel="stylesheet" /> <style> .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409eff; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center; } .avatar { width: 178px; height: 178px; display: block; } </style> <title>文件上傳</title> </head> <body> <div id="app"> <!-- ajax方式上傳 --> <el-upload class="avatar-uploader" action="/users/upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </div> <script> var app = new Vue({ el: "#app", data() { return { imageUrl: "" }; }, methods: { handleAvatarSuccess(res, file) { this.$message.success('上傳頭像成功') this.imageUrl = URL.createObjectURL(file.raw); }, beforeAvatarUpload(file) { const isJPG = file.type === "image/jpeg"; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error("上傳頭像圖片只能是 JPG 格式!"); } if (!isLt2M) { this.$message.error("上傳頭像圖片大小不能超過 2MB!"); } return isJPG && isLt2M; } } }); </script> </body></html>
配置:app.js
// 為koa上下文擴展一些校驗方法app.use(bouncer.middleware());
基本使用:user.js
router.post("/", ctx => { try { // 校驗開始 ctx .validateBody("uname") .required("要求提供用戶名") .isString() .trim() .isLength(6, 16, "用戶名長度為6~16位"); // ctx.validateBody('email') // ?.optional() // ?.isString() // ?.trim() // ?.isEmail('非法的郵箱格式') ctx .validateBody("pwd1") .required("密碼為必填項") .isString() .isLength(6, 16, "密碼必須為6~16位字符"); ctx .validateBody("pwd2") .required("密碼確認(rèn)為必填項") .isString() .eq(ctx.vals.pwd1, "兩次密碼不一致"); // 校驗數(shù)據(jù)庫是否存在相同值 // ctx.validateBody('uname') // ?.check(await db.findUserByUname(ctx.vals.uname), 'Username taken') ctx.validateBody("uname").check("jerry", "用戶名已存在"); // 如果走到這里校驗通過 // 校驗器會用凈化后的值填充 `ctx.vals` 對象 console.log(ctx.vals); console.log("POST /users"); // const { body: user } = ctx.request; // 請求body const user = ctx.vals; user.id = users.length + 1; users.push(user); ctx.body = { ok: 1 };} catch (error) { if (error instanceof bouncer.ValidationError) { ctx.body = '校驗失?。?+error.message; return; } throw error}});
使用:./routes/api.js
const captcha = require("trek-captcha");router.get("/captcha", async ctx => { const { token, buffer } = await captcha({ size: 4 }); ctx.body = buffer;});
圖片顯示,upload-avatar.html
<!-- 驗證碼 --><img src="/api/captcha" id="captcha" /><script>document.getElementById('captcha').onclick = function() { captcha.src = "/users/captcha?r=" + Date.now(); };</script>
接口編寫,./routes/api.js
router.get("/sms", async function(ctx) { // 生成6位隨機數(shù)字驗證碼 let code = ran(6); // 構(gòu)造參數(shù) const to = ctx.query.to; // 目標(biāo)手機號碼 const accountSid = "3324eab4c1cd456e8cc7246176def24f"; // 賬號id const authToken = "b1c4983e2d8e45b9806aeb0a634d79b1"; // 令牌 const templateid = "613227680"; // 短信內(nèi)容模板id const param = `${code},1`; // 短信參數(shù) const timestamp = moment().format("YYYYMMDDHHmmss"); const sig = md5(accountSid + authToken + timestamp); // 簽名 try { // 發(fā)送post請求 const resp = await axios.post( "https://api.miaodiyun.com/20150822/industrySMS/sendSMS", qs.stringify({ to, accountSid, timestamp, sig, templateid, param }), { headers: { "Content-Type": "application/x-www-form-urlencoded" } } ); if (resp.data.respCode === "00000") { // 短信發(fā)送成功,存儲驗證碼到session,過期時間1分鐘 const expires = moment() .add(1, "minutes") .toDate(); ctx.session.smsCode = { to, code, expires }; ctx.body = {ok:1} } else { ctx.body = {ok:0, message: resp.data.respDesc} }} catch (e) { ctx.body = {ok:0, message: e.message}}});
前端頁面,register.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <link rel="stylesheet" /> <style></style> <title>文件上傳</title> </head> <body> <div id="app"> <el-form :model="regForm" ref="regForm"> <el-form-item> <el-input type="tel" v-model="regForm.phone" autocomplete="off" placeholder="手機號" ></el-input> </el-form-item> <el-form-item> <el-input type="text" v-model="regForm.captcha" autocomplete="off" placeholder="圖形驗證碼" ></el-input> <img :src="captchaSrc" @click="getCaptcha" /> </el-form-item> <el-form-item> <el-input type="text" v-model="regForm.code" autocomplete="off" placeholder="短信驗證碼" ></el-input> <el-button type="primary" @click="getSmsCode()" >獲取短信驗證碼</el-button > </el-form-item> <el-form-item> <el-input type="password" v-model="regForm.password" autocomplete="off" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm()">提交</el-button> </el-form-item> </el-form> </div> <script> var app = new Vue({ el: "#app", data() { return { regForm: { phone: "", captcha: "", code: "", password: "" }, captchaSrc: "/api/captcha" }; }, methods: { getCaptcha() { this.captchaSrc = "/api/captcha?r=" + Date.now(); }, getSmsCode() { axios .get("/api/sms?to=" + this.regForm.phone) .then(res => res.data) .then(({ code }) => (this.regForm.code = code)); }, submitForm() { axios .post("/students", this.regForm) .then(() => alert("注冊成功")) .catch(error => alert("注冊失敗:" + error.response.data.message)); } } }); </script> </body></html>
const Router = require("koa-router");const router = new Router({ prefix: "/students" });const bouncer = require("koa-bouncer");router.post("/", async ctx => {?try {??// 輸入驗證??const { code, to, expires } = ctx.session.smsCode;??ctx??.validateBody("phone")??.required("必須提供手機號")??.isString()??.trim()??.match(/1[3-9]\d{9}/, "手機號不合法")??.eq(to, "請?zhí)顚懡邮斩绦诺氖謾C號");??ctx??.validateBody("code")??.required("必須提供短信驗證碼")??.isString()??.trim()??.isLength(6, 6, "必須是6位驗證碼")??.eq(code, "驗證碼填寫有誤")??.checkPred(() => new Date() - new Date(expires) < 0, "驗證碼已過期");??ctx??.validateBody("password")??.required("必須提供密碼")??.isString()??.trim()??.match(/[a-zA-Z0-9]{6,16}/, "密碼不合法");??// 入庫, 略??ctx.body = { ok: 1 };} catch (error) {??if (error instanceof bouncer.ValidationError) {???console.log(error);???ctx.status = 401;?} else {???ctx.status = 500;?}??ctx.body = { ok: 0, message: error.message };}});module.exports = router;
路由相關(guān)
'koa-bodyparser' 【post解析】
app.use(bodyParser({ extendTypes:\['json','form','text'\] }))
'koa-router' 【路由】
import Router from 'koa-router'; import axios from './utils/axios' import Cart from '../dbs/models/cart' import md5 from 'crypto-js/md5' //加密 let router \= new Router({prefix: '/cart'}) router.post('/getCart', async ctx \=> { let {id} \= ctx.request.body console.log(id); try { let result \= await Cart.findOne({cartNo: id}) ctx.body \= { code: 0, data: result ? result.detail\[0\] : {} } } catch (e) { ctx.body \= { code: -1, data: {} } } })
'koa-multer' 【文件上傳】
router.post("/upload", upload.single("file"), ctx \=> { console.log(ctx.req.file); // 注意數(shù)據(jù)存儲在原始請求中 console.log(ctx.req.body); // 注意數(shù)據(jù)存儲在原始請求中 ctx.body \= "上傳成功"; });
'koa2-cors' 【跨域】
// 跨域 var cors \= require('koa2-cors'); app.use(cors());
'koa-bouncer' 【校驗】
//配置const bouncer \= require("koa-bouncer"); app.use(bouncer.middleware());//使用router.post("/", ctx => { try { // 校驗開始 ctx .validateBody("uname") .required("要求提供用戶名") .isString() .trim() .isLength(6, 16, "用戶名長度為6~16位"); // ctx.validateBody('email') // ?.optional() // ?.isString() // ?.trim() // ?.isEmail('非法的郵箱格式') ctx .validateBody("pwd1") .required("密碼為必填項") .isString() .isLength(6, 16, "密碼必須為6~16位字符"); ctx .validateBody("pwd2") .required("密碼確認(rèn)為必填項") .isString() .eq(ctx.vals.pwd1, "兩次密碼不一致"); // 校驗數(shù)據(jù)庫是否存在相同值 // ctx.validateBody('uname') // ?.check(await db.findUserByUname(ctx.vals.uname), 'Username taken') ctx.validateBody("uname").check("jerry", "用戶名已存在"); // 如果走到這里校驗通過 // 校驗器會用凈化后的值填充 `ctx.vals` 對象 console.log(ctx.vals); console.log("POST /users"); // const { body: user } = ctx.request; // 請求body const user = ctx.vals; user.id = users.length + 1; users.push(user); ctx.body = { ok: 1 };} catch (error) { if (error instanceof bouncer.ValidationError) { ctx.body = '校驗失?。?+error.message; return; } throw error}});
數(shù)據(jù)庫相關(guān)
'koa-generic-session' 【登錄狀態(tài)鑒權(quán)】
app.use(session({key: 'mt', prefix: 'mt:uid', store: new Redis()}))
'koa-session' 【登錄狀態(tài)鑒權(quán)】
app.keys \= \['some secret'\]; //設(shè)置秘鑰// 配置項 const SESS\_CONFIG \= { key: 'kkb:sess', // cookie鍵名 maxAge: 86400000, // 有效期,默認(rèn)一天 httpOnly: true, // 僅服務(wù)器修改 signed: true, // 簽名cookie };// 注冊 ~~~~app.use(session(SESS\_CONFIG, app));
'koa-redis' 【redis】
app.use(session({key: 'mt', prefix: 'mt:uid', store: new Redis()}))
'mongoose' 【面向?qū)ο蟛僮鲾?shù)據(jù)庫】
//連接mongoose.connect(dbConfig.dbs,{ useCreateIndex: true, useNewUrlParser:true })//操作import mongoose from 'mongoose' const Schema \= mongoose.Schema const Cart \= new Schema({ id: { type: String, require: true }, detail: { type: Array, require: true }, cartNo: { type: String, require: true }, user: { type: String, require: true }, time: { type: String, require: true } }) export default mongoose.model('Cart', Cart)
聯(lián)系客服