前端點滴(Node.js)(五)---- 構建 Web 應用(二)數據上傳

Node.js

2. 數據上傳

在實際的業務中,我們往往需要接收一些數據,比如表單數據、文件提交、Json上傳、XML上傳等等。

Node的http模塊只對HTTP報文頭部進行了解析,然後觸發request事件。如果請求中還帶有內容部分(比如POST報文,它具有請求頭以及請求體),內容部分需要用戶自行接收和解析。通過請求頭的Transfer-Encoding和Content-Length可以判斷請求中是否存在內容。

實踐:

var http = require('http');
var hasBody = function (req) {
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};
var server = http.createServer();
server.on('request', (req, res) => {
    if (hasBody(req)) {
        var buffers = [];
        req.on('data', function (chunk) {
            buffers.push(chunk);
            // res.end(Buffer.from(chunk))  //=> name=Errrl&age=20
        });
        req.on('end', function () {
            req.rawBody = Buffer.concat(buffers).toString();
            res.end(req.rawBody);    //=> name=Errrl&age=20
        });
    } else {
        res.end()
    }
}).listen(8000, () => {
    console.log('server is create')
})

 

(1)表單數據

最常見的數據提交就是通過網頁的表單提交數據到服務端或者說是控制端(C層):

<form action="/upload" method="post"> 
     <label for="username">Username:</label> <input type="text" name="username" id="username" /> 
     <br />
    <input type="submit" name="submit" value="Submit" /> 
</form>

 默認提交表單,請求頭中的Content-Type字段值爲application/x-www-form-urlencoded:

由於它的報文體內容與查詢字符串相同:

name=Errrl&age=20

所以解析起來也非常容易:

var http = require('http');
var querystring = require('querystring')
var hasBody = function (req) {
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};


var server = http.createServer();
server.on('request', (req, res) => {
    if (hasBody(req)) {
        var buffers = [];
        req.on('data', function (chunk) {
            buffers.push(chunk);
        });
        req.on('end', function () {
            req.rawBody = Buffer.concat(buffers).toString();
            if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
                req.body = querystring.parse(req.rawBody);
            }
            res.end();
            console.log(req.body);
        });
    } else {
        res.end()
    }
}).listen(8000, () => {
    console.log('server is create')
})

結果:

在後續的業務中可以直接訪問req.body獲取提交表單的數據。

(2)其他格式

除了提交表單數據外,常見的提交還有JSON文件和XML文件等,判斷和解析它們的原理比較相似,都是依據Content-Type中的值決定,其中JSON類型的值爲application/json,XML的爲application/xml。

需要注意的是,在Content-Type中可能還要附帶如下的編碼信息:

Content-Type:application/json;charset=utf-8;

因此在做判斷時,需要注意區分:

var mime = function(rq){
    var str = req.headers['content-type']||'';
    return str.split(';')[0];
}

1. JSON 文件

如果從客戶端提交JSON文件內容,對於Node來說,要處理它是不需要額外的庫的,操作如下:

/**處理上傳的 Json 文件 */
var http = require('http');
var hasBody = function (req) {
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};
var mime = function(req){
    var str = req.headers['content-type']||'';
    return str.split(';')[0];
}

var server = http.createServer();
server.on('request', (req, res) => {
    if (hasBody(req)) {
        var buffers = [];
        req.on('data', function (chunk) {
            buffers.push(chunk);
        });
        req.on('end', function () {
            req.rawBody = Buffer.concat(buffers).toString();
            if (mime(req) === 'application/json') { 
                try { 
                    req.body = JSON.parse(req.rawBody); 
                } catch (e) { 
                    // 異常內容Lj響應Bad request 
                    res.writeHead(400); 
                    res.end('Invalid JSON'); 
                    return; 
                }
                console.log(req.body)
                res.end() 
            }
        });
    } else {
        res.end()
    }
}).listen(8000, () => {
    console.log('server is create')
})

2. XML 文件

解析XML文件就稍微複雜一點,需要引入第三方插件xml2js :

/**處理上傳的 XML 文件 */
var http = require('http');
var xml = require('xml2js');
var hasBody = function (req) {
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};
var mime = function (req) {
    var str = req.headers['content-type'] || '';
    return str.split(';')[0];
}

var server = http.createServer();
server.on('request', (req, res) => {
    if (hasBody(req)) {
        var buffers = [];
        req.on('data', function (chunk) {
            buffers.push(chunk);
        });
        req.on('end', function () {
            req.rawBody = Buffer.concat(buffers).toString();
            if (mime(req) === 'application/xml') {
                xml.parseString(req.rawBody, function (err, xml) {
                    if (err) {
                        // 異常內容Lj響應Bad request 
                        res.writeHead(400);
                        res.end('Invalid XML');
                        return;
                    }
                    req.body = xml;
                    console.log(req.body);
                    res.end();
                });
            }
        });
    } else {
        res.end()
    }
}).listen(8000, () => {
    console.log('server is create')
})

XML文件:

<?xml version="1.0" encoding="utf-8" ?>
<root>
    <part id = "01" name="選項一">
        <name>Errrl</name>
        <age>20</age>
        <sex>男</sex>
    </part>
    <part id="02" name="選項二">
        <name>Errl</name>
        <age>22</age>
        <sex>男</sex>
    </part>
</root>

結果:

總結:

採用這種類似的方式,無論客戶端提交的是什麼格式的數據,我們都可以通過這種方式來判斷該數據是何種類型,然後採取對應的解析方式進行解析。

(3)附件上傳

除了常見的表單和特殊格式的內容提交外,還有一種比較特殊的表單,通常的表單,其內容可以通過urlencode的方式編碼內容形成報文體,在發送給服務器,但是業務場景往往需要用戶直接提交文件,在前端HTML代碼中,特殊表單與普通表單的差異在於該表單中可以含有file類型的控件,以及需要指定表單屬性enctype爲multipart/form-data,如下所示:

<form action="/" method="post" enctype="multipart/form-data"> 
     <label for="username">Username:</label> <input type="text" name="username" id="username" /> 
     <label for="file">Filename:</label> <input type="file" name="file" id="file" /> 
     <br /> 
     <input type="submit" name="submit" value="Submit" /> 
</form>

瀏覽器遇到multipart/form-data表單提交時,構造的請求報文與普通表單的報文完全不同,首先請求頭很特殊:

Content-Type: multipart/form-data; boundary=AaB03x 
Content-Length: 18231

它代表本次提交的內容由多部分構成,其中boundary=AaB03x 指的是每每部分內容的分界符,AaB03x是隨機生成的一段字符串,報文體的內容通過在它前面添加的--進行分界,報文結束時在它的前後都加上--表示結束。另外Content-Type的值必須確保報文體的長度。

接下來就看看特殊表單提交與普通表單提交的報文區別:

假設上傳一個js文件,並進行文件上傳,生成的報文如下:

--AaB03x\r\n 
Content-Disposition: form-data; name="username"\r\n 
\r\n 
Jackson Tian\r\n 
--AaB03x\r\n 
Content-Disposition: form-data; name="file"; filename="diveintonode.js"\r\n 
Content-Type: application/javascript\r\n 
\r\n 
 ... contents of diveintonode.js ... 
--AaB03x--

提交普通表單,生成的報文如下:

--AaB03x\r\n 
Content-Disposition: form-data; name="username"\r\n \r\n 
Jackson Tian\r\n 

文件控件提交時,生成的報文如下:

--AaB03x\r\n 
Content-Disposition: form-data; name="file"; filename="diveintonode.js"\r\n 
Content-Type: application/javascript\r\n 
\r\n 
 ... contents of diveintonode.js ...

一旦我們知曉報文如何構成,那麼解析起來就方便很多。值得注意的是:由於是文件上傳,那麼像普通表單、json或者xml那樣先接收內容在進行解析的方式將變得不可接受。所以接收大小、格式未知的數據量時,我們要注意謹慎操作:

/**整合 */
var http = require('http');
var xml = require('xml2js');
var formidable = require('formidable')
var hasBody = function (req) {
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};
var mime = function (req) {
    var str = req.headers['content-type'] || '';
    return str.split(';')[0];
}

var server = http.createServer();
server.on('request', (req, res) => {
    if (hasBody(req)) {
        var buffers = [];
        req.on('data', function (chunk) {
            buffers.push(chunk);
        });
        req.on('end', function () {
            req.rawBody = Buffer.concat(buffers).toString();
            if (mime(req) === 'application/json') {
                try {
                    req.body = JSON.parse(req.rawBody);
                } catch (e) {
                    // 異常內容Lj響應Bad request 
                    res.writeHead(400);
                    res.end('Invalid JSON');
                    return;
                }
                console.log(req.body)
                res.end()
            } else if (mime(req) === 'application/xml') {
                xml.parseString(req.rawBody, function (err, xml) {
                    if (err) {
                        // 異常內容Lj響應Bad request 
                        res.writeHead(400);
                        res.end('Invalid XML');
                        return;
                    }
                    req.body = xml;
                    console.log(req.body);
                    res.end();
                });
            } else if (mime(req) === 'multipart/form-data') {
                var form = new formidable.IncomingForm();
                form.parse(req, function (err, fields, files) {
                    req.body = fields;
                    req.files = files;
                    console.log(req.body);
                    console.log(req.files)
                    res.end();
                });
            }
        });
    } else {
        res.end()
    }
}).listen(8000, () => {
    console.log('server is create')
})

在此重點介紹的模塊是formidable。首先話不多說 npm install formidable。它是基於流式處理解析報文,將接收到的文件寫入系統的臨時文件夾中,並返回對應的路徑。在後期經常使用。

(4)數據庫操作 ---- 操作實例

1. 連接MySql數據庫

(1)MySQL的基本操作CURD

  • C:create 具體方法就是 INSERT INTO `users`(`id`,`name`) VALUES ([value-1],[value-2])
  • U:update 具體方法就是 UPDATE `users` SET `name`='value' WHERE id=1
  • R:read 具體方法就是 SELECT `id`, `name` FROM `users` WHERE 1 
  • D:delete 具體方法就是 DELETE FROM `users` WHERE id=value

注意:R中的where可以加也可以不加,加上就是有條件的閱讀,不加就是整體閱讀。U、D一定要加上where條件如果不加死定。

(2)安裝與連接數據庫示例

1. 將數據庫表導入到本地數據庫中,並命名新的數據庫文件test2

鏈接: https://pan.baidu.com/s/19s13smuDcTeaDTAU0UqG7Q
提取碼: 94md
複製這段內容後打開百度網盤手機App,操作更方便哦

npm install mysql

創建 js 文件連接數據庫,進行操作:

/* mysql.js */

var mysql = require('mysql');   
// 連接數據庫
var connection = mysql.createConnection({
    host:'localhost',
    user:'root',
    password:'***',   // 填自己的
    database:'test2'
});
// 開啓鏈接
connection.connect();
// 簡單測試
var  mysql = 'select * from users where id=1';
connection.query(mysql,function(err,results,fields){
    console.log(err);
    console.log(results);
    console.log(fields);
})
connection.end(); // 注意一定要斷開資源

測試:

node mysql.js 啓動

結果:

null                          
[                             
  RowDataPacket {             
    id: 1,                    
    name: '路飛',               
    nengli: '超人系橡膠果實',        
    jituan: '草帽海賊團',          
    img: ''                   
  }                           
]                             
[                             
  FieldPacket {               
    catalog: 'def',           
    db: 'test2',              
    table: 'users',           
    orgTable: 'users',        
    name: 'id',               
    orgName: 'id',            
    charsetNr: 63,            
    length: 11,               
    type: 3,                  
    flags: 20483,             
    decimals: 0,              
    default: undefined,       
    zeroFill: false,          
    protocol41: true          
  },                          
  FieldPacket {               
    catalog: 'def',           
    db: 'test2',              
    table: 'users',           
    orgTable: 'users',        
    name: 'name',             
    orgName: 'name',          
    charsetNr: 33,            
    length: 765,              
    type: 253,                
    flags: 4097,              
    decimals: 0,              
    default: undefined,       
    zeroFill: false,          
    protocol41: true          
  },                          
  FieldPacket {               
    catalog: 'def',           
    db: 'test2',              
    table: 'users',           
    orgTable: 'users',        
    name: 'nengli',           
    orgName: 'nengli',        
    charsetNr: 33,            
    length: 765,              
    type: 253,                
    flags: 4097,              
    decimals: 0,              
    default: undefined,       
    zeroFill: false,          
    protocol41: true          
  },                          
  FieldPacket {               
    catalog: 'def',           
    db: 'test2',              
    table: 'users',           
    orgTable: 'users',        
    name: 'jituan',           
    orgName: 'jituan',        
    charsetNr: 33,            
    length: 765,              
    type: 253,                
    flags: 4097,              
    decimals: 0,              
    default: undefined,       
    zeroFill: false,          
    protocol41: true          
  },                          
  FieldPacket {               
    catalog: 'def',           
    db: 'test2',              
    table: 'users',           
    orgTable: 'users',        
    name: 'img',              
    orgName: 'img',           
    charsetNr: 33,            
    length: 765,              
    type: 253,                
    flags: 4097,              
    decimals: 0,              
    default: undefined,       
    zeroFill: false,          
    protocol41: true          
  }                           
]                             

 

2. 項目初始化、安裝第三方模塊、構建html模板

新建一個目錄名稱爲:模塊化-案例;

npm init 初始化

安裝項目所需的模塊:npm install art-template mysql bootstrap jquery

index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Hero - Admin</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
    <style>
        .hero-list img {
            width: 50px;
        }
    </style>
</head>

<body>
    <header>
        <div class="page-header container">
            <h1>海賊王 <small>角色管理器</small></h1>
        </div>
    </header>
    <div class="container hero-list">
        <a class="btn btn-success pull-right" href="/add">添加英雄</a>
        <table class="table table-hover">
            <thead>
                <th>編號</th>
                <th>名稱</th>
                <th>能力</th>
                <th>團體</th>
                <th>操作</th>
            </thead>
            <tbody id="tbody">
                {{each data}}
                <tr>
                    <td>{{$value.id}}</td>
                    <td>{{$value.name}}</td>
                    <td>{{$value.nengli}}</td>
                    <td>{{$value.jituan}}</td>
                    <td>
                        <a href="#">查看</a>
                        <a href="#">修改</a>
                        <a href="#">刪除</a>
                    </td>
                </tr>
                {{/each}}
            </tbody>
        </table>
    </div>
</body>

</html>

3. 啓動項目

(1)創建http服務器並加載靜態頁面

http.js:

// 負責啓動服務器
var http = require('http');
// 獲取請求
var qingqiu = require('./http_請求處理');
var server = http.createServer();
server.listen(8080,function(err){
    console.log('請在瀏覽器中打開127.0.0.1:8080');
});

qingqiu.server(server); // 將server對象傳進http_請求處理.js中

http_請求處理.js:

var fs = require('fs');
var chuli = require('./http_處理數據');
// 導出數據(binds方法)
// binds方法設置監聽
module.exports.server = function(server){
    server.on('request',function(request,response){
        var urls = request.url;
        // 獲取請求路徑,判斷路徑請求,
        if(urls == '/'){
            // 調用yewu模塊獲取data
            var res = chuli.data;
            response.end(res);
        }else{
            // 接收靜態資源請求並按照請求路徑響應
            fs.readFile('.'+urls,function(err,data){
                response.end(data);
            });
        }
    })
}

http_處理數據.js:

var template = require('art-template');
template.defaults.root = './';
var data = template('./index.html',{data:123});
module.exports.data = data;

效果:

(2)動態獲取數據

修改 http_處理數據.js:

// 加載引入 ./http_連接數據庫.js 模塊獲取數據
var sql = require('./http_連接數據庫');
var template = require('art-template');
template.defaults.root = './';
var res = template('./index.html',{data:sql.data});
module.exports.res = res;

 http_連接數據庫.js:

var mysql = require('mysql');
var connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '***',
    database: 'test2'
});

connection.connect();

var sql = "select * from users";

connection.query(sql,function(error,res){
    console.log(res);
    exports.data = res;
});

connection.end();

結果:

存在問題:通過連接數據庫查找到的數據,對外導出時,導不出去。這導致模板數據爲空。

解決方法:利用回調函數,將數據導出。

(3)解決問題

修改 http_處理數據.js:

// 加載引入 linkdb模塊獲取數據
var sql = require('./http_連接數據庫');
var template = require('art-template');
template.defaults.root = './';
// 利用回調函數 獲取linkdb得到的數據
sql.getdata(function(datas){
    // 使用模板引擎遍歷解析數據
    // 將解析好的數據導出
    module.exports.res = template('./index.html',{data:datas});
});

修改 http_連接數據庫.js:

var mysql = require('mysql');
// 設置連接信息
var connection = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: '',
    database: 'test2'
});
// 打開連接
connection.connect();

// 將整個查的方法導出 
// 因爲我們需要接受一個回調函數
module.exports.getdata = function (calls) {
    var sql = 'select * from users';
    connection.query(sql, function (err, sql_data) {
        var da = sql_data;
        // 調用回調函數,將數據當做實參進行函數的回調
        calls(da);
        
    })
    connection.end();
}

結果:

4. 獲取單個用戶的信息

前提:修改模板 index.html:

<td>
    <a href="/getone?id={{$value.id}}">查看</a>
    <a href="/setone?id={{$value.id}}">修改</a>
    <a href="/delone?id={{$value.id}}">刪除</a>
</td>

(1)接受前臺的請求

修改 http_請求處理.js:

var fs = require('fs');
var chuli = require('./http_處理數據');
// 導出數據(server方法)
// server方法設置監聽
module.exports.server = function(server){
    server.on('request',function(request,response){
        var urls = request.url;
        if(urls == '/'){
            // 調用chuli模塊獲取data
            var res = chuli.data;
            response.end(res);
        }else if(urls == '/get_one'){
            res.end(1);
        }else{
            // 接收靜態資源請求並按照請求路徑響應
            fs.readFile('.'+urls,function(err,data){
                response.end(data);
            });
        }
    })
}

但是,http_請求處理模塊,無法處理前後不同類型的請求,需要我們在服務器端接收並處理客戶端發送的get以及post請求;

(2)獲取請求類型以及參數

GET 請求把所有的內容編碼到訪問路徑中,POST 請求的內容全部都在請求體中。 http.ServerRequest 並沒有一個屬性內容爲請求體,原因是等待請求體傳輸可能是一件 耗時的工作,譬如上傳文件。而很多時候我們可能並不需要理會請求體的內容,惡意的 POST 請求會大大消耗服務器的資源。所以 Node.js 默認是不會解析請求體的,當我們需要的時候, 只能手動來做 。

獲取請求類型

var http = require('http');
var server = http.createServer();

server.on('request', function(request, response) {
    // 獲取請求類型
    var method = request.method;
    console.log(method);
    response.end();
});

server.listen(8000, function() {
    console.log(' 請訪問http://localhost:8000');
});

獲取GET請求參數

var http = require('http');
var url = require('url');
var server = http.createServer();

server.on('request', function(request, response) {
    // 獲取請求類型
    var method = request.method;
    if(method=="GET"){
        //解析請求地址
        url_obj = url.parse(request.url,true);
        console.log(url_obj.pathname);
        console.log(url_obj.query);
        response.end();
    }else if(method=="POST"){
    
    }
});

server.listen(8080, function() {
    console.log(' 請訪問http://localhost:8080');
});

 使用postman進行請求測試:

獲取post請求參數

else if (method == "POST") {
        // url_obj = url.parse(request.url,true);
        // console.log(url_obj.query); 
        
        //以上代碼 無內容,失敗
        // POST請求的內容全部都在請求體中 
}

原因:手冊中明確說明爲了支持各種http應用,Node.js的HTTP API是非常底層的。它只涉及流處理與消息的解析。它把一個消息解析成消息頭與消息主體,但不解析具體的消息頭或者消息主體。因此我們需要查找更底層的網絡實現,node中的基礎網絡模塊net就派上用場了。

模仿上述各類上傳代碼:

else if (method == "POST") {
    // url_obj = url.parse(request.url,true);
    // console.log(url_obj.query); 

    //以上代碼 無內容,失敗
    // POST請求的內容全部都在請求體中 

    var data = '';
    // net 模塊中的 net.Socket 提供的data及end事件 
    // 綁定data事件獲取數據
    request.on('data', function (chunk) {
        data += chunk;
    })
    // 綁定end事件,監聽數據接受完成
    request.on('end', function () {
        // console.log(data);
        console.log(require('querystring').parse(data));
        response.end();
    })
}

 

5. 獲取單個用戶的信息

修改 http_請求處理.js 優先判斷請求類型

var fs = require('fs');
var url = require('url');
var chuli = require('./http_處理數據');

module.exports.server = function(server){
    server.on('request',function(request,response){
        var urls = url.parse(request.url,true);
        var method = request.method;
        // 獲取請求路徑,判斷路徑請求
        if(method == "GET"){
            if(urls.pathname == '/'){
                // 調用yewu模塊獲取data
                var res = chuli.res;
                response.end(res);
            }else if(urls.pathname == '/get_one'){
                // 獲取id值
                var id = urls.query.id;
                // 進行傳參(關鍵)
                // ... 
            }else{
                // 接收靜態資源請求並按照請求路徑響應
                fs.readFile('.'+urls.pathname,function(err,data){
                    response.end(data);
                });
            }
        }else if(method == "POST"){

        }
    })
}

傳參怎麼傳,這是關鍵,爲了方便操作封裝每一個處理數據方法:

修改 http_處理數據.js:

// 加載引入 ./http_連接數據庫 模塊獲取數據
var url = require('url');
var sql = require('./http_連接數據庫');
var template = require('art-template');
template.defaults.root = './';

module.exports = {
    /* 在此模塊中接受請求、解析請求、響應客戶端 */
    getAll:function(req,res){
        sql.select(function(data){
            var data = template('./index.html', { data: data });
            /* 響應 */
            res.end(data);
        })
    },
    
    getOne:function(req,res){
        /* 交給此模塊處理解析請求 */
        var id = url.parse(req.url,true).query.id;
        /* 這裏又要傳遞參數所以對 http_連接數據庫.js 每個功能進行封裝 */
        sql.where("id="+id).select(function(data){
            var data = template('./new.html', { data: data });
            /* 響應 */
            res.end(data);
        })
    }
    
}

修改 http_連接數據庫.js:

// 加載第三方插件
var mysql = require('mysql');
// 連接數據庫
var connection = mysql.createConnection({
    host:'localhost',
    user:'root',
    password:'***',
    database:'test2'
})
// 開啓鏈接
connection.connect();

module.exports = {
    where:function(wh){
        this.wh = wh; // this表示整個對象
        return this;  
    },
    select:function(callback){
        // 判斷是否存在where條件,有就根據where條件查找,沒有就整體查詢
        if(this.wh == undefined){
            /* 寫入sql語句 */
            var sql = "select * from users";
        }else{
            var sql = "select * from users where "+this.wh;
        }
        /* 根據sql語句進行查詢 */
        connection.query(sql,function(err,sql_data){
            callback(sql_data);
        });
        this.wh = undefined;   // 重置where,避免this.wh會影響後面的調用
    }
}

修改http_請求處理.js:

var fs = require('fs');
var url = require('url');
var chuli = require('./http_處理數據');

module.exports.server = function(server){
    server.on('request',function(request,response){
        var urls = url.parse(request.url,true);
        var method = request.method;
        // 獲取請求路徑,判斷路徑請求
        if(method == "GET"){
            if(urls.pathname == '/'){
                /* 傳入request,response */
                chuli.getAll(request,response);
            }else if(urls.pathname == '/getone'){
                chuli.getOne(request,response);
            }else{
                // 接收靜態資源請求並按照請求路徑響應
                fs.readFile('.'+urls.pathname,function(err,data){
                    response.end(data);
                });
            }
        }else if(method == "POST"){

        }
    })
}

./new.html模板:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <table border="1" cellspacing="0" style="width: 800px;margin: 10px auto;text-align: center">
        <thead>
            <th>編號</th>
            <th>名稱</th>
            <th>能力</th>
            <th>團體</th>
            <th>操作</th>
        </thead>
        <tbody>
            {{each data}}
            <tr>
                <td>{{$value.id}}</td>
                <td>{{$value.name}}</td>
                <td>{{$value.nengli}}</td>
                <td>{{$value.jituan}}</td>
                <td>
                    <a href="#">查看</a>
                    <a href="#">修改</a>
                    <a href="#">刪除</a>
                </td>
            </tr>
            {{/each}}
        </tbody>
    </table>
</body>

</html>

結果:

6. 鏈式操作原理解析

http_處理數據中鏈式操作數據庫的原理其實就是http_連接數據庫中where裏的return this;既保存了數據也返回了對象本身。

7. 修改用戶信息

怎麼修改:

1)獲取原數據(GET請求)

2)修改數據(POST請求)

前提

添加新的模板 upuser.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        table {
            margin: 0 auto;
            border-collapse: collapse;
            width: 800px;
            height: 500px;
        }

        td {
            border: 1px solid #ccc;
        }
    </style>
</head>

<body>
    <form action="/setone?id={{data[0].id}}" method="post">
        <table>
            <tr>
                <td>姓名</td>
                <td><input name="name" type="text" value="{{data[0].name}}"></td>
            </tr>
            <tr>
                <td>能力</td>
                <td><input name="nengli" type="text" value="{{data[0].nengli}}"></td>
            </tr>
            <tr>
                    <td>團體</td>
                    <td><input name="jituan" type="text" value="{{data[0].jituan}}"></td>
                </tr>
            <tr>
                <td>上傳圖片</td>
                <td><input type="file"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="確認修改"></td>
            </tr>
        </table>
    </form>
</body>

</html>

(1)http_請求處理.js 中添加路徑

/* method=="GET" */
else if(urls.pathname == '/setone'){
     chuli.setone_get(request,response);
}
/* method=="POST" */
if(urls.pathname == '/setone'){
     chuli.setone_post(request,response);
}

(2)http_處理數據.js 中封裝數據處理方法

setone_get:function(req,res){
    var id = url.parse(req.url, true).query.id;
    sql.where('id='+id).select(function(datas){
        var data = template('./upuser.html',{ data: datas });
        res.end(data);
    })
},
setone_post:function(req,res){
    var id = url.parse(req.url, true).query.id;
    /* 底層中獲取請求體數據 */
    var datas = '';
    req.on('data',function(data){
        datas+=data;
    });
    req.on('end',function(){
        sql.where('id='+id).update(require('querystring').parse(datas),function(data){
            // console.log(data);  // 此處返回的是一個OkPacket對象
            if(data.affectedRows>=1){  // 更新行爲
                var backstr = "<script>alert('修改成功');window.location.href='/'</script>" // 彈出窗口,並回到頁面
                res.setHeader('Content-type','text/html;charset=utf-8');
                res.end(backstr);
            }
        });
    })
}

(3)http_連接數據庫.js 中封裝數據庫方法

update:function(data,callback){
        console.log(data); //=> {name:'路飛',nengli:'超人系橡膠果實',jituan:'草帽海賊團'}
        var set = '';
        for(i in data){    //將對象組裝成string字符串
            set+=i+"='"+data[i]+"',";
        }
        set = set.slice(0,set.length-1);   //string的slice方法,剪切
        // slice(初始位置,結束位置)
        
        //=> 結果:name='路飛',nengli='超人系橡膠果實',jituan='草帽海賊團'
        if(this.wh == undefined){
            console.log('滾,一定要加where');
            return;
        }else{
            var sql = "update users set "+set+" where "+this.wh
        }
        connection.query(sql,function(err,sql_data){
            callback(sql_data);
        })
        this.wh = undefined; 
    },

8. 刪除用戶信息

(1)http_請求處理.js 中添加路徑

/* method == 'GET' */
else if(re_url_pn == '/delone'){
    chuli.delone(request,response);
}

 (2)http_處理數據.js 中封裝數據處理方法

delone:function(req,res){
    var id = url.parse(req.url, true).query.id;
    sql.where('id='+id).delete(function(data){
        if(data.affectedRows>=1){   
            var backstr = "<script>alert('刪除成功');window.location.href='/'</script>" // 彈出窗口,並回到頁面
            res.setHeader('Content-type','text/html;charset=utf-8');
            res.end(backstr);
        }
    })
},

 (3)http_連接數據庫.js 中封裝數據庫方法

delete:function(callback){
    if(this.wh == undefined){
         console.log('滾,一定要加where');
         return;
    }else{
         var sql = "delete from users where "+this.wh;
    }
    connection.query(sql,function(err,sql_data){
        callback(sql_data);
    })
    this.wh = undefined;
}

9. 添加、上傳用戶信息

待後續,Express框架中提及.......。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章