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"
}
}
package.json
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();
// };
controller.js
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
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"
}
}
}
log4js.json
>加载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)
})
}
};
node.js api接口示例
>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
body表单数据接收
接收参数时(包括接口中调用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;
mysql配置文件
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,
}
mysql操作文件
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>
vue测试页
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});
},
}
vue api接口
> 示例中使用的是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中的调用方式
node.js 接口调用示例的更多相关文章
- ajax请求node.js接口时出现 No 'Access-Control-Allow-Origin' header is present on the requested resource错误
ajax请求node.js接口出现了如下的错误: XMLHttpRequest cannot load http://xxx.xxx.xx.xx:8888/getTem?cityId=110105&a ...
- Hyperledger fabric Client Node.js Hello World示例程序
简介 Hyperledger fabric Client (HFC)提供了基于Node.js的应用接口来访问Hyperledger区块. 本文介绍了一个使用HFC访问IBM Bluemixr区块服务的 ...
- 在 Node.js 上调用 WCF Web 服务
摘要:有时我们需要在WCF中做一些复杂数据处理和逻辑判断等,这时候就需要在NodeJS中调用WCF服务获取数据,这篇文件介绍如何在Node中调用WCF服务获取数据. Node项目中调用WCF服务获取数 ...
- 基于JAVA的全国天气预报接口调用示例
step1:选择本文所示例的接口"全国天气预报接口" url:https://www.juhe.cn/docs/api/id/39/aid/87step2:每个接口都需要传入一个参 ...
- JAVA的免费天气api接口调用示例
step1:选择本文所示例的接口"免费天气api" url:https://www.juhe.cn/docs/api/id/39/aid/87 step2:每个接口都需要传入一个参 ...
- node.js前后台交互示例 -- 使用node.js实现用户注册功能
node.js环境自行搭建,参考菜鸟教程的node.js就可以. 1 通过ajax提交index.html中form表单 register.html文件如下: <!doctype html> ...
- Node.js接口避免重复启动
众所周知,一个Node接口要是想被调用,得先在命令行中执行如下代码来启动接口 node base.js 但是一旦修改了base.js,就得重新执行这句命令 注:这里的base.js是我的node接口文 ...
- .net短信接口调用示例(106短信通道)
1. [代码]调用代理示例 using System;using System.Data;using System.Configuration;using System.Collections;usi ...
- Http接口调用示例教程
介绍HttpClient库的使用前,先介绍jdk里HttpURLConnection,因为HttpClient是开源的第三方库,使用方便,不过jdk里的都是比较基本的,有时候没有HttpClient的 ...
随机推荐
- layui时间控件闪退的问题
项目上线,发现后台管理系统layui的子页面出现时间控件闪退的问题,根本选取不到时间. 其原因是:如果出现页面找到多个节点,只有第一个节点能正常使用后面的节点都会闪退,可以理解为目前laydate不支 ...
- Qt编写自定义控件61-通用移动
一.前言 通用移动类,目标就是为了实现放入任意的控件以后,支持鼠标拖动,在容器中或者父类中拖动,这个应用场景非常多,比如在地图上放置的设备,需要用户自行按下拖动到指定的合适的位置,然后保存设备的位置坐 ...
- 让pc端代码适用移动端——<meta name="viewport"
写的代码,在pc端运行正常,在移动端就很小很小,需要放大.这时候可引入这个标签 @参考博客 用法,在<head></head>中添加<meta name="vi ...
- 常用快捷键 & BLOG & Website
blog http://kejianttt.com/http://pptdesign.blogbus.com/ Website http://www.webdesignerdepot.com/ 素 ...
- 16点睛Spring4.1-TaskScheduler
转发:https://www.iteye.com/blog/wiselyman-2213049 16.1 TaskScheduler 提供对计划任务提供支持; 使用@EnableScheduling开 ...
- ASP.net发布项目引用了C++DLL后页面提示找不到指定模块的异常
1.在引用C++dll的DllImport位置指定dll位置 [DllImport(@"C:\Windows\System32\DDyn_Method.dll", EntryPoi ...
- iOS 13 DeviceToken获取发生变化
问题描述: iOS 13 通过[deviceToken description]获取到的内容已经变了,这段代码运行在 iOS 13 上已经无法获取到准确的DeviceToken字符串了, NSStri ...
- ubuntu 安装 typora
# or run: # sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA300B7755AFCFAE wget -qO ...
- PHP中NULL和‘'的区别
PHP中NULL和‘'区别 null的类型是null,""的类型是string 所以是不同东西 <pre>$a=22;unset($a);var_dump($a);&l ...
- Linux下的Curses库的源码下载与安装
curses库是可以在linux终端中写出字符用户界面的一个库,现在较新的版本应该是ncurses库,现在已经很少有人再去使用他了,所以想拥有这个库并且在linux下写出自己用户界面的可以参考一下本博 ...