測試用例git地址(node.js部分):https://github.com/wuyongxian20/node-api.gitjavascript
項目架構以下:vue
controllers:java
文件夾下爲接口文件node
logs:mysql
log4js 日誌打印文件ios
mongodb:git
mongodb數據庫配置及幫助類的封裝github
mysql:sql
mysql數據庫配置及數據庫的封裝mongodb
utils:
幫助類
app.js:
node.js啓動文件
controller.js:
controller接口封裝調用
相互關係:
>package.json 項目依賴
>mongodb和mysql爲基本的數據庫配置和幫助類
>controllers文件夾下的接口引用mongodb和mysql操做數據庫
>controller.js是對controllers文件夾的引用和設置api路由跳轉
>app.js 加載controller.js文件,即加載全部的controllers文件夾下的接口
VUE+Node.js接口調用步驟
MySql數據庫準備
>mysql 添加測試數據庫 rehab(數據庫名只須要和mysql\config\index.js中配置文件的數據庫名對應起來就行)
>添加測試表test{id:int,name:varchar}
Node.js接口部分(示例中不考慮mongodb數據庫使用部分)
一、建立node.js項目,向package.json中寫入項目須要的依賴文件,依次是
{ "name": "node-demo", "version": "1.0.0", "description": "node demo", "main": "app.js", "scripts": { "start": "supervisor app.js" }, "keywords": [ "koa", "async" ], "author": "Michael Liao", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/michaelliao/learn-javascript.git" }, "dependencies": { "koa": "^2.8.1", "koa-bodyparser": "^4.2.1", "koa-router": "^7.4.0", "mongodb": "^3.3.2", "mysql": "^2.17.1", "q": "^1.5.1", "log4js": "^5.2.0" } }
koa:koa框架
koa-bodyparser:解析表單數據,並放入到ctx.request.body
koa-router:路由
mongod:node的mongodb數據庫中間件
mysql:node的mysql數據庫中間件
q:Promise處理模塊
log4js:日誌文件
二、controller.js ,設置GET\POST\PUT\DELETE四種接口的路由,並把controllers文件夾下的全部js文件加載進來
const fs = require('fs'); // add url-route in /controllers: function addMapping(router, mapping) { for (var url in mapping) { if (url.startsWith('GET ')) { var path = url.substring(4); router.get(path, mapping[url]); // console.log(`register URL mapping: GET ${path}`); } else if (url.startsWith('POST ')) { var path = url.substring(5); router.post(path, mapping[url]); // console.log(`register URL mapping: POST ${path}`); } else if (url.startsWith('PUT ')) { var path = url.substring(4); router.put(path, mapping[url]); console.log(`register URL mapping: PUT ${path}`); } else if (url.startsWith('DELETE ')) { var path = url.substring(7); router.del(path, mapping[url]); // console.log(`register URL mapping: DELETE ${path}`); } else { console.log(`invalid URL: ${url}`); } } } function addControllers(router, dirs) { for(let i=0;i<dirs.length;i++){ fs.readdirSync(__dirname + '/' + dirs[i]).filter((f) => { return f.endsWith('.js'); }).forEach((f) => { // console.log(`process controller: ${f}...`); let mapping = require(__dirname + '/' + dirs[i] + '/' + f); addMapping(router, mapping); }); } } module.exports = function (dir) { let controllers_dir = dir || 'controllers', router = require('koa-router')(); debugger let controllers=['controllers','controllers/admin'] addControllers(router, controllers); // addControllers(router, 'controllers/admin'); return router.routes(); }; // function addControllers(router, dir) { // fs.readdirSync(__dirname + '/' + dir).filter((f) => { // return f.endsWith('.js'); // }).forEach((f) => { // console.log(`process controller: ${f}...`); // let mapping = require(__dirname + '/' + dir + '/' + f); // addMapping(router, mapping); // }); // } // module.exports = function (dir) { // let // controllers_dir = dir || 'controllers', // router = require('koa-router')(); // debugger // addControllers(router, controllers_dir); // // addControllers(router, 'controllers/admin'); // return router.routes(); // };
三、app.js
const Koa = require('koa'); const bodyParser = require('koa-bodyparser'); const controller = require('./controller'); const app = new Koa(); //log4js 日誌配置 var log4js = require('log4js'); log4js.configure('./config/log4js.json'); // log request URL: app.use(async (ctx, next) => { //添加容許請求頭 ctx.append('Access-Control-Allow-Origin', '*') ctx.append('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild') // ctx.append('Content-Type', 'application/json;charset=utf-8') ctx.append('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS,PATCH') console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); //http 預請求處理(post/put/delete 請求在正式請求以前會先發送一個OPTIONS的預請求,只須要把這個OPTIONS的預請求正常返回,後續的請求就會正常執行) if (ctx.request.method === 'OPTIONS') { ctx.body = "OK" } else { //繼續執行api請求 await next(); } }); // parse request body: app.use(bodyParser()); // add controller: app.use(controller()); app.listen(3000); console.log('app started at port 3000...');
app.js作的事情是
>加載koa框架
>加載form參數解析的中間件koa-bodyparser
>加載log4js日誌配置文件
{ "appenders": { "console": { "type": "console" }, "trace": { "type": "dateFile", "filename": "./logs/access-", "pattern": ".yyyy-MM-dd.log", "alwaysIncludePattern": true, "maxLogSize ": 31457280 }, "http": { "type": "logLevelFilter", "appender": "trace", "level": "trace", "maxLevel": "trace" }, "info": { "type": "dateFile", "filename": "./logs/info-", "encoding": "utf-8", "pattern": ".yyyy-MM-dd.log", "maxLogSize": 10000000, "alwaysIncludePattern": true, "layout": { "type": "pattern", "pattern": "[%d{ISO8601}][%5p %z %c] %m" }, "compress": true }, "maxInfo": { "type": "logLevelFilter", "appender": "info", "level": "debug", "maxLevel": "error" }, "error": { "type": "dateFile", "filename": "./logs/error-", "pattern": ".yyyy-MM-dd.log", "maxLogSize": 10000000, "encoding": "utf-8", "alwaysIncludePattern": true, "layout": { "type": "pattern", "pattern": "[%d{ISO8601}][%5p %z %c] %m" }, "compress": true }, "minError": { "type": "logLevelFilter", "appender": "error", "level": "error" } }, "categories": { "default": { "appenders": [ "console", "http", "maxInfo", "minError" ], "level": "all" } } }
>加載api路由 controller.js
>請求url處理
app.use(async (ctx, next) => { //添加容許請求頭 ctx.append('Access-Control-Allow-Origin', '*') ctx.append('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild') // ctx.append('Content-Type', 'application/json;charset=utf-8') ctx.append('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS,PATCH') console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); //http 預請求處理(post/put/delete 請求在正式請求以前會先發送一個OPTIONS的預請求,只須要把這個OPTIONS的預請求正常返回,後續的請求就會正常執行) if (ctx.request.method === 'OPTIONS') { ctx.body = "OK" } else { //繼續執行api請求 await next(); } });
>>ctx.append()
添加請求頭,目的在服務器端解決跨域問題
>> ctx.request.method === 'OPTIONS'處理
請求在正式請求以前會先發送一個OPTIONS的預請求,只須要把這個OPTIONS的預請求正常返回,後續的請求就會正常執行
>>await next()
當一箇中間件調用 next() 則該函數暫停並將控制傳遞給定義的下一個中間件。當在下游沒有更多的中間件執行後,堆棧將展開並 且每一箇中間件恢復執行其上游行爲。
>啓動端口監聽,啓動項目
四、api接口(以處理mysql數據的接口爲例)
controllers文件夾下建立mysqlapi.js文件
let util = require('../utils/util') const db = require('../mysql/mysql.js') var logger = require('log4js').getLogger("index"); let util_http = require('../utils/util_http') module.exports = { /** * 根據數據表名查詢所有 */ 'GET /mysql/findAll': async (ctx, next) => { ctx.response.type = 'application/json'; let table = ctx.request.query.table let sql = `select * from ${table}` await db.selectAll(sql).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) }, /** * 根據數據表名和指定查詢條件查詢 */ 'GET /mysql/findBy': async (ctx, next) => { ctx.response.type = 'application/json'; ctx.append('Access-Control-Allow-Origin', '*') let table = ctx.request.body.table let where = ctx.request.body.where await db.selectBy(table, where).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) }, /** * 根據數據表名和id查詢 */ 'GET /mysql/findById': async (ctx, next) => { ctx.response.type = 'application/json'; ctx.append('Access-Control-Allow-Origin', '*') let table = ctx.request.query.table let id = ctx.request.query.id let sql = `select * from ${table} where id='${id}'` await db.selectAll(sql).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) }, /** * 添加數據 */ 'POST /mysql/add': async (ctx, next) => { // ctx.response.type = 'application/json'; // ctx.res.header('Access-Control-Allow-Origin', '*'); if (ctx.req.method == 'POST') { let data = await util_http.getPOSTRes(ctx.req) data = JSON.parse(data) let table = data.table let params = data.params await db.insertData(table, params).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) } else { ctx.body = util.err('請求錯誤') } }, /** * 更新數據 */ 'PUT /mysql/update': async (ctx, next) => { if (ctx.req.method == 'PUT') { let data = await util_http.getPOSTRes(ctx.req) data = JSON.parse(data) let table = data.table let sets = data.sets let where = data.where // console.log('sql', table, sets, where) await db.updateData(table, sets, where).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) } else { ctx.body = util.err('請求錯誤') } }, // /** // * 更新數據 // */ // 'PATCH /mysql/patch': async (ctx, next) => { // // ctx.response.type = 'application/json'; // console.log('patch init') // ctx.body = '2222' // //ctx.body=util.res('123') // // console.log('request',ctx.request) // // let table = ctx.request.body.table // // console.log('table',table) // // let sets = ctx.request.body.sets // // let where = ctx.request.body.where // // await db.updateData(table, sets, where).then(res => { // // ctx.body = util.res(res) // // }).catch(err => { // // ctx.body = util.err(err) // // }) // }, /** * 刪除數據 */ 'DELETE /mysql/delete': async (ctx, next) => { let table = ctx.request.body.table let where = ctx.request.body.where await db.deleteData(table, where).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) }, /** * 根據數據表名和id刪除數據 */ 'DELETE /mysql/deleteById': async (ctx, next) => { ctx.response.type = 'application/json'; ctx.append('Access-Control-Allow-Origin', '*') let table = ctx.request.query.table let id = ctx.request.query.id let where = { id: id } await db.deleteData(table, where).then(res => { ctx.body = util.res(res) }).catch(err => { ctx.body = util.err(err) }) } };
>get 接口中使用ctx.request.query接收Params方式傳遞的表單參數
>post接口中使用ctx.request.body接收body方式傳遞的表單參數
>>post接口中,須要使用監聽方式接收表單數據(req.on('data',(chunk)=>{}))+req.on('end',()=>{}))
let util_http={ /** * 接口執行成功統一回復格式 * @param {*} result 返回結果 * @param {*} code 返回代碼 * @param {*} msg 返回消息 */ async getPOSTRes(req){ return new Promise((resolve,reject)=>{ let data = ''; //2.註冊data事件接收數據(每當收到一段表單提交的數據,該方法會執行一次) req.on('data', function (chunk) { // chunk 默認是一個二進制數據,和 data 拼接會自動 toString data += chunk; }); // 3.當接收表單提交的數據完畢以後,就能夠進一步處理了 //註冊end事件,全部數據接收完成會執行一次該方法 req.on('end', function () { //(1).對url進行解碼(url會對中文進行編碼) data = decodeURI(data); resolve(data) }); }) } } module.exports=util_http
接收參數時(包括接口中調用mysql接口時),均須要藉助async,await關鍵字,將代碼執行的控制權交給下一級中間件,待下一級中間件執行完成後,繼續後續代碼操做
另附mysql文件夾下的mysql配置文件和mysql操做文件
const mysql = require('mysql') const connectdb=()=>{ let connection = mysql.createConnection({ host : 'localhost', port : '3306', user : 'root', password : '', database : 'rehab' }) return connection; } module.exports=connectdb;
const conn = require('./config/index'); const connection = conn(); // 查詢全部數據 let selectAll = async(sql,callback)=>{ return sqlQuery(sql) } let selectBy = async(table,where,callback)=>{ var _WHERE=''; // var keys=''; // var values=''; for(var k2 in where){ _WHERE+=k2+"='"+where[k2]+"' AND "; //_WHERE+= k2+"='"+where[k2]+"'"; } _WHERE=_WHERE.slice(0,-5) // UPDATE user SET Password='321' WHERE UserId=12 //update table set username='admin2',age='55' where id="5"; var sql="SELECT * FROM "+table+' WHERE '+_WHERE; // console.log(sql); return sqlQuery(sql) } // 插入一條數據 let insertData =async (table,datas,callback)=>{ var fields=''; var values=''; for( var k in datas){ fields+=k+','; values=values+"'"+datas[k]+"'," } fields=fields.slice(0,-1); values=values.slice(0,-1); // console.log(fields,values); var sql="INSERT INTO "+table+'('+fields+') VALUES('+values+')'; return sqlQuery(sql) } /** * 更新一條數據 * @param {*} table 數據表名 * @param {*} sets 更新字段 * @param {*} where 限制條件 */ let updateData=async function(table,sets,where){ var _SETS=''; var _WHERE=''; var keys=''; var values=''; for(var k in sets){ _SETS+=k+"='"+sets[k]+"',"; } _SETS=_SETS.slice(0,-1); for(var k2 in where){ _WHERE+=k2+"='"+where[k2]+"' AND "; //_WHERE+= k2+"='"+where[k2]+"'"; } _WHERE=_WHERE.slice(0,-5) // UPDATE user SET Password='321' WHERE UserId=12 //update table set username='admin2',age='55' where id="5"; var sql="UPDATE "+table+' SET '+_SETS+' WHERE '+_WHERE; // console.log(sql); return sqlQuery(sql) } // 刪除一條數據 let deleteData=function(table,where,callback){ var _WHERE=''; for(var k2 in where){ _WHERE+=k2+"='"+where[k2]+"' AND "; //_WHERE+= k2+"="+where[k2]; } _WHERE=_WHERE.slice(0,-5) // DELETE FROM user WHERE UserId=12 注意UserId的數據類型要和數據庫一致 var sql="DELETE FROM "+table+' WHERE '+_WHERE; // connection.query(sql,callback); return sqlQuery(sql) } let sqlQuery=function(sql){ return new Promise((resolve,reject)=>{ connection.query(sql,(err,result)=>{ if(err){ console.log('錯誤信息-',err.sqlMessage); let errNews = err.sqlMessage; reject(errNews) } else{ resolve(result) } }) }) } module.exports = { selectAll, selectBy, insertData, deleteData, updateData, }
VUE操做(接口調用操做)
一、Vue-cli 3.0搭建項目,配置package.json項目依賴,添加vue.js文件,配置vue文件路由,main.js的組件引用操做省略
二、測試文件
<template> <div class="api"> <div class="api-item"> <van-button type="default" @click="getTest">get test</van-button> </div> <div class="api-item"> <van-field v-model="name.add" placeholder="請輸入name" /> <van-button type="default" @click="addTest">add test</van-button> </div> <div class="api-item"> <van-field v-model="name.update_set" placeholder="請輸入更改值" /> <van-field v-model="name.update_where" placeholder="請輸入初始值" /> <van-button type="default" @click="updateTest">update test</van-button> </div> <div class="api-item"> <van-field v-model="name.delete" placeholder="請輸入刪除值" /> <van-button type="default" @click="deleteTest">delete test</van-button> </div> <div class="api-item api-result"> <p>result:</p> <p>code:{{result.code}}</p> <p>msg:{{result.msg}}</p> <p>result:{{result.result}}</p></div> </div> </template> <script> import {api} from "../../api/server"; export default { name: "apitest", data(){ return{ openid:'28d91cb4-28a6-4110-b401-a6ca03cddf27', result:'', name:{ add:'22', update_set:'44', update_where:'33', delete:'2233' }, msg:'' } }, methods:{ getTest(){ let params={ table:'test' } let that=this; api.get(params).then(res=>{ console.log('res',res) that.result=res }).catch(err=>{ console.log('err',err) that.result=err }) }, addTest(){ if(this.name.add==''){ this.Toast('請輸入name'); return false } let params= { table: 'test', params: { id: this.util.randomNum(), name: this.name.add } } let that=this; api.add(params).then(res=>{ console.log('res',res) that.result=res }).catch(err=>{ console.log('err',err) that.result=err }) }, updateTest(){ if(this.name.update_where==''||this.name.update_set==''){ this.Toast('請輸入name'); return false } let params= { table: 'test', sets: { name:this.name.update_set }, where: { name:this.name.update_where } } // let params={ // id:'11', // name:'22' // } let that=this; api.update(params).then(res=>{ console.log('res',res) that.result=res }).catch(err=>{ console.log('err',err) that.result=err }) }, deleteTest(){ if(this.name.update_where==''||this.name.update_set==''){ this.Toast('請輸入name'); return false } let params= { table: 'test', where: { name:this.name.delete } } // let params={ // id:'11', // name:'22' // } let that=this; api.delete(params).then(res=>{ console.log('res',res) that.result=res }).catch(err=>{ console.log('err',err) that.result=err }) }, } } </script> <style lang="less" scoped> .api{ text-align: left; background-color: #ffffff; .api-item{ margin: 8px; } } </style>
三、api接口
import axios from 'axios'; const server = 'http://localhost:3000'; export const api={ get(params){ return axios.get(server + '/mysql/findAll', {params}); }, add(params){ let headers = {'Content-Type': 'multipart/form-data'} return axios.post(server + '/mysql/add', params, {headers}); }, update(params){ let headers = {'Content-Type': 'multipart/form-data'} return axios.put(server + '/mysql/update', params,{headers}); }, delete(params){ let headers = {'Content-Type': 'multipart/form-data'} return axios.delete(server + '/mysql/delete', {data:params},{headers}); }, }
> 示例中使用的是axios組件調用的api接口
> params接口參數形式說明
>> get接口參數須要使用{}包裹,如{params}
>> post\put接口直接添加params參數
>> delete接口需加上'data'說明,如{data:params}
>headers
>> post\put\delete三種方式須要添加請求頭header設置 headers = {'Content-Type': 'multipart/form-data'}
在node.js中接收方式爲ctx.request.body
>> get方式請求不須要加請求頭,在node.js中接收方式爲ctx.request.query
>> put\delete 請求方式在發送請求前會先發送預請求命令,請求方式爲OPTIONS,這時只須要在node.js中正常返回,不須要作任何處理
最後,展現幾種接口在postman中的調用方式