天天看點

Node.js實戰應用——建構靜态檔案http伺服器

這是我第一次使用Node.js來程式設計,而且是建構一個一個靜态檔案資源通路的Http server。

我個人從事java相關的開發差不多有10年了吧,一直都是在建構後端的服務、應用。對于前端一直沒有過多的接觸。對于servlet、jsp等技術也僅限于工作的需要(包含struct等mvc架構)。

最近工作中,參與負責了公司産品Object-Centric理念的開發模式建構。利用Business model對象拉通前後端的開發體驗,通過Business Model 對象組織所有和該業務模型對象關聯的資源:實體模型、試圖模型、服務(rest)、業務校驗、資料轉換處理、頁面等。

目前由于參與到前端頁面相關技術的建構和研究,突然對這方面的技術産生了濃厚的興趣。對Node.js、AngularJS、TypeScript都有興趣深入的學習和使用。後面打算再把mangodb納入到學習計劃中,這樣就把MEAN技術拉通了:)。

下面就是我基于Node.js實作的一個簡單的靜态頁面檔案的http server,總體體驗來說,在編碼的高效性、靈活性等體驗,對比java實作會好很多。但是在類型的識别、編譯錯誤的識别、類型的提示上沒有強語言類型的java來的好。

首先來看一下整個工程的目錄結構: 

Node.js實戰應用——建構靜态檔案http伺服器

目錄說明:

HTTP_SERVER:整個工程的根目錄;

app:靜态檔案存放的目錄,對于我來說就是我“未來”期望一個web工程的根目錄,對應于java建構web應用,就是web-app了。

css:用來存放樣式定義檔案

檔案說明:

server.js:提供靜态http服務的主程式。

mine.js:鍵值對的配置檔案,定義了對不同的檔案類型的處理定義。

index.html:web app中的首頁,靜态的html的檔案。

indx.css:index.html首頁引用的層疊樣式表的定義檔案。

首先來看一下server.js的實作:

var http = require('http');
var url = require('url');
var fs = require('fs');
// 自己定義的類型定義檔案
var mine = require('./mine').types;
var path = require('path');

var port = '3000';
var hostname = '127.0.0.1';

var server = http.createServer((req, res) => {
    var pathName = url.parse(req.url).pathname;
    // url.parse會将一個請求的路徑解析為格式化的輸出
    /*
    { 
        protocol : 'http' ,
        auth : null ,
        host : 'example.com:8080' ,
        port : '8080' ,
        hostname : 'example.com' ,
        hash : null ,
        search : '?a=index&t=article&m=default',
        query : 'a=index&t=article&m=default',
        pathname : '/one',
        path : '/one?a=index&t=article&m=default',
        href : 'http://example.com:8080/one?a=index&t=article&m=default'
    }
    */
    var realPath = path.join("app", pathName);
    console.log(realPath);

    var ext = path.extname(realPath);
    ext = ext ? ext.slice(1) : 'unknow';
    // ext = ext ? ext.split('.')[1] : 'unknown';
    fs.stat(realPath, (err, stats) => {
        if (err) {
            console.log(err);
            res.writeHead(404, { 'Content-Type': 'text/plain' });
            res.write(`This request URL ${pathName} was not found on this server.`);
            res.end();
        } else {
            fs.readFile(realPath, "binary", (err, data) => {
                if (err) {
                    res.writeHead(500, { 'Content-Type': 'text/plain' });
                    res.end(err);
                } else {
                    console.log(data);
                    var contentType = mine[ext] || "text/plain";
                    res.writeHead(200, { 'Content-Type': contentType });
                    res.write(data, "binary");
                    res.end();
                }
            })
            console.log(stats);
        }
    });
});

server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}`);
})      

代碼的一點說明:

var ext = path.extname(realPath);

ext = ext ? ext.slice(1) : 'unknow';

這兩句其實是通過截取通路路徑的最後的字元串,來擷取通路的檔案類型。例如,下面的url:http://localhost:3000/index.html。通過這兩句處理之後,ext内的值是html。如果url是:http://localhost:3000/index,那麼ext的值就是unknow。

fs.stat(realPath, (err, stats) => {

}

這個裡面的處理,是我不同于網上參考實作的處理。是我自己查閱了node.js的實作,根據node.js的說明文檔自己添加的,其實就是對檔案的一個預處理,防止對檔案直接操作抛出異常。

再看一下mine.js的代碼實作:

exports.types = {
    "html": "text/html",
    "xml": "text/xml",
    "js": "text/javascript",
    "css": "text/css",
    "json": "application/json",
    "txt": "text/plain",
    "pdf": "application/pdf",
    "doc": "application/msword",
    "xls": "application/vnd.ms-excel",
    "ico": "image/x-icon",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "bmp": "image/bmp",
    "svg": "image/svg+xml",
    "tiff": "image/tiff",
    "gif": "image/gif",
    "swf": "application/x-shockwave-flash",
    "wav": "audio/x-wav",
    "wma": "audio/x-ms-wma",
    "mp3": "audio/mpeg",
    "wmv": "video/x-ms-wmv",
    "avi": "video/x-msvideo",
    "gz": "application/x-gzip",
    "manifest": "text/cache-manifest"
};      

 這個檔案沒什麼好說的,主要是還i應用在傳回時,設定httpheader的内容使用。

再看一下html,這個也是我參考了網上的實作,不過我稍微做了一點改造,增加了一點javascript的代碼做測試:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>春曉</title>
    <link href="./css/index.css" target="_blank" rel="external nofollow"  rel="stylesheet" type="text/css">
    <script>
        function displayDate() {
            document.getElementById("demo").innerHTML = Date();
        }
    </script>
</head>

<body>
    <nav>春曉</nav>
    <div id="value">
        <p>春眠不覺曉</p>
        <p>處處聞啼鳥</p>
        <p>夜來風雨聲</p>
        <p>花落知多少</p>
    </div>

    <h1>My First JavaScript</h1>
    <p id="demo">This is a paragraph.</p>
    <button type="button" οnclick="displayDate()">
Display Date</button>
</body>

</html>
           

 再把css的定義檔案貼上來,其實也是我參考網上的實作:

body{
    background-color:#222222;
}
nav {
    color:#cc0000;
    font-weight:bold;
    font-size:2em;
}
#value{
    color:#cc0000;
    font-size:2.1em;
}
           

運作server.js:

首先通過指令行,進入到工程的根目錄下:

我是在vsc(visula stuido code)中,直接選中server.js右鍵選擇〔在指令提示符中打開〕

Node.js實戰應用——建構靜态檔案http伺服器

在指令中輸入node server.js

Node.js實戰應用——建構靜态檔案http伺服器

 回車後,啟動服務,并看到日志輸出:

Node.js實戰應用——建構靜态檔案http伺服器

 在浏覽器中輸入:http://localhost:3000/index.html得到的頁面:

Node.js實戰應用——建構靜态檔案http伺服器

 就可以正常看到我們定義的app目錄下的index.html檔案,同時在這個檔案中定義的js也可以正常運作。

總結:使用node.js的确可以快速、高效的完成前端的開發,相對于java很高效。同時,對于我習慣于java開發的人來說,裡面的一些文法、寫法确實有些不太習慣。

下一步,是準備利用express這個架構來搭建相同的靜态http server的實作。

在下一步就是建構基于mongodb的小應用了,最後在引入typeScript,嘿嘿,還是挺有意思的。