1. 程式人生 > 實用技巧 >Node.js 學習筆記之三:構建 Web 服務

Node.js 學習筆記之三:構建 Web 服務

示例3. 構建 Web 伺服器

這部分示例將致力於用 Node.js 模擬一個類似於 Apache 的 Web 伺服器,處理瀏覽器端的請求,將相關的頁面響應給瀏覽器。首先,我們要在code目錄下執行mkdir 03_webSever命令來建立用於存放這一組示例的目錄。然後執行以下步驟:

  1. code/03_webSever目錄下執行mkdir www命令,建立網站目錄,然後在其中建立index.htmlogin.htm兩個 HTML 檔案以及一個名為style.css的 CSS 檔案:

    • index.htm:

      <!DOCTYPE html>
      <html lang="zh-cn">
          <head>
              <meta charset="utf-8" />
              <link rel="stylesheet" type="text/css" href="style.css" />
              <title>首頁</title>
          </head>
          <body>
              <h1>你好,nodejs!</h1>
              <p> <a href="login.htm">請登入!</a> </p>
          </body>
      </html>
      
    • login.htm:

      <!DOCTYPE html>
      <html lang="zh-cn">
          <head>
              <meta charset="utf-8" />
              <link rel="stylesheet" type="text/css" href="style.css" />
              <title>登入頁面</title>
          </head>
          <body>
              <h1>你已經登入。。。</h1>
              <p> <a href="index.htm">回首頁!</a> </p>
          </body>
      </html>
      
    • style.css:

      body {
          background: gray;
      }
      
  2. code/03_webSever目錄下執行touch 03-webServer.js命令,建立指令碼檔案,並輸入如下程式碼:

    const http = require('http')
    const fs = require('fs')
    const server = http.createServer()
    
    server.on('request', function(req, res) {
        const webRoot = './www'
        const url = req.url
        if ( url === '/' ) {
            url = '/index.htm'
        }
    
        fs.readFile(webRoot+url, function(err, data) {
            if ( err !== null ) {
                console.error('錯誤資訊:' + err.message)
                return res.end('<h1>404 頁面沒找到!</h1>')
            }
            res.end(data)
        })
    })
    
    server.listen(8080, function(){
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  3. 儲存所有檔案後,在code/03_webSever目錄下執行node 03-webServer.js命令,然後開啟瀏覽器並訪問http://localhost:8080/,就會看到如下頁面:

示例4. 使用art-template模版引擎生成網頁

這一部分本示例將以生成個人資訊頁面為例,演示在伺服器端基於 Node.js 使用art-template模板引擎來生成網頁。為此,我們需要在code目錄下執行mkdir 04_templatingEngine命令來建立用於存放這一組示例的目錄。

1. 單模版渲染

首先來示範一下如何使用art-template模版引擎的渲染單一模版檔案,請跟著以下步驟來構建示例:

  1. code/04_templatingEngine目錄下執行npm install art-template --save命令,安裝將art-template包安裝到當前示例專案中。

  2. code/04_templatingEngine目錄下執行touch singleTpl.htm命令,建立一個模版檔案,並在其中輸入以下程式碼:

    <!DOCTYPE html>
    <html lang="zh-cn">
        <head>
            <meta charset="utf-8" />
            <link rel="stylesheet" type="text/css" href="style.css" />
            <title>{{ name }}的個人資訊</title>
        </head>
        <body>
            <h1>{{ name }}的個人資訊</h1>
            <table>
                <tr><td>姓名:</td><td>{{ name }}</td></tr>
                <tr><td>年齡:</td><td>{{ age }}</td></tr>
                <tr><td>性別:</td><td>{{ sex }}</td></tr>
                <tr>
                    <td>愛好:</td>
                    <td>{{ each items }} {{ $value }} {{ /each }}</td>
                </tr>
            </table>
        </body>
    </html>
    
  3. code/04_templatingEngine目錄下執行touch 04-useTemplating_engine.js命令,建立一個指令碼檔案,具體如下:

    const http = require('http')
    const fs = require('fs')
    const template = require('art-template')
    
    class human {
        constructor(name, age, sex, items=[])
        {
            this.name  = name
            this.age   = age
            this.sex   = sex
            this.items = items
        }
    }
    
    const server = http.createServer()
    
    server.on('request', function(req, res){
        const url = req.url
        let boy = null
        if ( url === '/' ) {
            boy = new human('凌傑', '37', '男', ['看書', '看電影','旅遊'])
        } else if ( url === '/wang' ) {
            boy = new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
        }
    
        if ( boy === null ) {
            return res.end('<h1>404 頁面沒找到!</h1>')
        }
    
        fs.readFile('./singleTpl.htm', function(err, data){
            if ( err !== null ) {
                return res.end('<h1>404 沒找到模版檔案!</h1>')
            }
    
            const strHtml = template.render(data.toString(), {
                name : boy.name,
                age  : boy.age,
                sex  : boy.sex,
                items: boy.items
            })
    
            res.end(strHtml)
        })
    })
    
    server.listen(8080, function(){
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  4. 儲存所有檔案後,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine.js命令,然後開啟瀏覽器並訪問http://localhost:8080/wang,就會看到如下頁面:

2. 多模版組合渲染

在同一 Web 應用中,所有的頁面通常都由相同的頭部和底部元素,所以為了減少程式碼的冗餘,提高重用率,開發者們通常會考慮將重複的部分獨立成一個單獨的模版檔案,然後用相互包含的方式組合成頁面。下面就繼續以art-template模板引擎為例來演示一下如何將多個模版組合渲染成單一的 HTML 頁面,其具體步驟如下:

  1. code/04_templatingEngine目錄下執行touch tpl1.art tpl2.art命令,建立兩個模版檔案,然後在這兩個檔案中分別輸入以下程式碼:

    • tpl1.art :

      <header>
         <h1>檢視個人資訊</h1>
         <br>
      </header>
      
    • tpl2.art :

      <footer>
         <div>
             <p>&copy; 2016 owlman.org;本站系純HTML5站點。</p>
         </div>
      </footer>
      
  2. code/04_templatingEngine目錄下執行touch multiTpl.htm命令建立用於組合的 HTML 頁面檔案,並在其中輸入以下程式碼:

    <!DOCTYPE html>
    <html lang="zh-cn">
         <head>
             <meta charset="utf-8" />
             <link rel="stylesheet" type="text/css" href="style.css" />
             <title>檢視個人資訊</title>
         </head>
         <body>
             {{ include './tpl1.art' }}
             <h2>{{ name }}的個人資訊</h2>
             <table>
                 <tr><td>姓名:</td><td>{{ name }}</td></tr>
                 <tr><td>年齡:</td><td>{{ age }}</td></tr>
                 <tr><td>性別:</td><td>{{ sex }}</td></tr>
                 <tr>
                     <td>愛好:</td>
                     <td>{{ each items }} {{ $value }} {{ /each }}</td>
                 </tr>
             </table>
             {{ include './tpl2.art' }}
         </body>
     </html>
    
  3. code/04_templatingEngine目錄下執行cp 04-useTemplating_engine.js 04-useTemplating_engine2.js命令,將之前的程式碼複製一份,並修改如下:

    const http = require('http')
    const fs = require('fs')
    const template = require('art-template')
    
    template.defaults.root = __dirname // 配置模版的查詢根目錄
    
    class human {
        constructor(name, age, sex, items=[])
        {
            this.name  = name
            this.age   = age
            this.sex   = sex
            this.items = items
        }
    }
    
    const server = http.createServer()
    
    server.on('request', function(req, res){
        const url = req.url
        let boy = null
        if ( url === '/' ) {
            boy = new human('凌傑', '37', '男', ['看書', '看電影','旅遊'])
        } else if ( url === '/wang' ) {
            boy = new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
        }
    
        if ( boy === null ) {
            return res.end('<h1>404 頁面沒找到!</h1>')
        }
    
        fs.readFile('./multiTpl.htm', function(err, data){ // 修改了要讀取的模版檔案
            if ( err !== null ) {
                return res.end('<h1>404 沒找到模版檔案!</h1>')
            }
    
            const strHtml = template.render(data.toString(), {
                name : boy.name,
                age  : boy.age,
                sex  : boy.sex,
                items: boy.items
            })
    
            res.end(strHtml)
        })
    })
    
    server.listen(8080, function(){
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  4. 儲存所有檔案後,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine2.js命令,然後開啟瀏覽器並訪問http://localhost:8080,就會看到如下頁面:

3. 多模版繼承渲染

當然,如果重複的元素只有頭部和尾部的話,有時候使用模版繼承語法來渲染頁面會是一個更好的選擇,下面就來繼續演示一下art-template模板引擎的繼承語法來渲染 HTML 頁面,其具體步驟如下:

  1. code/04_templatingEngine目錄下執行touch baseTpl.art命令,建立父模版檔案,然後在該檔案中輸入以下程式碼:

    <!DOCTYPE html>
    <html lang="zh-cn">
        <head>
            <meta charset="utf-8" />
            <link rel="stylesheet" type="text/css" href="style.css" />
            <title>{{ name }}的個人資訊</title>
         </head>
         <body>
            <header>
               <h1>檢視個人資訊</h1>
               <br>
            </header>
    
             {{ block 'message' }}
             {{ /block }}
    
            <footer>
                <div>
                    <p>&copy; 2016 owlman.org;本站系純HTML5站點。</p>
                </div>
            </footer>
         </body>
    </html>
    
  2. code/04_templatingEngine目錄下執行touch extendTpl.htm命令,建立子模版檔案,然後在該檔案中輸入以下程式碼:

    {{ extend 'baseTpl.art' }}
    
    {{ block 'message' }}
    <h1>{{ name }}的個人資訊</h1>
    <table>
        <tr><td>姓名:</td><td>{{ name }}</td></tr>
        <tr><td>年齡:</td><td>{{ age }}</td></tr>
        <tr><td>性別:</td><td>{{ sex }}</td></tr>
        <tr>
            <td>愛好:</td>
            <td>{{ each items }} {{ $value }} {{ /each }}</td>
        </tr>
    </table>
    {{ /block }}
    
  3. code/04_templatingEngine目錄下執行cp 04-useTemplating_engine.js 04-useTemplating_engine3.js命令,將之前的程式碼複製一份,並修改如下:

    // 用Node.js生成動態頁面
    // 作者:owlman
    // 時間:2019年07月12日
    
    const http = require('http')
    const fs = require('fs')
    const template = require('art-template')
    
    template.defaults.root = __dirname
    
    class human {
        constructor(name, age, sex, items=[])
        {
            this.name  = name
            this.age   = age
            this.sex   = sex
            this.items = items
        }
    }
    
    const server = http.createServer()
    
    server.on('request', function(req, res) {
        const url = req.url
        let boy = null
        if (url === '/') {
            boy = new human('凌傑', '37', '男', ['看書', '看電影','旅遊'])
        } else if (url === '/wang') {
            boy = new human('蔓兒', '25', '女', ['看書', '看電影','寫作'])
        }
    
        if (boy === null) {
            return res.end('<h1>404 頁面沒找到!</h1>')
        }
    
        fs.readFile('./extendTpl.htm', function(err, data) {
            if ( err !== null ) {
               return res.end('<h1>404 沒找到模版檔案!</h1>')
            }
    
            const strHtml = template.render(data.toString(), {
                name : boy.name,
                age  : boy.age,
                sex  : boy.sex,
                items: boy.items
            })
    
            res.end(strHtml)
        })
    })
    
    server.listen(8080, function(){
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  4. 儲存所有檔案後,在code/04_templatingEngine目錄下執行node 04-useTemplating_engine3.js命令,然後開啟瀏覽器並訪問http://localhost:8080,就會看到與之前相同的頁面。

示例5. Web 表單處理

這一部分示例將致力於演示用 Node.js 處理 Web 表單,我們將會分別示範如何用getpost兩種方法來處理表單的請求。首先,我們要在code目錄下執行mkdir 05_webForm命令來建立用於存放這一組示例的目錄。

1. get 方法

先用一個資訊查詢程式來演示一下如何處理使用get方法來發送請求的表單。首先,在code/05_webForm目錄下執行mkdir get_form命令,並執行以下步驟:

  1. code/05_webForm/get_form目錄下執行npm install art-template命令,將art-template安裝到當前示例專案中。

  2. code/05_webForm/get_form目錄下執行touch index.htm,建立一個模版檔案,具體如下:

    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
        <meta charset="UTF-8">
        <title>個人資訊查詢</title>
    </head>
    <body>
        <h1>個人資訊查詢</h1>
        <form action="/query" method="GET">
            <label for="message">請輸入要查詢的姓名:</label>
            <input type="text" name="qname" />
            <input type="submit" value="查詢" />
        </form>
        <br />
        {{ if name }}
            <table>
                <caption>{{ name }}的個人資訊</caption>
                <tr><td>姓名:</td><td>{{ name }}</td></tr>
                <tr><td>年齡:</td><td>{{ age }}</td></tr>
                <tr><td>性別:</td><td>{{ sex }}</td></tr>
                <tr>
                    <td>愛好:</td>
                    <td>{{ each items }} {{ $value }} {{ /each }}</td>
                </tr>
            </table>
        {{ else if query_error }}
            <h2>沒有找到相關資訊!</h2>
        {{ /if }}
    </body>
    </html>
    
  3. code/05_webForm/get_form目錄下執行touch app.js,建立一個指令碼檔案,具體如下:

    const http = require('http')
    const fs = require('fs')
    const url = require('url')
    const template = require('art-template')
    
    class human {
        constructor(name, age, sex, items=[])
        {
            this.name  = name
            this.age   = age
            this.sex   = sex
            this.items = items
        }
    }
    
    const db = [
        new human('凌傑', '37', '男', ['看書', '看電影','旅遊']),
        new human('蔓兒', '25', '女', ['看書', '看電影','寫作']),
        new human('張語', '32', '女', ['看書', '旅遊','繪畫'])
    ]
    
    const server = http.createServer(function(req, res){
        const query = url.parse(req.url, true)
        let obj = null
        let query_error = false
        if ( query.pathname === '/' ) {
            query_error = false
        }
        else if (query.pathname === '/query') {
            for(let i = 0; i < db.length; ++i) {
                if (db[i].name == query.query["qname"]) {
                    obj = db[i]
                }
            }
            if ( obj === null ) {
                query_error = true
            }
        } else  {
            return res.end('<h1>404 頁面沒找到!</h1>')
        }
    
        fs.readFile('./index.htm', function(err, data){
            if ( err !== null ) {
                return res.end('<h1>404 沒找到模版檔案!</h1>')
            }
    
            let strHtml = null
            if ( obj !== null ) {
                strHtml = template.render(data.toString(), {
                    name : obj.name,
                    age  : obj.age,
                    sex  : obj.sex,
                    items: obj.items,
                    query_error: query_error
                })
            } else {
                strHtml = template.render(data.toString(), {
                    name : false,
                    query_error: query_error
                })
            }
            res.end(strHtml)
        })
    })
    
    server.listen(8080, function() {
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  4. 儲存所有檔案後,在code/05_webForm/get_form目錄下執行node app.js命令,結果如下:

2. post 方法

先來演示如何處理使用post方法來發送請求的表單。首先,在code/05_webForm目錄下執行mkdir post_form命令,並執行以下步驟:

  1. code/05_webForm/get_form目錄下執行npm install art-template命令,將art-template安裝到當前示例專案中。

  2. code/05_webForm/post_form目錄下執行touch index.htm,建立一個模版檔案,具體如下:

    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
        <meta charset="UTF-8">
        <title>個人資訊管理</title>
    </head>
    <body>
        <h1>個人資訊管理</h1>
        <table>
            <caption>個人資料表</caption>
            <tr><th>姓名</th><th>年齡</th><th>性別</th><th>愛好</th></tr>
            {{ each db }}
            <tr>
                <td>{{ $value.name }} </td>
                <td>{{ $value.age }} </td>
                <td>{{ $value.sex }} </td>
                <td>{{ each $value.items }} {{ $value }} {{ /each }}</td>
            </tr>
            {{ /each }}
        </table>
    
        <form action="/add" method="POST">
        <table>
            <caption>錄入新人員</caption>
            <tr><td>姓名:</td><td><input type="text" name="uname" /></td></tr>
            <tr><td>年齡:</td><td><input type="text" name="age"></td></tr>
            <tr><td>性別:</td><td><input type="text" name="sex"></td></tr>
            <tr><td>愛好:</td><td><input type="text" name="items"></td></tr>
        </table>
        <input type="submit" value="新增" />
        </form>
    </body>
    </html>
    
  3. code/05_webForm/post_form目錄下執行touch app.js,建立一個指令碼檔案,具體如下:

    const http = require('http')
    const fs = require('fs')
    const url = require('url')
    const querystring = require('querystring')
    const template = require('art-template')
    
    class human {
        constructor(name, age, sex, items=[])
        {
            this.name  = name
            this.age   = age
            this.sex   = sex
            this.items = items
        }
    }
    
    const db = [
        new human('凌傑', '37', '男', ['看書', '看電影','旅遊']),
        new human('蔓兒', '25', '女', ['看書', '看電影','寫作']),
        new human('張語', '32', '女', ['看書', '旅遊','繪畫'])
    ]
    
    const server = http.createServer(function(req, res){
        const query = url.parse(req.url, true)
        if ( query.pathname === '/' ) {
            fs.readFile('./index.htm', function(err, data) {
                if ( err !== null ) {
                    return res.end('<h1>404 沒找到模版檔案!</h1>')
                }
    
                const strHtml = template.render(data.toString(), {
                    "db": db
                })
    
                res.end(strHtml)
            })
        }
        else if ( query.pathname === '/add' ) {
            req.on('data', function(chunk) {
                const obj = querystring.parse(chunk.toString())
                db.push(new human(
                    obj['uname'],
                    obj['age'],
                    obj['sex'],
                    obj['items'].split(','),
                ))
            })
    
            res.writeHead(302, {
                'location': `/`
            })
    
            res.end()
        } else  {
            return res.end('<h1>404 頁面沒找到!</h1>')
        }
    })
    
    server.listen(8080, function(){
        console.log('請訪問http://localhost:8080/,按Ctrl+C終止服務!')
    })
    
  4. 儲存所有檔案後,在code/05_webForm/post_form目錄下執行node app.js命令,結果如下: