node.js 接口调用示例
测试用例git地址(node.js部分):https://github.com/wuyongxian20/node-api.git
项目架构如下:
controllers:
文件夹下为接口文件
logs:
log4js 日志打印文件
mongodb:
mongodb数据库配置及帮助类的封装
mysql:
mysql数据库配置及数据库的封装
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数据库使用部分)
1、创建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:日志文件
2、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(); // };
3、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() 则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并 且每个中间件恢复执行其上游行为。
>启动端口监听,启动项目
4、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操作(接口调用操作)
1、Vue-cli 3.0搭建项目,配置package.json项目依赖,添加vue.js文件,配置vue文件路由,main.js的组件引用操作省略
2、测试文件
<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>
3、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中的调用方式