Node.js 系列 - 搭建路由 & 处理表单提交

做为还在漫漫前端学习路上的一位自学者。我以学习分享的方式来整理本身对于知识的理解,同时也但愿可以给你们做为一份参考。但愿可以和你们共同进步,若有任何纰漏的话,但愿你们多多指正。感谢万分!html


以前, 咱们搭建了静态文件服务器. 用户经过在浏览器搜索栏输入 URL 来请求保存在服务器的指定文件. 可是除了提供静态文件, 服务器能作的还有不少不少. 在这一篇, 咱们要学会用 Node.js 处理从前端页面的 HTML 表单中提交的信息.前端

搭建路由

在平常, 咱们访问一个站点的不一样地址时, 一般页面内容也随之改变. 这是由于服务器为了实现更多的功能, 其会根据请求 URL 的不一样而作出不一样的处理, 这被称做 "路由"npm

『 路由 』简单来讲就是 请求和请求处理代码之间的映射关系. 当服务器为一个特定 URL 挂在了请求处理代码时, 全部针对于这个特定 URL 的请求都会交由其处理.json

假设咱们要作一个用于自我介绍的我的网页, 其包含: "主页". "项目介绍页面", "关于我页面".浏览器

那么咱们能够像下面代码中那样来搭建路由规则:bash

// 引入相关模块
var http = require('http');
var url = require('url');

// 搭建 HTTP 服务器
var server = http.createServer(function(req, res) {
    // 获取请求 URL, 根据 URL 中的 pathname 来匹配对应的处理方法.
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;

    switch (urlPathname) {
        case "/main":
            // 由于返回内容中有中文, 因此别忘了指定编码方式
            res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
            res.write("主页页面");
            res.end();
            break;
        case "/aboutme":
            res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
            res.write("关于我页面");
            res.end();
            break;
        case "/projects":
            res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
            res.write("项目介绍页面");
            res.end();
            break;
        // 若是都不匹配就返回 404 
        default:
            res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
            res.write("404 - Not Found");
            res.end();
            break;
    }
});

// 在 3000 端口监听请求
server.listen(3000, function() {
    console.log("服务器运行中.");
    console.log("正在监听 3000 端口:")
})
复制代码

上面代码根据请求 URL 的不一样, 而将请求交给不一样的处理代码. 你能够尝试运行服务器, 而后用浏览器去请求相应的 URL, 来看看获得的响应是什么.服务器

123


获取 GET 表单提交

在学习了路由相关知识以后, 咱们再来了解一下如何获取从客户端发过来的 表单提交.app

咱们先介绍用 GET 方法提交的表单. 经过 GET 提交的表单内容会组装成『 查询字符串 』嵌入在请求 URL 里. 例以下面这段:函数

https://www.zhihu.com/search?type=content&q=罐装汽水Garrik
复制代码

? 问号开始就是这段 URL 的查询字符串; 参数之间用 & 分开; = 等号前面的是参数名, 后面的是参数值.post

上面这段 URL 的查询字符串如何解析成 JSON 的话就是:

{
    "type": "content",
    "q": "罐装汽水Garrik"
}
复制代码

那么再简单了解了基础知识以后呢, 就让咱们赶快来写代码吧!

首先让咱们来写一个有 HTML 表单的页面, 而后命名为 login.html (固然你也能够按照你的想法写代码和命名)

这个表单我想用来提交登陆信息, form 元素的 action 属性我定义为 login, 意思是将请求发送到 login 这段路径下. method 属性我定义为 get, 意思是以 GET 方法提交表单.

<body>
    <form action="login" method="get">
        帐户: <input type="text" name="username" />
        <br /> 
        密码: <input type="text" name="password" />
        <br />
        <input type="submit" value="提交">
    </form>
</body>
复制代码

以后再让咱们来写服务器代码. 经过前面的介绍, 你知道咱们须要解析 URL 的查询字符串. 作到这点很简单, 只须要在调用 url.parse 函数解析请求 URL 时为其传入第二个参数 true. 这个函数就会自动帮你把 URL 的查询字符串解析成一个 JavaScript 对象了, 保存在函数返回对象的 query 属性中. 若是没有查询的话属性值就是 null

咱们能够用路由去匹配路径, 当请求 URL 的路径和表单发送的路径相匹配时, 将请求交给特定代码去处理.

var server = http.createServer(function(req, res) {
    // 解析请求 URL
    var urlObj = url.parse(req.url, true);
    // 获取请求 URL 的路径
    var urlPathname = urlObj.pathname;
    // 获取请求 URL 的查询字符串解析成的对象
    var queryObj = urlObj.query;
    
    // 路由
    switch (urlPathname) {
        // 响应 login 页面
        case "/":
        case "":
            // 我用了静态服务器那篇的模块, 不了解的地方能够去那篇参考
            readStaticFile(res, "./login.html");
            break;
        // 响应查询对象的 JSON 形式到浏览器 
        case "/login":
            res.writeHead(200, { "Content-Type": "text/plain" });
            res.write(JSON.stringify(queryObj));
            res.end();
            break;
        // 错误处理
        default:
            readStaticFile(res, "./404.html");
    }
});
复制代码

当运行起服务器以后, 访问 login 页面, 提交表单你看到的应该像是下面这样:

Screen Shot 2018-10-09 at 12.49.09 AM

Screen Shot 2018-10-09 at 12.49.20 AM


获取 POST 表单提交

说完 GET, 咱们再来讲说用 POST 方法提交表单. 不一样于用 GET 方法时, 提交的内容都包含在 URL 里. POST 提交的内容所有的都在请求体中.

咱们 HTTP 服务器 http.createServer 接收的请求对象 req 并无一个属性内容为请求体. 缘由是 POST 请求体可能体积很是大, 若是每次接收请求都包含请求体的话会很耗时. 并且万一遇到了恶意 POST 请求攻击, 服务器的资源就被大大地浪费了.

为了获取 POST 请求体, 咱们须要手动来操做. 由于 POST 请求数据量可能很大, 因此它被拆分红了不少个小数据块 ( chunk ) 咱们经过在服务器监听请求对象 req 的 'data' 事件来一个个地接收这些数据块, 并将其拼接在一块儿.

当请求传输完毕, 会触发请求对象 req 的 'end' 事件. 咱们须要监听它, 事件触发后, 在其事件处理函数中解析 POST 的请求体.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url, true);
    var urlPathname = urlObj.pathname;

    switch (urlPathname) {
        case "/":
        case "":
            readStaticFile(res, "./login.html");
            break;
        case "/login":
            // 当请求方法为 POST 时触发
            if (req.method === 'POST') {
                // 用于保存拼接后的请求体
                var post = '';
                // 'data' 事件触发, 将接受的数据块 chunk 拼接到 post 变量上
                req.on('data', function(chunk) {
                    post += chunk;
                });
                // 请求完毕, 'end' 事件触发
                req.on('end', function() {
                    // querystring 是 Node.js 自带模块, parse 方法用于将查询字符串解析成对象
                    var queryObj = querystring.parse(post);
                    // 将接收的 POST 请求体以 JSON 格式响应回客户端
                    res.writeHead(200, { "Content-Type": "text/plain" });
                    res.write(JSON.stringify(queryObj));
                    res.end();
                });
            }
            break;
        default:
            readStaticFile(res, "./404.html");
    }
});
复制代码

对了, 最重要的一点, 别忘了将 login.html 文件中的表单提交方法从 get 改为 post

<body>
    <form action="login" method="post">
        <!-- 省略了 -->
    </form>
</body>
复制代码

如今运行服务器, 提交表单, 看看结果是什么. 应该效果像下图所示:

Screen Shot 2018-10-09 at 12.49.09 AM

Screen Shot 2018-10-09 at 10.43.14 AM


POST 文件上传

文件上传咱们能够很方便的用第三方模块 formidable 来实现.

首先用 npm 来安装模块:

npm install formidable --save
复制代码

formidable 是用因而表单数据解析的模块, 很是适合用于文件上传的处理. 使用该模块时, 先要调用它的 IncomingForm 构造函数初始模块. 该函数返回一个 IncomingForm 实例用于解处理表单提交数据. 以后经过调用该实例的 parse 方法来解析数据.

当用户使用表单提交数据时,表单中可能会包含两类数据: 普通表单数据, 文件数据. parse 方法解析时,会将这两种数据分别放到fieldsfiles 这两个回调参数中.

那么很少废话直接上代码:

// 模块引入
var formidable = require('formidable');

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url, true);
    var urlPathname = urlObj.pathname;

    switch (urlPathname) {
        case "/":
        case "":
            readStaticFile(res, "./upload.html");
            break;
        // 路由为 '/upload'
        case "/upload":
            if (req.method === 'POST') {
                // 初始化 formidable 的 IncomingForm 实例
                var form = new formidable.IncomingForm();

                // uploadDir 设置上传文件时临时文件存放的位置
                form.uploadDir = "./uploads";
                // keepExtensions 属性设置是否保留上传文件的扩展名, 默认为 false
                form.keepExtensions = true;
                
                // 开始解析
                form.parse(req, function(err, fields, files) {
                    if (err) {
                        var message = "文件解析失败";
                    } else {
                        var message = "文件上传成功";
                    }
                    res.writeHead(200, { "Content-Type": "text/plain;charset=utf-8" });
                    res.write(message);
                    res.end();
                })
            }
            break;
        default:
            readStaticFile(res, "./404.html");
    }
});
复制代码

服务器代码写完后, 让咱们写 upload.html 文件:

<body>
    <form action="upload" enctype="multipart/form-data" method="post">
        <input type="file" name="upload" />
        <br />
        <input type="submit" value="提交">
    </form>
</body>
复制代码

注意要设置的表单的编码方式 enctype"multipart/form-data" 表单数据默认的编码方式为 "application/x-www-form-urlencoded" 不可用于文件上传. 在使用包含文件上传控件的表单时,必须使用 "multipart/form-data" 这个值.

写好后, 运行服务器, 上传一张你喜欢的照片, 看看结果是什么. 如下是个人操做:

Screen Shot 2018-10-09 at 11.54.45 AM

Screen Shot 2018-10-09 at 11.55.17 AM

能够看到照片已经上传到了 uploads 目录下.

GET vs POST

前面分别用 GET 和 POST 方法提交了表单, 那么这两种方法到底区别是什么呢?

先来看看 MDN 对这两个方法的定义:

  • 『 HTTP GET 方法 』: 请求指定的资源. 使用 GET 的请求应该只用于获取数据
  • 『 HTTP POST 方法 』: 发送数据给服务器

上面说的已经很简洁, 当你想要请求服务器上的资源时用 GET 方法. 发送数据时用 POST 方法. 像我以前用 GET 方法提交登陆信息, 是不符合规范的.  实际开发中, 这种行为不容许出现.

说完定义, 让咱们再来看看这两种方法在表现上有什么不一样.

  • 善于观察的你必定已经发现, GET 提交的表单数据显式地添加在了请求 URL 的查询字符串中. 而 POST 把提交的数据放置在了请求体中. 这也体现出为何 GET 不能用于传输数据, 你总不但愿你的帐号和密码这么明显地暴露在 URL 里吧.

  • 由于浏览器对 URL 的长度都有限制, 因此 GET 方式提交的数据是有大小限制的, 通常不超过 1024 字节. 理论上讲, POST 提交数据时没有大小限制的. 但出于性能考虑, 服务器接收时可能对 POST 传输的数据大小进行限制.


😆 好啦,今天的分享就告一段落啦。下一篇中,我会介绍 "模板引擎"

若是喜欢的话就点个关注吧!O(∩_∩)O 谢谢各位的支持❗️

相关文章
相关标签/搜索