Express框架学习笔记
MDN永远的神
参考资料
安装 Express – Express 中文文档 | Express 中文网 (expressjs.com.cn)
[express框架 – 小火柴的蓝色理想 – 博客园 (cnblogs.com)](https://www.cnblogs.com/xiaohuochai/p/7189074.html#:~:text=Express是一个简洁、灵活的 node.js Web 应用开发框架%2C 它提供一系列强大的特性,帮助开发者创建各种 Web 和移动设备应用。 本文将详细介绍express框架,对Express的描述,它是一个基于 Node.js 平台,快速、开放、极简的 web 开发框架。 优点是易上手、高性能、扩展性强 1、易上手:nodejs最初就是为了开发高性能web服务器而被设计出来的,然而相对底层的API会让不少新手望而却步。 express对web开发相关的模块进行了适度的封装,屏蔽了大量复杂繁琐的技术细节,让开发者只需要专注于业务逻辑的开发,极大的降低了入门和学习的成本)
Express Web Framework (Node.js/JavaScript) – 学习 Web 开发 | MDN (mozilla.org)
尚硅谷视频
官方文档
Express 4.x – API Reference – Express 中文文档 | Express 中文网 (expressjs.com.cn)
Express是一个简洁、灵活的 ,基于node.js 平台的Web 应用开发框架, 它提供一系列强大的特性,帮助开发者创建各种 Web 和移动设备应用。
Express框架建立在node.js内置的http模块上。
安装
-
初始化npm包环境
npm init
-
下载express包
npm i express -S
,现在的版本会自动保存在运行环境的,
npm i express --no-save
-
当然也可以使用yarn安装
yarn add express
Hello Express
// 引入express包
const express = require('express');
// 创建应用对象
const app = express();
// 路由的配置
app.get('/',(request,response)=>{
// send是exprss封装的,它会自动设置utf-8字符集
// 不用再设置reponse.setHeaders("content-Type",'text/html;charset=utf-8');
response.send("Hello Express");
});
// 启动服务,监听端口
app.listen(80, ()=>{
console.log('端口80监听中');
})
路由
什么是路由
路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。
之前其实就已经接触过了,http请求,我们根据路径调用指定的回调函数。所以这里是由路由来分配,交给指定的回调函数。
路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成的。
访问的路径‘/’即为router的路径。
路由的组成
第一部分:HTTP请求的方法(get或post,或者是其他的请求方法)
第二部分:URL路径
第三部分: 回调函数
代码实现
const express = require('express');
const fs = require('fs');
const app = express();
// 创建路由规则
// 如果request的请求时get类型,并且url路径是'/',那么就交个这个路由来处理
// 这样就不用写太多的if else 了
app.get('/',(request,response)=>{
response.send('路由页面')
});
app.get('/admin',(request, reponse) =>{
reponse.send('后台页面');
})
app.get('/login',(request, reponse) =>{
let body = fs.readFileSync('./2-login.html');
reponse.end(body);// 这里必须使用end
})
app.get('/register',(request, reponse) =>{
reponse.send('注册界面页面');
})
app.post('/login',(request, reponse) =>{
reponse.send('登录成功');
})
// 可以响应任意的请求
app.all('test',(request,response)=>{
response.send('任意类型');
})
app.listen('80');
登录界面html
<!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>
<!-- 这里的action建议写出这样,要不然域名一换,就不能骑做了,写出这样自动与url相拼接 -->
<form action="/login" method='post'>
<input type="text" name="uername">
<input type="password" name="pwd">
<input type="submit">
</form>
</body>
</html>
当Express服务器接收到一个HTTP请求时,它会查找已经为适当的HTTP方法和路径定义的路由
如果找到一个,Request和Response对象会被创建,并被传递给路由的回调函数
我们便可以通过Request对象读取请求,通过Response对象返回响应
内网穿透可以让别人访问自己内网
修改hosts文件的作用
路径:
C:\Windows\System32\drivers\etc\hosts
修改这个文件可以本地解析域名,具有较高的优先级。
比如这样设置
127.0.0.1 qq.com
这样设置的话,其实访问qq.com 其实就是访问的我的主机。
request对象
官方api:
Express 4.x – API Reference – Express 中文文档 | Express 中文网 (expressjs.com.cn)
Request对象是路由回调函数中的第一个参数,代表了用户发送给服务器的请求信息。
通过Request对象可以读取用户发送的请求包括URL地址中的查询字符串中的参数,和post请求的请求体中的参数。
获取请求方法
request.method
获取请求HTTP协议版本
request.httpVersion
获取请求的URL
request.url
获取请求头信息
request.headers
给headers后面加属性名,还可以获得它的属性,比如
request.headers.host
这个可以获取主机名。
获取get请求查询字符串
**属性/** 方法 |
描述 |
---|---|
request.query |
获取get请求查询字符串的参数,拿到的是 一个对象 |
request.query
返回的结果
{ vip: 'true', hh: '1' } //http://localhost/req?vip=true&hh=1
获取get请求参数路由的参数
文档原文:This property is an object containing properties mapped to the
named route “parameters”
. For example, if you have the route , then the “name” property is available as . This object defaults to .
/user/:name``req.params.name``{}
在url中使用
/:xxx
可以给那个对象添加属性。
**属性/** 方法 |
描述 |
---|---|
request.params | 获取get请求参数路由的参数,拿到的是一个对象 |
app.get('/req/:id',(request,response)=>{
// 这里给那么空对象添加id属性
console.log(request.params.id);
response.end('hhh');
});
获取post请求体
**属性/** 方法 |
描述 |
---|---|
request.body | 获取post请求体,拿到的是一个对象(要借助一个中间件) |
获取请求头中指定key对应的value
**属性/** 方法 |
描述 |
---|---|
request.get(xxxx) | 获取请求头中指定key对应的value |
除了
request.headers.xxx
来获取请求头里面的属性,还可以使用
request.get(xxx)
来获取请求头里面的属性,比如
request.get(host)
来获取请求头里面的主机名。
response对象
这个基本和nodejs元素的属性和方法相同。
Response对象是路由回调函数中的第二个参数,代表了服务器发送给用户的响应信息。
通过Response对象可以设置响应报文中的各个内容,包括响应头和响应体。
基本除了响应体,其他的都别写中文。
**属性/** 方法 |
描述 |
---|---|
response.send() | 给浏览器做出一个响应 |
response.end() | 给浏览器做出一个响应(不会自动追加响应头) |
response.download() | 告诉浏览器下载一个文件 |
response.sendFile() |
给浏览器发送一个文件里面内容(就不用手动fs了) ,地址必须是绝对路径(__dirname) |
response.redirect() |
重定向到一个新的地址(url)比如说
,它的响应状态码是302就是跳转的意思,响应头里面有个
,它的值就是浏览器跳转的地址,浏览器看到过后会自动跳转过去。 有一个实际的应用就是,登录跳转,若发现用户没有登录,则转到登录界面。 |
response.set(header,value)/setHeaders() | 自定义响应头内容 |
res.status(code)/res. setStatus | 设置响应状态码 |
res.statusMessage | 设置响应字符串(一般成功就是OK) |
有一个地方需要注意(千万要注意)
response.send()
会自动设置响应头的字符集,所以下述代码会报错
response.write('1234');
response.send('hhh');// 在write也就是发完内容给客户端之后,不能在设置响应头了,所以这里会报错。
// 所以这里只能用response.end('hhh'); 而且还要设置响应头
// response.setHeaders('Content-type','text/css;charset=utf-8');
静态资源设置
利用 Express 托管静态文件 – Express 中文文档 | Express 中文网 (expressjs.com.cn)
一定要看看上面的链接
可以用来
设置网站的根目录(经过实验看来只能放一层目录?也就是代码和public同一层级目录下)
。不用再像原生的一样,每次都需要自己拼接了,而不再需要自己设置报错了。
const express = require('express');
const app = express();
// 配置网站根目录
app.use(express.static(__dirname + '/public'));
app.get.......
// 它会先去根目录里面找有没有相关的资源,如果没有,那么就看看有没有相关的响应路由。根目录的优先级最大,若路由和根目录同名里面的文件同名,那么使用根目录里面的文件。
注意:所有文件的路径都是相对于存放目录的,因此,存放静态文件的目录名不会出现在 url 中。
网站的根目录一般放html、css、JavaScript、img、audio、音频、字体文件。这些资元一般不太变化。
对于频繁变化的规则则需要设置路由规则来响应请求。
中间件
官网文档
Writing middleware for use in Express apps – Express 中文文档 | Express 中文网 (expressjs.com.cn)
中间件(Middleware) 是一个函数,它可以访问请求对象(request), 响应对象(response) 和
web 应用中处于请求-响应循环流程中的中间件
,一般被命名为 next 的变量。
Middleware functions can perform the following tasks:
- Execute any code.
- Make changes to the request and the response objects.
- End the request-response cycle.
- Call the next middleware in the stack.
If the current middleware function does not end the request-response cycle, it must call to pass control to the next middleware function. Otherwise, the request will be left hanging.
next()
(否则,请求将会被挂起)。如果没有next,那么在这个中间件就必要完成响应。
另外,你还可以同时装在一系列中间件函数,从而在一个挂载点上创建一个子中间件栈。(先挂载,先执行)
其实感觉就是抽离了公共部分的代码,将其分装成一个函数,更加的方便多个调用,避免大量重复。比如对查询字符串的某些判断,一些每个路由都会用到的处理
一个例子(个人理解)
// 中间件就是函数
const express = require('express');
const app = express();
const f1 = function (request,reponse,next) {
console.log(1);
next();
}
const f2 = function (request,reponse,next) {
console.log(2);
next();
}
app.use(f1);
app.use(f2);
app.get('/',(request,response)=>{
response.send('中间件');
})
app.listen(80);
上述代码执行时请求一次输出
1 2
。分析一下代码,我们定义了两个函数f1 和 f2, 然后将其挂载在app上,那么当前执行的实现,先执行f1函数,调用next,执行f2函数,调用next发现没有中间件了,那么返回app.get,继续执行当前的内容。
中间件的分类
给特定的某个路由设置中间件还可以这样做:
现在只有静态资源的中间件被express内置。
比如说轮播图这些就是动态资源。
请求体参数中间件
需要使用请求体中间件来获取请求体的参数。
之前是怎么做的呢?
requset.on('data',chunk=>{
body += chunk;
});
requset.on('end',()=>{
body = querystring.parse(post);
})
现在我们可以引入一个第三方的中间件
body-parser
body-parser – npm (npmjs.com)
下载
yarn add body-parser
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
app.use(function (req, res) {
res.end(req.body)// 直接可以获取请求体对象
})
路由中间件
把所有路由都写在一个页面不方便维护和管理。
路由.js
// 安装 express
// 引入 express 包
const express = require('express');
// 创建应用对象
const app = express();
//
const router = require('./routes/router');
const adminRouter = require('./routes/admin');
// 挂载
app.use(router);
app.use(adminRouter);
//1. 创建一个 routes 文件夹
//2. 创建单独的文件 router.js
//3. 修改 router.js 中代码(四步)
//4. 主文件中引入 router.js
//5. app.use 设置中间件
// 监听端口 启动服务
app.listen(80, () => {
console.log('服务已经启动.. 端口 80 监听中....');
});
router.js
//1. 引入 express 包
const express = require('express');
//2. 创建路由器对象 router 是一个微型的 app 对象
const router = express.Router()
//3. 修改路由
router.get('/home', (request, response) => {
response.send('home 页面');
});
router.get('/home.html', (request, response) => {
response.send('<h1>home.html</h1>')
})
router.get('/videos', (request, response) => {
//数据处理 读取数据库 读取文件
response.send('电影列表页');
});
router.get('/x/cover/:id.html', (request, response) => {
//获取id
let id = request.params.id;
//获取 电影的信息 JSON
// 与 HTML 结构拼接 形成最终的 HTML (响应体)
});
//4. 暴露router对象
module.exports = router;
模板引擎
模板引擎就是为了使用用户界面与业务数据(内容)分离而产生的。简单来说,
使用模板引擎来动态渲染数据
。下面学习ESJ模板引擎。
在没有模板引擎之前,服务端的htm文件是和JavaScript一起书写的,很难做到分离。
因为来引入数据,那么就是有在js里面写html,如果不这样,那么将意味着你读取html文件后讲不知道数据需要插入的位置在哪里。
其实使用模板引擎就相当于使用一个特殊的占位符,js解析的时候可以知道需要渲染的数据在哪里就可以了。
所以需要做代码分离。
模板引擎有哪些:[推荐13款javascript模板引擎-阿里云开发者社区 (aliyun.com)](https://developer.aliyun.com/article/293805#:~:text= ICanHaz.js 是一个简单而且功能强大的客户端的 JavaScript 模板引擎。 Dotpl-JS 是一个纯javascript模板引擎%2C支持IF和FOR关键字,多循环衔套及字段渲染,跨浏览器支持。,是一个实用的javascipt工具%2C页面静态化利器! 13. EJS EJS 可以将数据和模板合并然后生成 HTML 文本。)
这里主要学习的是EJS:
EJS – 嵌入式 JavaScript 模板引擎 | EJS 中文文档 (bootcss.com)
安装
yarn add ejs
引入ejs
const ejs = require('ejs');
一、变量的拼接
语法是
<%= %>
// 调用方法,render两个参数
// str 要编译的字符串
// data 数据对象
let str = '<h1><%=msg%><h1>'
let data = {msg:'需要渲染的信息'};
const res = ejs.render(str,data);
console.log(res);
这样我们就可以吧html和js分开了
<!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>
<h1><%=msg%></h1>
</body>
</html>
// 引入ejs
const ejs = require('ejs');
const fs = require('fs');
// 调用方法,render两个参数
// str 要编译的字符串
// data 数据对象
let str = fs.readFileSync('./1.html').toString();
let data = {msg:'需要渲染的信息'};
const res = ejs.render(str,data);
console.log(res);
二、数据的遍历
可以使用for循环,但是在html中怎么使用呢?使用<%for {%> xxx <%}%>
这样基本上就在html使用JavaScript的语法了,比如forEach,if这些什么的。
html
<!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>
<ul>
<%for(let i=0;i<msg.length;i++){ %>
<li><%= msg[i] %></li> <!-- 这里子自动添加到ul中>
<%}%>
</ul>
</body>
</html>
js
// 引入ejs
const ejs = require('ejs');
const fs = require('fs');
// 调用方法,render两个参数
// str 要编译的字符串
// data 数据对象
let str = fs.readFileSync('./1.html').toString();
let data = {msg:[
"1",
"2",
"3",
"4",
"5"
]};
const res = ejs.render(str,data);
console.log(res);
做一个小案例:小说列表
html
<!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>
<ul>
<%for(let i=0;i<msg.length;i++){ %>
<li><%= msg[i] %></li>
<%}%>
</ul>
</body>
</html>
js
const express = require('express');
// 引入ejs
const ejs = require('ejs');
const fs = require('fs');
const app = express();
// 调用方法,render两个参数
// str 要编译的字符串
// data 数据对象
let data = {msg:[
"黎明之剑",
"我的治愈系游戏",
"炼狱艺术家",
"诸界末日在线"
]};
app.get('/novels',(request,response)=>{
let content = fs.readFileSync('./1.html').toString();
content = ejs.render(content,data);
response.end(content);
});
app.listen(80,()=>{
console.log("starting");
});
三、判断
<% if(xx) {%>
<%}%>
四、ejs在express中应用
设置express的模板引擎
// 设置使用的模板引擎
app.set('view engine', 'ejs');// view engine 必须写这个
// 设置模板存放的位置(html代码)
app.set('views','./views') // views 也必须这样写
使用响应
// 这是去./view 下面的寻找 play.ejs 注意后缀。 data还是数据对象
response.render('player',data);