前端点滴(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框架中提及.......。

 

 

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