nodejs-restful架构以及相关项目
2018-06-30 本文已影响2人
DragonRat
作者:烨竹
参考:
- RESTful API设计指南:https://goo.gl/KpKEvS
- 简明RESTful API设计要点:https://goo.gl/FqRdLG
- 从了解REST到设计RESTful:https://goo.gl/p469eO
如果,我们要为机器人设计一个简单的RESTful API,配合HTTP 实作,考虑的动作会有:
- POST:新增一个机器人(为机器人型号,命名,种类,设计结构,相关功能…的新增)
- GET:取得一个机器人(我们从全部的机器人,找出一个机器人)
- PUT:更新机器人(更新他的功能,比方,飞天变成遁地)
- DELETE:移除这个机器人(美江表示:这是机器人的转移…)
- GET:找出全部的机器人(列出所有机器人名单)
哇,说着说… 这世界上很多东西,都可以用此REST规范来设计呢!
那么,对映路由设计的话,可以考虑以下情况:
-
POST:
/robots
-
GET:
/robot/:id
(ex. /robot/5取得id值为5号的机器人) -
PUT:
/robots/:id
-
DELETE:
/robots/:id
-
GET:
/robtos
观念厘清以后,我们来看看,如何实作于express 架构上吧!
实作RESTful Web API
准备:除了express以外,这边还会用到body-parser,务必载入!
我们直接看程式:
robots.js
var express = require('express');
var app = express();
var bodyParser = require( 'body-parser' );
// configure app to use bodyParser()
// this will let us get the data from Request
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.text());
// Create a router to handle routes for a set of RestAPI
var RestAPI = express.Router();
// CREATE (/restful/robots)
RestAPI.post('/robots', function(req, res) {
// ...
res.json({ name: req.body.name , message: "我已經收到機器人名字:"+ req.body.name});
});
// READ ALL & FORM (/restful/robots)
RestAPI.get('/robots',function(req,res){
// ...
res.json({message:"你要的機器人列表!!"});
});
// READ (/restful/robots/:id)
RestAPI.get('/robots/:id', function(req, res) {
// ...
res.json({id:req.params.id, message:"我要了解機器人"+req.params.id+"號!"});
});
// UPDATE ((/restful/robots/:id))
RestAPI.put('/robots/:id', function(req, res) {
// ...
res.json({id:req.params.id, message:"我要改造機器人"+req.params.id+"號!"});
});
// DELETE (/restful/robots/:id)
RestAPI.delete('/robots/:id', function(req, res) {
// ...
res.json({id:req.params.id, message:"美江說,他要買"+req.params.id+"號-機器人"});
});
//讓此路徑適用於這個router
app.use('/restful',RestAPI);
app.listen(3000,function(){
console.log('Ready...for 3000');
});
测试,可以用google 浏览器提供的扩充功能postman:
测试POST:
一个简单应用restful的项目:todo的增删改查
目的:为这几天所学做个总结----expressJS如何设定路由,基本的表单传送操作与呈现,静态档案的使用;也学习了如何串接mongoDB做CRUD,并且将其资料呈现在网页上使用Jade 样版,还有最近的RESTful架构与MVC架构分析
项目结构总览:
需要实现的功能是什么?需要运用 什么技术?怎样一步一步的实现?
需要实现的功能很简单:就是增删改查;
运用的技术:上面有简单的介绍
一步一步实现:第一步建模(简单的前端界面),然后根据前端界面细化增删改查
建模规划
首先建一个前端界面:
module.exports=
[{id:1,message:'testqqq'},
{id:2,message:'fasfasdf'},
{id:3,message:'mmmmmmm'}];
前端界面和数据库结构如下:
驱动文件:主要是引入“mvc”中的“C”;
var express = require('express');
var bodyParser = require( 'body-parser' );
var todoRouter= require('./routes/todo');
var app = express();
//var dataset=require('./recordset.js'); //要有文件來做页面呈現, 所以直接引入!
//set view engine
app.set("view engine","pug")
//set view directory
app.set("views",__dirname+"/views") // 样版所在位置
//app.get('/todo',function(req,res){
//res.render('restfulTP',{itemlist:dataset}); // render 到 restful樣版
//});
// configure app to use bodyParser()
// this will let us get the data from Request
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.text());
// Apply this router on (/restful)
app.use('/', todoRouter);
app.use(express.static(__dirname+'/public')); //一些必要的javascript, css皆放入此!!
app.listen(3000,function(){
console.log('Ready...for 3000');
});
“c”文件:todo.js
引入“m”文件,运用restful原理显示“v”文件;代码如下:
var express=require('express');
var router = express.Router();
// load mongodb-CURL
var modelCreate=require('../model/todocreatedb.js');
var modelUpdate=require('../model/todoupdatedb.js');
var modelRemove=require('../model/todoremovedb.js');
var modelQuery=require('../model/todoquerydb.js');
router.use(function(req, res, next) {
// do logging
console.log('Something is happening.');
next(); // make sure we go to the next routes and don't stop here
});
// READ ALL & FORM (/restful/todo)
router.get('/todo',function(req,res){
//mongodb find all.....
modelQuery.QueryGet({},function(record){
if(req.xhr)
res.render('recordTP',{layout:false, itemlist:record});
else
res.render('restfulTP',{itemlist:record});
});
});
// CREATE (/restful/todo)
router.post('/todo', function(req, res) {
// ...
var dataset=[{message:req.body.momsg}];
//console.log(dataset);
modelCreate.InsertNew(dataset,function(msg){ //res 是新增筆數
// res.send('Write '+msg+' records to collections!');
//res.send(body);
return res.redirect('/todo');
});
//res.send('you push a request to create');
});
// READ (/restful/todo/:id)
// 這邊要注意的:這裡的id是網址列後的搜尋字串
// 在用 req.params是根據路由給的參數名稱, 與req.body的不同處!
// 如果你怕會搞混, 請修改!
router.get('/todo/:id', function(req, res) {
// mongodb find one or all...
var dataset={message:req.params.id}
modelQuery.QueryGet(dataset,function(record){
if(req.xhr)
res.render('recordTP',{layout:false, itemlist:record});
else
res.render('restfulTP',{itemlist:record});
});
//res.send('you push a request to read one');
});
// UPDATE ((/restful/todo/:id))
router.put('/todo/:id', function(req, res) {
// ...
var dataset={id:parseInt(req.params.id),message:req.body.momsg};
modelUpdate.UpdateSave(dataset,function(record){
res.render('oneTP',{layout:false,
oneid:record.id,onemsg:record.message});
});
//res.send('you push a request to put! ' + req.body.moid+req.body.momsg);
});
// DELETE (/restful/todo/:id)
router.delete('/todo/:id', function(req, res) {
// ...
var dataset={id:parseInt(req.params.id)}
//console.log(dataset);
modelRemove.RemoveSave(dataset,function(msg){
res.send(msg);
});
});
module.exports=router;
“m”文件:数据库相关操作其实可以放在一个文件里面,不过我分了四个文件;主要是四个增删改查的方法
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Use connect method to connect to the server
module.exports.InsertNew=function(data,callback){
MongoClient.connect('mongodb://tester:password@localhost:27017/lxkdb',{ useNewUrlParser: true }, function(err, db) {
assert.equal(null, err);
var dbo=db.db('lxkdb');
dbo.collection('student',function(err,collection){
if(err) throw err;
//找id最大值的那一笔, 之后id继续往上加...
collection.find({},{'id':1}).sort({'id':-1}).limit(1).toArray(
function(err,items){
if(err) throw err;
var dataset=[]; //定义一个新的资料集dataset
var current=0;
if(items.length>0)
{
current=items[0].id+1;
}
//把传进來的data,变成一个新的dataset,给insertMany
data.map(function(obj){
dataset.push({id:current++,message:obj.message});
});
//把资料集加入mongodb, 可以是一笔或多笔
collection.insertMany(dataset, function(err, r) {
callback(r.insertedCount); //计算新增的笔数
});
});
});
});
}
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Use connect method to connect to the server
module.exports.UpdateSave=function(data,callback){
MongoClient.connect('mongodb://tester:password@localhost:27017/lxkdb',{ useNewUrlParser: true }, function(err, db) {
assert.equal(null, err);
var dbo=db.db('lxkdb');
if(err) throw err;
dbo.collection("student").findAndModify(
{id:data.id},
[],
{ $set: { message:data.message} },
{new : true},
function(err,doc) {
if(err) throw err;
callback(doc.value);
}
);
});
}
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Use connect method to connect to the server
module.exports.RemoveSave=function(data,callback){
MongoClient.connect('mongodb://tester:password@localhost:27017/lxkdb',{ useNewUrlParser: true }, function(err, db) {
assert.equal(null, err);
var dbo=db.db('lxkdb');
if(err) throw err;
dbo.collection('student',function(err,collection){
collection.remove({id:data.id},{w:1},function(err,result){
if(err) throw err;
callback('Document Removed Successfully!');
});
});
});
}
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Use connect method to connect to the server
module.exports.QueryGet=function(data,callback){
MongoClient.connect('mongodb://tester:password@localhost:27017/lxkdb',{ useNewUrlParser: true }, function(err, db) {
assert.equal(null, err);
var dbo=db.db('lxkdb');
if(err) throw err;
dbo.collection('student',function(err,collection){
if(data.message)
collection.find({'message' : {$regex : '.*'+ data.message+ '.*'}}).toArray(function(err,items){
if(err) throw err;
callback(items);
});
else
collection.find({}).toArray(function(err,items){
if(err) throw err;
callback(items);
});
});
});
}
“V”文件主要负责界面显示:
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Use connect method to connect to the server
module.exports.QueryGet=function(data,callback){
MongoClient.connect('mongodb://tester:password@localhost:27017/lxkdb',{ useNewUrlParser: true }, function(err, db) {
assert.equal(null, err);
var dbo=db.db('lxkdb');
if(err) throw err;
dbo.collection('student',function(err,collection){
if(data.message)
collection.find({'message' : {$regex : '.*'+ data.message+ '.*'}}).toArray(function(err,items){
if(err) throw err;
callback(items);
});
else
collection.find({}).toArray(function(err,items){
if(err) throw err;
callback(items);
});
});
});
}
for item in itemlist
- var foo= "pencil" + item.id
- var fkk= "remove" + item.id
- var rec= "record" + item.id
div(id=rec)
button.btn.btn-default.btn-xs(type='button', id=foo,
data-toggle='modal', data-target='#myModal')
span.glyphicon.glyphicon-pencil(aria-hidden='true')
button.btn.btn-default.btn-xs(type='button', id=fkk)
span.glyphicon.glyphicon-remove(aria-hidden='true')
span(class=foo)=item.message
p
script(src='javascripts/buttonlist.js')
- var fjo= "pencil" + oneid
- var fjk= "remove" + oneid
button.btn.btn-default.btn-xs(type='button', id=fjo, data-toggle='modal', data-target='#myModal')
span.glyphicon.glyphicon-pencil(aria-hidden='true')
button.btn.btn-default.btn-xs(type='button', id=fjk)
span.glyphicon.glyphicon-remove(aria-hidden='true')
span(class=fjo)=onemsg
p
script(src='javascripts/buttonlist.js')
doctype html
html(lang='en')
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1')
// The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags
meta(name='description', content='')
meta(name='author', content='')
link(rel='icon', href='http://getbootstrap.com/favicon.ico')
title Sticky Footer Navbar Template for Bootstrap
// Bootstrap core CSS
link(href='http://getbootstrap.com/docs/3.3/dist/css/bootstrap.min.css', rel='stylesheet')
// IE10 viewport hack for Surface/desktop Windows 8 bug
link(href='https://getbootstrap.com/docs/3.3/assets/css/ie10-viewport-bug-workaround.css', rel='stylesheet')
// Custom styles for this template
link(href='https://getbootstrap.com/docs/3.3/examples/sticky-footer-navbar/sticky-footer-navbar.css', rel='stylesheet')
// Just for debugging purposes. Don't actually copy these 2 lines!
//
if lt IE 9script(src='http://getbootstrap.com/docs/3.3/assets/js/ie8-responsive-file-warning.js')
script(src='http://getbootstrap.com/docs/3.3/assets/js/ie-emulation-modes-warning.js')
// HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries
//
if lt IE 9
script(src='https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js')
script(src='https://oss.maxcdn.com/respond/1.4.2/respond.min.js')
body
// Fixed navbar
nav.navbar.navbar-default.navbar-fixed-top
.container
.navbar-header
button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar')
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand(href='#') Project name
#navbar.collapse.navbar-collapse
ul.nav.navbar-nav
li.active
a(href='/') Home
li
a(href='/person') Persons
li
a(href='/student') Students
// /.nav-collapse
// Begin page content
.container
.page-header
block page-header
p.lead
block content
footer.footer
.container
p.text-muted 2016-Created by circleuniv
//
Bootstrap core JavaScript
==================================================
// Placed at the end of the document so the pages load faster
script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js')
script.
window.jQuery || document.write('<script src="http://getbootstrap.com/docs/3.3/assets/js/vendor/jquery.min.js"><\\\/script>')
script(src='http://getbootstrap.com/docs/3.3/dist/js/bootstrap.min.js')
// IE10 viewport hack for Surface/desktop Windows 8 bug
script(src='http://getbootstrap.com/docs/3.3/assets/js/ie10-viewport-bug-workaround.js')
//一開始第一次, 二個都有作用, 因為layout
script(src='javascripts/restfuljs.js')
script(src='javascripts/buttonlist.js')
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}
相关js操作:
var reqdata={};
//做列表items button 的处理....
$('#itemset button').bind('click', function(){
// gets the id of a clicked button
var obj = $(this).attr('id');
var itemid='';
if(/pencil/.test(obj))
{
itemid=obj.replace(/pencil/gi,'');
var spancs='#itemset span.'+obj; //这笔按鈕的span 寻找方式字串。
var getmsg=$(spancs).text(); //取得所有的這笔资料。
$('#myModal').find($('#message-text')).val(getmsg); //把资料送給对话框里的textbox
reqdata={'moid':itemid,'momsg':getmsg}; //把id记录下來。
}
if(/remove/.test(obj)) //刪除...
{
itemid=obj.replace(/remove/gi,'');
reqdata={'moid':itemid};
//移除单一資料,直接做!!
//这里是delete ... url:'./restful/todo/1'
$.ajax({
url: '/todo/'+reqdata.moid,
type: 'DELETE'
}).done(function(resp){
var oneset='div#record'+reqdata.moid;
$(oneset).remove();
alert(resp);
});
}
})
//做修改 储存 button 的处理...
$('#myModal #saveitem').on('click',function(){
var postdata=$('#myModal').find($('#message-text')).val();
//alert(postdata);
reqdata.momsg=postdata; //原先的资料会更改成這个...
//把資料送給后端做处理。
//这里为put ... url:'./restful/todo/1'
$.ajax({
url: '/todo/'+reqdata.moid,
type: 'PUT',
data: reqdata
}).done(function(result){
var oneset='#itemset div#record'+reqdata.moid;
//alert(result);
$(oneset).html(result); //更新完,將結果覆盖原本的div#recode+id
});
//將dialog隐藏
$('#myModal').modal('hide')
})
var reqdata={};
//做新增 button 的处理...
$('#plus').on('click',function(){
reqdata={'momsg':$('#MsgTB').val()};
//把資料送給后端做处理。
//这里为post ... url:'./restful/todo'
$.ajax({
url: '/todo',
type: 'post',
data: reqdata
}).done(function(result){
alert('ok!');
$('#itemset').html(result);
});
})
//做搜寻 button 的处理...
$('#search').on('click',function(){
reqdata={'momsg':$('#MsgTB').val()};
//把資料送給后端做处理。
//这里为get ... url:'./restful/todo/fasdfkmkmkoika'
$.ajax({
url: '/todo/'+reqdata.momsg,
type: 'get'
}).done(function(result){
$("#itemset").html(result);
});
})
css引用的bootstrap的css。。。
。。。
。。
。
界面效果如下
增:
其余的就不多做介绍