本文为 Node.js 系列笔记第四篇。文章参考:nodejs 教程;《深入浅出 Node.js》;阮一峰 nodejs 博客; Node.js v16.13.0 文档
一、路由
路由(Routing)是由一个 URL (路径)和一个特定 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
简单来说,路由指的就是针对不同请求的 URL,处理不同的业务逻辑。如下图所示:
- 示例
// 路由
let myURL = new URL(req.url, 'http://127.0.0.1:3000/');
let pathname = myURL.pathname;
if (pathname == '/login') {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end("登录");
} else if (pathname == '/register') {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end("注册");
} else if (pathname == '/admin') {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end("后端业务逻辑");
} else {
res.writeHead(404, { 'Content-Type': 'text/html; charset="utf-8"' });
}
res.end();
二、EJS 模板引擎
什么是模板引擎?
模板引擎让表现层 (HTML 文档) 和业务数据分离。 通过特定的语法,来动态地用数据渲染出 HTML 文档。
HTML 文档在服务器用模板引擎根据数据动态地渲染好,然后直接将渲染完整的页面响应给客户端。浏览器加载完页面内容可以立即展示,增加了用户体验,同时也有利于 SEO 优化。
什么是 EJS 模板引擎?
EJS 是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。可以让你在不破坏 HTML 文档结构的情况下,直接在标签内书写 JavaScript 代码。
怎么使用 EJS?
它是一个第三方模块,需要通过 npm 安装 —— npm ejs 。具体使用可参考 EJS 官方网站 。
具体用法有下面几种:
let template = ejs.compile(str, options);
template(data);
// => 输出渲染后的 HTML 字符串
ejs.render(str, data, options);
// => 输出渲染后的 HTML 字符串
ejs.renderFile(filename, data, options, function(err, str){
// str => 输出渲染后的 HTML 字符串
});
EJS 有什么语法?
其实 EJS 的语法和 HTML 差不多,只要最后文件后缀名改成 .ejs
,它就成为了 EJS 模板文件。
在模板中,<% %>
括起来的内容会作为 Javascript 代码来编译;<%= %>
和 <%- %>
将括起来的变量中的数据渲染到模板。(还有一些其他的标签,后文再进行说明)
比如,下面的模板会将 list 数组中的 title 属性值用 for 循环一个个地渲染到 <li>
元素中。
<%for(var i=0; i < list.length; i++){%>
<li>
<%=list[i].title%> // 绑定数组 list 中数据 title
</li>
<%}%>
举个例子说明一下
利用 ejs.renderFile
方法可以将服务器中获取的数据 data
渲染到前台。
http.createServer(function (req, res) {
// 创建静态 WEB 服务器
routes.static(req, res, 'static');
// 路由
let myURL = new URL(req.url, 'http://127.0.0.1:3000/');
let pathname = myURL.pathname;
if (pathname == '/login') {
let msg = '数据库里面获取的数据'; // 假装这就是数据库里获取的数据
ejs.renderFile('./views/login.ejs', { msg: msg }, (err, data) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end(data);
})
}
}).listen(3000);
上述代码中,{ msg: msg }
就将数据 msg
渲染到了前台,也就是 ./views/login.ejs
中。那么,前台页面如何将后台渲染的数据进行绑定呢?这就用到了 ejs 特有的标签,如 <%=
等(后文介绍)。
下面来看前台页面 login.ejs
<!DOCTYPE html>
...
<body>
<h3>
ejs模板引擎
</h3>
<p>
<%=msg%>
</p>
</body>
</html>
这样,我们就可以看到后台数据已经被渲染到了前台页面。
同时,我们也可以看到其 HTML 结构也发生了改变。
这就是动态数据,当数据库中的数据发生改变时,页面信息也会对应改变。
下面来看 EJS 中的特有标签
<%
:脚本标签,用于流程控制(里面可以写循环语句或条件语句),无输出。比如,你可以在里面放一个 for 循环来渲染多个数据。<%_
:删除其前面的空格符<%=
:输出数据到模板(输出是转义 HTML 标签)<%-
:输出非转义的数据到模板<%#
:注释标签,不执行、不输出内容<%%
:输出字符串 ‘<%’%>
:一般结束标签-%>
:删除紧随其后的换行符_%>
:将结束标签后面的空格符删除
再来个例子感受一下
let msg = "数据库里面所获取的数据";
let list = [
{
title: '关注'
},
{
title: '推荐'
},
{
title: '热榜'
},
{
title: '吾辈问答'
},
]
// 动态加载
ejs.renderFile('./views/login.ejs', {
msg: msg,
list: list
}, (err, data) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end(data);
});
login.ejs 文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>
<%=msg%>
</h3>
<ul>
<%for(var i=0; i < list.length; i++){%>
<li>
<%=list[i].title%>
</li>
<%}%>
</ul>
</body>
</html>
实现效果
三、GET 和 POST
1. 概述
Http 定义了与服务器交互的不同方法,最基本的方法有 4 种,分别是
GET
,POST
,PUT
,DELETE
。
URL 全称是资源描述符,我们可以这样认为:一个 URL 地址,它用于描述一个网络上的资源,而 HTTP 中的GET
、POST
、PUT
、DELETE
就对应着对这个资源的查
、改
、增
、删
4 个操作。到这里,大家应该有个大概的了解了,GET
一般用于获取/查询资源信息,而POST
一般用于更新资源信息。
超文本传输协议 HTTP 设计目的是保证客户端机器与服务器之间的通信。在客户端和服务器之间进行请求-响应时,两种最常用到的方法就是 GET
和 POST
。
GET
:从指定的资源请求数据。一般用于获取数据。POST
:向指定的资源提交要被处理的数据。一般用于提交数据。
- GET
GET 请求的数据会附在 URL 中,如下图:
- POST
POST 请求通常是通过 HTML 表单发送,并返回服务器的修改结果。
<form action="/doLogin" method="post">
...
</form>
POST 通过 request body 传递参数,如下图:
- 示例
下面是一个利用 POST 提交表单数据的例子。通过 ejs 来渲染
服务器 app.js
http.createServer(function (req, res) {
routes.static(req, res, 'static');
// 路由
let myURL = new URL(req.url, 'http://127.0.0.1:3000/');
let pathname = myURL.pathname;
if (pathname == '/login') {
// 使用 ejs 渲染
ejs.renderFile("./views/form.ejs", {}, (err, data) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset="utf-8"' });
res.end(data);
})
} else if (pathname == '/doLogin') {
// 获取 POST 传值
let postData = '';
// 拿到数据片段进行拼接
req.on('data', (chunk) => {
postData += chunk;
})
// 传输结束触发
req.on('end', () => {
console.log(postData);
res.end();
})
} else {
res.writeHead(404, { 'Content-Type': 'text/html; charset="utf-8"' });
}
}).listen(3000);
form.ejs
<!DOCTYPE html>
...
<body>
<form action="/doLogin" method="post"> // 指定为 POST 请求
用户名:<input type="text" name="username">
密 码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
...
2. GET 和 POST 区别
参见 W3C 说明:w3school: GET 对比 POST
3. 其他常见问题
声明:以下内容抄录其他文章
GET 和 POST 报文上的区别 ?
先下结论,GET 和 POST 方法没有实质区别,只是报文格式不同。
GET 和 POST 只是 HTTP 协议中两种请求方式,而 HTTP 协议是基于 TCP/IP 的应用层协议,无论 GET 还是 POST,用的都是同一个传输层协议,所以在传输上,没有区别。
-
报文格式上,不带参数时,最大区别就是第一行方法名不同:
POST 方法请求报文第一行是这样的
POST /uri HTTP/1.1 \r\n
GET 方法请求报文第一行是这样的GET /uri HTTP/1.1 \r\n
-
带参数时。在约定中,GET 方法的参数应该放在 url 中,POST 方法参数应该放在 body 中。
问题 1:POST 方法比 GET 方法安全?
按照网上大部分文章的解释,POST 比 GET 安全,因为数据在地址栏上不可见。
然而,从传输的角度来说,他们都是不安全的,因为 HTTP 在网络上是明文传输的,只要在网络节点上捉包,就能完整地获取数据报文。
要想安全传输,就只有加密,也就是 HTTPS。
问题 2:GET 方法的长度限制是怎么回事?
在网上看到很多关于两者区别的文章都有这一条,提到浏览器地址栏输入的参数是有限的。
首先说明一点,HTTP 协议没有 Body 和 URL 的长度限制,对 URL 限制的大多是浏览器和服务器的原因。
浏览器原因就不说了,服务器是因为处理长 URL 要消耗比较多的资源,为了性能和安全(防止恶意构造长 URL 来攻击)考虑,会给 URL 长度加限制。