MEAN Stack - REST APIs

該文章為網(wǎng)絡(luò)課 Introduction to MongoDB using the MEAN Stack學(xué)習(xí)筆記。

1.關(guān)于REST(Representational State Transfer)

1.1 什么是REST?

是一種編程模式,它通過(guò)HTTP來(lái)搭建客戶端瀏覽器與服務(wù)器之間的通信。
It is a paradigm for a browser with a server over HTTP.

關(guān)于HTTP request,可以看做是三個(gè)方面的組合:

  1. Verb:e.g. GET/POST
  2. Resource:e.g. /home
  3. Body (optional):e.g. JSON

當(dāng)服務(wù)器收到HTTP request之后培愁,會(huì)向客戶端返回HTTP request,這其中可以包含很多信息,比如status钉凌,JSON等等。

1.2 三個(gè)基本方面

3 fundamental aspects of REST design pattern: client, server, resource

1.3 CRUD (Creat/Read/Update/Delete)

POST -> Create
GET -> Read

1.4 什么是endpoint/routes捂人?

HTTP中使用的GET/user/42或者POST/user等被稱之為endpoint/routes御雕。創(chuàng)建REST API就是在創(chuàng)建能夠讓客戶端javascript代碼能夠使用的endpoint/routes。

1.5 什么是RESTful 滥搭?

  • 如果一個(gè)架構(gòu)符合REST原則酸纲,就稱它為RESTful架構(gòu)。

  • 總結(jié)一下什么是RESTful架構(gòu):

(1)每一個(gè)URI代表一種資源瑟匆; -----> e.g. URLs

(2)客戶端和服務(wù)器之間闽坡,傳遞這種資源的某種表現(xiàn)層; -----> e.g. data type

(3)客戶端通過(guò)四個(gè)HTTP動(dòng)詞愁溜,對(duì)服務(wù)器端資源進(jìn)行操作疾嗅,實(shí)現(xiàn)"表現(xiàn)層狀態(tài)轉(zhuǎn)化"。 -----> method: GET/POST/PUT/DELETE

2. Express

2.1 是什么冕象?

是用于啟動(dòng)一個(gè)HTTP Server的Node.js pakage代承。相當(dāng)于是一個(gè)Web Application Framework允許設(shè)計(jì)者用Node.js來(lái)搭建server。

2.2 一個(gè)簡(jiǎn)單的Hello World例子

  • package.json
  "dependencies": {
    "express": "4.12.3"
  }
  • server.js
var express = require('express');

var app = express(); // 創(chuàng)建一個(gè)express web app server

app.get('/', function(req, res) { // 創(chuàng)建一個(gè)routes為"/"的HTTP GET響應(yīng)
  res.send('Hello, world!'); //返回一個(gè)"Hello, world!"頁(yè)面的響應(yīng)
});
 
// 創(chuàng)建一個(gè)routes為"/user/:user"的HTTP GET響應(yīng)
// :user表示user是一個(gè)可變的參數(shù)渐扮,可以利用req.params取到
// req.query.school可以取到HTTP中?之后的school對(duì)應(yīng)的內(nèi)容
app.get('/user/:user', function(req, res) { 
  res.send('Page for user ' + req.params.user + + ' with school ' + req.query.school);
});

app.listen(3000); //讓這個(gè)server監(jiān)聽(tīng)來(lái)自port3000的請(qǐng)求
console.log('Server listening on port 3000!');
  • 結(jié)果
routes "/"
routes "/user/jiayuan"

3. TDD (Test Driven Development)

  • 思想:不需要瀏覽器來(lái)對(duì)server進(jìn)行測(cè)試论悴,利用一個(gè)叫做“superagent”的package來(lái)模擬瀏覽器發(fā)出的HTTP request,并接收HTTP response墓律,然后利用類似常規(guī)mocha測(cè)試的方法進(jìn)行測(cè)試意荤。

  • package.json

{
  "dependencies": {
    "express": "4.12.3",
    "mocha": "2.2.4",
    "superagent": "1.2.0"
  },
  "scripts": {
    "test": "mocha test.js"
  }
}
  • server.js
    值得注意的是,由于此處重視的是測(cè)試只锻,因此server上只寫(xiě)express的邏輯玖像,并以一個(gè)函數(shù)的方式export出去,此處并不需要寫(xiě)對(duì)server的啟動(dòng),即不需要寫(xiě)對(duì)port的監(jiān)聽(tīng)捐寥。
var express = require('express');

module.exports = function() {
  var app = express();

  app.get('/', function(req, res) {
    res.send('Hello, world!');
  });

  app.get('/user/:user', function(req, res) {
    res.send('Page for user ' + req.params.user + ' with option ' +
      req.query.option);
  });

  return app;
};
  • test.js
    對(duì)server的啟動(dòng)以及關(guān)閉由測(cè)試文件來(lái)執(zhí)行笤昨。
var app = require('./server');
var assert = require('assert');
var superagent = require('superagent');

describe('server', function() {
  var server;

  beforeEach(function() {
    server = app().listen(3000); //啟動(dòng)server
  });

  afterEach(function() {
    server.close(); //關(guān)閉server
  });

  it('prints out "Hello, world" when user goes to /', function(done) {
    // 創(chuàng)建一個(gè)對(duì)"http://localhost:3000/"的GET請(qǐng)求,并對(duì)響應(yīng)進(jìn)行測(cè)試
    superagent.get('http://localhost:3000/', function(error, res) {
      assert.ifError(error);
      assert.equal(res.status, 200);
      assert.equal(res.text, "Hello, world!");
      done();
    });
  });
});

4. Dependency Injection

  • 思想:相當(dāng)于一種code isolation握恳。對(duì)作用是initialization的代碼與使用這些代碼的代碼進(jìn)行隔離, 以此增加設(shè)計(jì)的靈活性瞒窒。

  • 例子
    假設(shè)我們需要利用express中從客戶端取到的數(shù)據(jù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作,那么一個(gè)非常直觀的做法是:

  1. 把mongoDB initialization setup的代碼放在server.js的全局處乡洼,然后在routeHandler中使用從客戶端取到的數(shù)據(jù)對(duì)mongoDB進(jìn)行操作崇裁。
//全局scope: mongoDB setup
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/test');

var userSchema = new mongoose.Schema({
  name: String
});

var User = mongoose.model('User', userSchema);

//express
var express = require('express');

var app = express();

app.get('/user/:user', function(req, res) {
    //利用id來(lái)查user并以JSON可是作為響應(yīng)
    User.findOne({ _id: req.params.id }, function(error, user) {
       res.json({ user: user });
    });
});


app.listen(3000);
console.log('Server listening on port 3000!');

這樣做的弊端是:當(dāng)需要用不同的數(shù)據(jù)庫(kù)是,我不僅需要改動(dòng)全局scope中數(shù)據(jù)庫(kù)的setup代碼束昵,我還需要改動(dòng)express中的routeHandler代碼拔稳。當(dāng)express中邏輯比較復(fù)雜的時(shí)候,這樣的改動(dòng)就顯得很cumbersome锹雏。

  1. 利用一個(gè)名叫"wagner-core"的包裹來(lái)對(duì)數(shù)據(jù)庫(kù)setup工作進(jìn)行封裝巴比,寫(xiě)成“service”
var express = require('express');
var mongoose = require('mongoose');
var wagner = require('wagner-core');

// Dependency Injection: setupModels 和 setupApp的代碼被隔離開(kāi)
setupModels(mongoose, wagner);

var app = express();

setupApp(app, wagner);

app.listen(3000);
console.log('Listening on port 3000!');

//對(duì) model的set up不再是全局scope礁遵,而是在一個(gè)函數(shù)中的局部scope
function setupModels(mongoose, wagner) {
  mongoose.connect('mongodb://localhost:27017/test');

  var userSchema = new mongoose.Schema({
    name: String
  });
  var User = mongoose.model('User', userSchema);

  wagner.factory('User', function() { // 在wagner中定義以個(gè)名叫“User”的Service
    return User;
  });
}

function setupApp(app, wagner) {
  //雖然setupApp中的代碼與setupModel中的scope并不一樣
  //但利用wagner.invoke來(lái)引入定義了的service轻绞,因此setupApp中的代碼可以在這個(gè)scope中得到利用
  var routeHandler = wagner.invoke(function(User) {
    return function(req, res) {
      User.findOne({ _id: req.params.id }, function(error, user) {
        res.json({ user: user });
      });
    };
  });

  app.get('/user/:id', routeHandler);
}

這樣的好處在于:

  • setup的代碼不需要在全局scope也可以在express的routeHandler中得到使用。這是一種closure的概念佣耐,User是函數(shù)routeHandler的closure政勃,相當(dāng)于雖然User并不在routeHandler的scope里面,但是卻可以用它兼砖,想到與routeHandler closes User奸远。
  • 對(duì)setup代碼的改動(dòng)可以反映到express的代碼中,而express得代碼并不需要?jiǎng)右从恪1热缫獡Q一個(gè)數(shù)據(jù)庫(kù)然走,只需要改動(dòng)setupModel中的代碼援制。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戏挡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晨仑,更是在濱河造成了極大的恐慌褐墅,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洪己,死亡現(xiàn)場(chǎng)離奇詭異妥凳,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)答捕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)逝钥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人拱镐,你說(shuō)我怎么就攤上這事艘款〕旨剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵哗咆,是天一觀的道長(zhǎng)蜘欲。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晌柬,這世上最難降的妖魔是什么姥份? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮年碘,結(jié)果婚禮上澈歉,老公的妹妹穿的比我還像新娘。我一直安慰自己盛泡,他們只是感情好闷祥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著傲诵,像睡著了一般凯砍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拴竹,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天悟衩,我揣著相機(jī)與錄音,去河邊找鬼栓拜。 笑死座泳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幕与。 我是一名探鬼主播挑势,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼啦鸣!你這毒婦竟也來(lái)了潮饱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诫给,失蹤者是張志新(化名)和其女友劉穎香拉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體中狂,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凫碌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胃榕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盛险。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苦掘,到底是詐尸還是另有隱情泉褐,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布鸟蜡,位于F島的核電站膜赃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏揉忘。R本人自食惡果不足惜跳座,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泣矛。 院中可真熱鬧疲眷,春花似錦、人聲如沸您朽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哗总。三九已至几颜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間讯屈,已是汗流浹背蛋哭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涮母,地道東北人谆趾。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叛本,于是被迫代替她去往敵國(guó)和親沪蓬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容