node.js的一個小例子

来源:http://www.cnblogs.com/binga/archive/2017/10/13/7652439.html
-Advertisement-
Play Games

這是我學習nodejs以來做的第一個小例子,很簡單,就是在第一個頁面里輸入自己的名字,在第二個頁面(有圖片)中顯示。思路和很多地方都參考了http://www.cnblogs.com/giggle/p/6287931.html 這篇文章,感謝作者。 這篇文章中說得不對的地方希望大家指正。 ...


安裝配置的環節這裡就不說了。

這是我學習nodejs以來做的第一個小例子,很簡單,就是在第一個頁面里輸入自己的名字,在第二個頁面(有圖片)中顯示。思路和很多地方都參考了http://www.cnblogs.com/giggle/p/6287931.html 這篇文章,感謝作者。

這篇文章中說得不對的地方希望大家指正。

客戶端的步驟是進入127.0.0.1:8000/login進入login.html,填寫輸入框後進入mian.html。

伺服器端的步驟是:

1.通過瀏覽器傳來的url獲取路由,判斷進入login.html,

2.用戶提交表單,在表單action方法中進入main.js,此時使用post方法,攜帶參數,並根據html中的{name}占位,利用node動態替換。

3.在遇到圖片src時,處理圖片請求。

 

先貼上兩個html文件:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="author" content="Hx2">
        <meta name="format-detection" content="telephone=no">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta name="Description" content="網頁描述"/>
        <meta name="Keywords" content="關鍵字"/>
        <title></title>
        <style>
            form {
                text-align:center;
            }
        </style>
    </head>
    <body>
        <form action="./deliver" method="post">
            賬戶:<input type="text" name="name"/><br/>
            密碼:<input type="password" name="password"/><br/>
            <input type="submit" value="提交"/>
        </form> 
    </body>
</html>
login.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="author" content="Hx2">
        <meta name="format-detection" content="telephone=no">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta name="Description" content="網頁描述"/>
        <meta name="Keywords" content="關鍵字"/>
        <title></title>
        <style>
            body {
                text-align:center;
            }
            span {
                color: brown;
            }
            img{
                width: 100%;
            }
        </style>
    </head>
    <body>
        <div>
            hello!<span>{name}</span>
        </div>
        <img src="./showImg" />
    </body>
</html>
main.js

 

首先是主文件,創建一個服務:

1 var http = require('http');
2 http.createServer(function(request,response)        {        
3         response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});        
4         if(request.url!=="/favicon.ico"){
5             
6               response.end('');    
7         }
8 }).listen(8000);        
9 console.log('Server running at http://127.0.0.1:8000/');
View Code

這段中引入node的自帶對象http。

response.writeHead是協議頭。

然後用if清除對favicon.ico的訪問,否則刷新一次就會有兩次訪問,就避免了未知的錯誤。

response.end結束請求。

listen是監聽某個埠。

 

當一個請求來到伺服器的時候,他會獲取到url的路徑來判斷接下來的操作。所以需要添加url模塊,利用這個模塊來獲得url中的相關路徑並處理。就是這句:var url = require('url');

連接到router.js文件後,此時主文件中獲取路由,通過正則處理掉多餘的 “ / ” 以正確匹配router.js裡面的方法。同時,用try catch來處理這裡可能發生的異常。

完整的main.js文件如下:

var http = require('http');
var url = require('url');
var router = require('./router.js');

http.createServer(function(request,response)        {        
        if(request.url!=="/favicon.ico"){
            pathname = url.parse(request.url).pathname;
            pathname = pathname.replace(/\//,'');
            try{//如果這一部分有錯誤,執行下麵catch
                router[pathname](request,response)
            }catch(err){
                console.info('router錯誤' + err);
                response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'}); //協議頭
                response.write(err.toString());
                response.end(''); 
            }
        }
}).listen(8000);        
console.log('Server running at http://127.0.0.1:8000/');

 

根據需求,我們需要處理的是進入login頁面的login、傳遞名字的deliver、顯示圖片的showImg。所以在router.js文件中有了個基本的骨架:

module.exports = {
    login : function(req,res){
        
    },
    deliver : function(req,res){
        
    },
    showImg : function(req,res){
        
    }
}
View Code

 

在router.js裡面有三個操作,他們有個共同點,就是都會讀取伺服器的本地文件將它寫在客戶端。這樣,把他的操作都提取出來放在一個文件中,有利於管理和調用,這裡建個optfile.js存放操作。暫定optfile.js裡面有三個方法,分別是同步讀取:readfileSync、非同步讀取:readfile、讀取圖片:readImg。具體操作等會說。

在router.js里的login中,如果是同步的話可以這樣寫,他會一步一步向下執行:

login : function(req,res){
        res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
        optfile.readfileSync('./view/login.html',res);
        res.write(data);
        res.end('');
},

但是node.js的優勢是非同步,我們需要做的是非同步的。

非同步讀取文件的方法(在下麵會說)需要一個閉包來存儲此時的request和response,來保證readFile在非同步執行的時候,這裡的request和response不會在垃圾回收機制下被自動清除。並且在閉包中加入end()就不存在當非同步還沒有執行完,主程式先執行完了報錯這種情況。

在後面的readFile方法里只需要調用這個閉包就可以了。

這個閉包就是下麵的recall(),我們在閉包中對讀取的文件進行相關的操作。

login : function(req,res){    
        response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
        function recall(data){
                res.write(data);
                res.end('');
        };
        optfile.readfile('./view/login.html',recall)
}      

在操作比較多的項目中,每一個相同操作(這裡是顯示到客戶端)都要寫一個recall函數非常麻煩,所以封裝成getRecall方法:

function getRecall(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
    function recall(data){
        res.write(data);
        res.end('');
    }
    return recall;
}

這樣,login就變得很簡潔:

login : function(req,res){
    recall = getRecall(req,res);//這個recall並不是getRecall,而是getRecall返回的閉包recall
    optfile.readfile('./view/login.html',recall)
},

在router.js里的deliver中,需要使用post或get方法傳參,這裡使用post方法:

var post = '';
req.on('data',function(chunk){
    post += chunk;
})
req.on('end',function(){
    post = querystring.parse(post);
    console.log('收到參數:' + post['name值'] + '\n');
}

這時的思路是把收到的參數顯示在main.html中,在main.js中接收參數(如果是多個就使用數組),而deliver方法就是帶著參數跳轉到main.js中。所以想到接下來的步驟應該是:

recall = getRecall(req,res);

optfile.readfile('./view/main.html',recall);

但是上面的post方法相對於上面的兩句是非同步的,非同步傳參,所以為了確保這兩句在post方法後執行,可以把他放在end事件中。

在recall函數里,他要做的就不僅僅是顯示在頁面那麼簡單,而是要尋找到帶{}標記的元素,把他替換為傳來的參數,重寫recall:

function recall(data){
    dataStr = data.toString();//字元串
    re = new RegExp('{name}','g');//new RegExp和/.../的區別是他可以在裡面直接用字元串拼接的形式表示
    dataStr = dataStr.replace(re,post['name值']);
    res.write(dataStr);
    res.end();
}
optfile.readfile('./view/main.html',recall);

如果需要傳輸多個參數,使用數組

arr = ['email','pwd'];
function recall(data){
    dataStr = data.toString();
    for(var i = 0; i < arr.length; i++){
        re = new RegExp('{' + arr[i] + '}','g');
        dataStr = dataStr.replace(re,post[arr[i]]);
    }
    res.write(dataStr);
    res.end();
}

第三個showImg,協議頭改成請求圖片的協議頭,然後讀取圖片即可:

showImg : function(req,res){
    res.writeHead(200,{'Content-Type':'image/jpeg'});
    optfile.readImg('./src/xin2.jpg',res);
}

完整的router.js:

var optfile = require('./models/optfile.js');
var url = require('url');
var querystring = require('querystring');//post需導入

function getRecall(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
    function recall(data){
        res.write(data);
        res.end('');
    }
    return recall;
}

module.exports = {
    login : function(req,res){
        recall = getRecall(req,res);//這個recall並不是getRecall,而是getRecall返回的閉包recall
        optfile.readfile('./view/login.html',recall)
    },
    deliver : function(req,res){
        var post = '';
        req.on('data',function(chunk){
            post += chunk;
        })
        req.on('end',function(){
            post = querystring.parse(post);
            console.log('收到參數:' + post['name'] + '\n');
            console.log('收到參數:' + post['password'] + '\n');
            
            function recall(data){
                dataStr = data.toString();
                re = new RegExp('{name}','g');
                dataStr = dataStr.replace(re,post['name']);
                res.write(dataStr);
                res.end();
            }
            optfile.readfile('./view/main.html',recall);
        })
    },
    showImg : function(req,res){
        res.writeHead(200,{'Content-Type':'image/jpeg'});
        optfile.readImg('./src/xin2.jpg',res);
    }
}

 

來說optfile.js了!optfile.js暫時有三個操作,同、非同步讀文件和讀取圖片。

var fs = require('fs');
module.exports = {
    readfileSync : function(path){
        var data = fs.readFileSync(path,'utf-8');
    },
    readfile : function(path,res){
        fs.readFile(path,function(err,data){
            if(err){
                console.log('讀文件異常!');
            }else{
                console.log('讀好了');
            }
        })
    },
    readImg : function(path,res){
        ...
    }
}

大概這麼寫,但是上面有說,如果想在readFile中添加一個操作,比如說需求中將data顯示在客戶端,此時可能的思路是把console.log('讀好了');替換為res.write(data) 之類。但是不對。因為readFile方法是非同步的,在非同步里,主線程執行過程中會有很多分線程來執行各種不同的操作,但分線程並不影響主線程的執行。在讀取文件的這個分線程中,如果直接執行res.write(data)會報錯,因為主線程此時可能已經執行完了。

所以這裡要調用到之前寫的閉包。雖然recall()在這個線程中執行,但原來的response並沒有被清除掉,還在等待著recall();當recall返回一個data時,原來的response接收到了,進行操作。這個閉包儲存了原來的req和res用來操作,並解決了分線程end的問題。

readfile : function(path,recall){
    fs.readFile(path,function(err,data){
        if(err){
            console.log('讀取文件分線程錯誤:'+err);
            recall('文件不存在');
        }else{
            console.log('非同步讀取文件data為:' + data.toString());
            recall(data);
        }
    })
    console.info('非同步讀取文件方法執行了');
},

如果readFile這裡非同步線程出錯,主線程可以執行,非同步可以讀取出來,但如果這裡有錯誤並且沒有回調recall('文件不存在');的話,記載圖標會一直轉,所以這裡處理異常要使用recall('文件不存在'),其中包括錯誤提示和結束線程。

readImg和文件讀取差不多,就是圖片需要用二進位流'binary'的方式讀取。

完整的optfile.js:

var fs = require('fs');
module.exports = {
    readfileSync : function(path){
        var data = fs.readFileSync(path,'utf-8');
        console.info('同步讀取文件執行完畢,data為:' + data);
    },
    readfile : function(path,recall){
        fs.readFile(path,function(err,data){
            if(err){
                console.log('讀取文件分線程錯誤:'+err);
                recall('文件不存在');
            }else{
                recall(data);
            }
        })
        console.info('非同步讀取文件方法執行了');
    },
    readImg : function(path,res){
        fs.readFile(path,'binary',function(err,imgdata){
            if(err){
                console.log('讀圖片分線程錯誤:'+ err);
                return;
            }else{
                res.write(imgdata,'binary');
                res.end('');
            }
        })
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 存放倒計時的div每隔1秒就刷新一次,結果在蘋果手機瀏覽器上面下拉時,頁面會有閃動效果。 解決辦法就是:給div定一個固定的高度,它就不會閃動了。 ...
  • 前言 頁面載入時,大致可以分為以下幾個步驟: 那麼在這整個過程中觸發了哪些常用的事件呢? document readystatechange事件 readyState 屬性描述了文檔的載入狀態,在整個載入過程中 document.readyState會不斷變化,每次變化都會觸發readystatec ...
  • 最近寫了點小東西一個滑動驗證 ...
  • JS中常用的內置函數如下: 1、eval(str):計算表達式的結果。 2、parseInt(str,n):將符串轉換成整數數字形式(可指定幾進位)。 3、parseFloat(str):將字元串轉換成符點數字形式。 4、isNaN(): 測試是(true)否(false)不是一個數字。 5、isF ...
  • 隔行變色功能,不用js,直接用css偽類就可以做,這個實例可以作為js插件開發很好的入門級實例。本文實現的隔行變色包括以下功能: 1,支持2種常用結構共存( div元素 和 表格類型 ) 2,一個頁面內支持不同模塊隔行變色,分別管理 3,可以定製的配置有: 奇/偶數行的背景顏色 特定的模塊加上隔行變 ...
  • template.js插件在表格渲染方面還是極其出色的,當數據在非同步載入後不用使用傳統的方式,在ajax裡面拼接html語句載入表格,直接用這個插件將ajax傳回來的json數組直接渲染在前端中,省下了不少時間。 我使用template.js主要是滿足項目實現為目的,不是深入探究,總結一下我在項目里 ...
  • ``` <!doctype html vue route demo Hello App! <! 使用 router link 組件來導航. <! 通過傳入 屬性指定鏈接. <! <router link 預設會被渲染成一個 標簽 Go to Foo Go to Bar <! 路由出口 <! 路由匹配 ...
  • //除法函數,用來得到精確的除法結果//說明:javascript的除法結果會有誤差,在兩個浮點數相除的時候會比較明顯。這個函數返回較為精確的除法結果。//調用:accDiv(arg1,arg2)//返回值:arg1除以arg2的精確結果function accDiv(arg1,arg2){ var ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...