javaScritp

nodejs-restful架构以及相关项目

2018-06-30  本文已影响2人  DragonRat

作者:烨竹

参考:

  1. RESTful API设计指南:https://goo.gl/KpKEvS
  2. 简明RESTful API设计要点:https://goo.gl/FqRdLG
  3. 从了解REST到设计RESTfulhttps://goo.gl/p469eO

如果,我们要为机器人设计一个简单的RESTful API,配合HTTP 实作,考虑的动作会有:

  1. POST:新增一个机器人(为机器人型号,命名,种类,设计结构,相关功能…的新增)
  2. GET:取得一个机器人(我们从全部的机器人,找出一个机器人)
  3. PUT:更新机器人(更新他的功能,比方,飞天变成遁地)
  4. DELETE:移除这个机器人(美江表示:这是机器人的转移…)
  5. GET:找出全部的机器人(列出所有机器人名单)

哇,说着说… 这世界上很多东西,都可以用此REST规范来设计呢!

那么,对映路由设计的话,可以考虑以下情况:

  1. POST/robots
  2. GET/robot/:id(ex. /robot/5取得id值为5号的机器人)
  3. PUT/robots/:id
  4. DELETE/robots/:id
  5. 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。。。
。。。
。。

界面效果如下

增:

其余的就不多做介绍

上一篇 下一篇

猜你喜欢

热点阅读