鍍金池/ 教程/ HTML/ 基本的 HTTP 服務(wù)器
備忘錄模式
解釋器模式
類似 Python 的 zip 函數(shù)
類變量和實(shí)例變量
提示參數(shù)
指數(shù)對(duì)數(shù)運(yùn)算
檢查變量的類型是否為數(shù)組
由數(shù)組創(chuàng)建一個(gè)字符串
生成隨機(jī)數(shù)
刪除數(shù)組中的相同元素
大寫單詞首字母
雙向服務(wù)器
類的混合
計(jì)算復(fù)活節(jié)的日期
轉(zhuǎn)換弧度和度
找到上一個(gè)月(或下一個(gè)月)
雙向客戶端
橋接模式
嵌入 JavaScript
AJAX
觀察者模式
克隆對(duì)象(深度復(fù)制)
一個(gè)隨機(jī)整數(shù)函數(shù)
清理字符串前后的空白符
歸納數(shù)組
平方根倒數(shù)快速算法
適配器模式
打亂數(shù)組中的元素
將數(shù)組連接
使用數(shù)組來(lái)交換變量
更快的 Fibonacci 算法
服務(wù)器
服務(wù)端和客戶端的代碼重用
客戶端
查找子字符串
策略模式
CoffeeScrip 的 type 函數(shù)
由數(shù)組創(chuàng)建一個(gè)對(duì)象詞典
回調(diào)綁定
工廠方法模式
映射數(shù)組
當(dāng)函數(shù)括號(hào)不可選
生成可預(yù)測(cè)的隨機(jī)數(shù)
不使用 jQuery 的 Ajax 請(qǐng)求
把字符串轉(zhuǎn)換為小寫形式
類方法和實(shí)例方法
擴(kuò)展內(nèi)置對(duì)象
定義數(shù)組范圍
MongoDB
匹配字符串
創(chuàng)建一個(gè)不存在的對(duì)象字面值
列表推導(dǎo)
比較范圍
修飾模式
檢測(cè)每個(gè)元素
拆分字符串
字符串插值
對(duì)象數(shù)組
去抖動(dòng)函數(shù)
使用 Nodeunit 測(cè)試
SQLite
單件模式
篩選數(shù)組
替換子字符串
數(shù)組最大值
計(jì)算(美國(guó)和加拿大的)感恩節(jié)日期
找到一個(gè)月中的最后一天
計(jì)算兩個(gè)日期中間的天數(shù)
基本的 HTTP 服務(wù)器
把字符串轉(zhuǎn)換為大寫形式
使用 HTML 命名實(shí)體替換 HTML 標(biāo)簽
For 循環(huán)
模板方法模式
重復(fù)字符串
使用 Jasmine 測(cè)試
對(duì)象的鏈?zhǔn)秸{(diào)用
數(shù)學(xué)常數(shù)
反轉(zhuǎn)數(shù)組
計(jì)算月球的相位
使用 Heregexes
查找子字符串
生成器模式
遞歸函數(shù)
HTTP 客戶端
創(chuàng)建 jQuery 插件
檢測(cè)與構(gòu)建丟失的函數(shù)
生成唯一ID
命令模式

基本的 HTTP 服務(wù)器

問(wèn)題

你想在網(wǎng)絡(luò)上創(chuàng)建一個(gè) HTTP 服務(wù)器。在這個(gè)方法中,我們將逐步從最小的服務(wù)器成為一個(gè)功能鍵值存儲(chǔ)。

解決方案

我們將使用 node.js HTTP 庫(kù)并在 Coffeescript 中創(chuàng)建最簡(jiǎn)單的 web 服務(wù)器。

開(kāi)始 'hi\n'

我們可以通過(guò)導(dǎo)入 node.js HTTP 模塊開(kāi)始。這會(huì)包含 createServer ,一個(gè)簡(jiǎn)單的請(qǐng)求處理程序返回 HTTP 服務(wù)器。我們可以使用該服務(wù)器監(jiān)聽(tīng) TCP 端口。

http = require 'http'
server = http.createServer (req, res) -> res.end 'hi\n'
server.listen 8000

要運(yùn)行這個(gè)例子,只需放在一個(gè)文件中并運(yùn)行它。你可以用 ctrl-c 終止它。我們可以使用 curl 命令測(cè)試它,可用在大多數(shù) *nix 平臺(tái):

$ curl -D - http://localhost:8000/
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked

hi

發(fā)生什么了?

讓我們一點(diǎn)點(diǎn)來(lái)反饋服務(wù)器上發(fā)生的事情。這時(shí),我們可以友好的對(duì)待用戶并提供他們一些 HTTP 頭文件。

http = require 'http'

server = http.createServer (req, res) ->
    console.log req.method, req.url
    data = 'hi\n'
    res.writeHead 200,
        'Content-Type':     'text/plain'
        'Content-Length':   data.length
    res.end data

server.listen 8000

再次嘗試訪問(wèn)它,但是這一次使用不同的 URL 路徑,比如 http://localhost:8000/coffee 。你會(huì)看到這樣的服務(wù)器控制臺(tái):

$ coffee http-server.coffee 
GET /
GET /coffee
GET /user/1337

得到的東西

假如我們的網(wǎng)絡(luò)服務(wù)器能夠保存一些數(shù)據(jù)會(huì)怎么樣?我們將在通過(guò) GET 方法 請(qǐng)求檢索的元素中設(shè)法想出一個(gè)簡(jiǎn)單的鍵值存儲(chǔ)。并提供一個(gè)關(guān)鍵路徑,服務(wù)器將請(qǐng)求返回相應(yīng)的值,如果不存在則返回 404 錯(cuò)誤。

http = require 'http'

store = # we'll use a simple object as our store
    foo:    'bar'
    coffee: 'script'

server = http.createServer (req, res) ->
    console.log req.method, req.url

    value = store[req.url[1..]]

    if not value
        res.writeHead 404
    else
        res.writeHead 200,
            'Content-Type': 'text/plain'
            'Content-Length': value.length + 1
        res.write value + '\n'

    res.end()

server.listen 8000

我們可以試試幾種 url,看看它們?nèi)绾位貞?yīng):

$ curl -D - http://localhost:8000/coffee
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 7
Connection: keep-alive

script

$ curl -D - http://localhost:8000/oops
HTTP/1.1 404 Not Found
Connection: keep-alive
Transfer-Encoding: chunked

使用你的頭文件

text/plain 是站不住腳的。如果我們使用 application/json 或 text/xml 會(huì)怎么樣?同時(shí),我們的存儲(chǔ)檢索過(guò)程也可以用一點(diǎn)重構(gòu)——一些異常的拋出 & 處理怎么樣? 來(lái)看看我們能想出什么:

http = require 'http'

# known mime types
[any, json, xml] = ['*/*', 'application/json', 'text/xml']

# gets a value from the db in format [value, contentType]
get = (store, key, format) ->
    value = store[key]
    throw 'Unknown key' if not value
    switch format
        when any, json then [JSON.stringify({ key: key, value: value }), json]
        when xml then ["<key>#{ key }</key>\n<value>#{ value }</value>", xml]
        else throw 'Unknown format'

store =
    foo:    'bar'
    coffee: 'script'

server = http.createServer (req, res) ->
    console.log req.method, req.url

    try
        key = req.url[1..]
        [value, contentType] = get store, key, req.headers.accept
        code = 200
    catch error
        contentType = 'text/plain'
        value = error
        code = 404

    res.writeHead code,
        'Content-Type': contentType
        'Content-Length': value.length + 1
    res.write value + '\n'
    res.end()

server.listen 8000

這個(gè)服務(wù)器仍然會(huì)返回一個(gè)匹配給定鍵的值,如果不存在則返回 404 錯(cuò)誤。但它根據(jù)標(biāo)頭 Accept 將響應(yīng)在 JSON 或 XML 結(jié)構(gòu)中。可親眼看一下:

$ curl http://localhost:8000/
Unknown key

$ curl http://localhost:8000/coffee
{"key":"coffee","value":"script"}

$ curl -H "Accept: text/xml" http://localhost:8000/coffee
<key>coffee</key>
<value>script</value>

$ curl -H "Accept: image/png" http://localhost:8000/coffee
Unknown format

你需要有所返回

我們的最后一步是提供客戶端存儲(chǔ)數(shù)據(jù)的能力。我們將通過(guò)監(jiān)聽(tīng) POST 請(qǐng)求來(lái)保持 RESTiness。

http = require 'http'

# known mime types
[any, json, xml] = ['*/*', 'application/json', 'text/xml']

# gets a value from the db in format [value, contentType]
get = (store, key, format) ->
    value = store[key]
    throw 'Unknown key' if not value
    switch format
        when any, json then [JSON.stringify({ key: key, value: value }), json]
        when xml then ["<key>#{ key }</key>\n<value>#{ value }</value>", xml]
        else throw 'Unknown format'

# puts a value in the db
put = (store, key, value) ->
    throw 'Invalid key' if not key or key is ''
    store[key] = value

store =
    foo:    'bar'
    coffee: 'script'

# helper function that responds to the client
respond = (res, code, contentType, data) ->
    res.writeHead code,
        'Content-Type': contentType
        'Content-Length': data.length
    res.write data
    res.end()

server = http.createServer (req, res) ->
    console.log req.method, req.url
    key = req.url[1..]
    contentType = 'text/plain'
    code = 404

    switch req.method
        when 'GET'
            try
                [value, contentType] = get store, key, req.headers.accept
                code = 200
            catch error
                value = error
            respond res, code, contentType, value + '\n'

        when 'POST'
            value = ''
            req.on 'data', (chunk) -> value += chunk
            req.on 'end', () ->
                try
                    put store, key, value
                    value = ''
                    code = 200
                catch error
                    value = error + '\n'
                respond res, code, contentType, value

server.listen 8000

在一個(gè) POST 請(qǐng)求中注意數(shù)據(jù)是如何接收的。通過(guò)在“數(shù)據(jù)”和“結(jié)束”請(qǐng)求對(duì)象的事件中附上一些處理程序,我們最終能夠從客戶端緩沖和保存數(shù)據(jù)。

$ curl -D - http://localhost:8000/cookie
HTTP/1.1 404 Not Found # ...
Unknown key

$ curl -D - -d "monster" http://localhost:8000/cookie
HTTP/1.1 200 OK # ...

$ curl -D - http://localhost:8000/cookie
HTTP/1.1 200 OK # ...
{"key":"cookie","value":"monster"}

討論

給 http.createServer 一個(gè)函數(shù) (request,response) - >…… 它將返回一個(gè)服務(wù)器對(duì)象,我們可以用它來(lái)監(jiān)聽(tīng)一個(gè)端口。讓服務(wù)器與 request 和 response 對(duì)象交互。使用 server.listen 8000 監(jiān)聽(tīng)端口 8000。

在這個(gè)問(wèn)題上的 API 和整體信息,參考 node.js httphttps 文檔頁(yè)面。此外,HTTP spec 可能派上用場(chǎng)。

練習(xí)

在服務(wù)器和開(kāi)發(fā)人員之間創(chuàng)建一個(gè)層,允許開(kāi)發(fā)人員做類似的事情:

server = layer.createServer
    'GET /': (req, res) ->
        ...
    'GET /page': (req, res) ->
        ...
    'PUT /image': (req, res) ->
        ...