前言
很久之前囫囵吞枣的学过一点node,最近决定用node+vue写个博客项目,所以重新学习了express的相关内容。
初始搭建
创建项目
- npm init给项目命名创建项目
- 终端进入项目,安装express依赖,npm i express
- js文件中引入express使用
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('有人请求了根路径')
})
app.listen(3000, function () {
console.log("服务器正在运行 http://localhost:3000")
});
之后终端使用node index.js(tab键补全)运行上面代码
路由和中间件
express完全是由路由和中间件构成的框架,本质上来说express应用就是为了监听不同的路径调用各种中间件,中间件本身是一个函数,也可以叫插件,只是在express里面更习惯叫做中间件
路由
路由:前端访问不同的地址,后端返回不同的数据,这就是后端路由
如果/users/login接口是登录用的,/users/reg接口是注册用的。
监听不同的路径执行不同的函数,从而处理不同的请求
这个路由一般是在app上使用的,一般称为应用级别的路由
中间件
中间件就是一个函数,带有三个参数,req,res,next
req 请求的相关信息
res 响应的相关信息
next函数 如果调用了next,就会去执行下一个匹配到路由的中间件
app.use(
function (req, res, next) {
console.log("中间件1")
res.send("中间件1")
next()
},
function (req, res, next) {
console.log("中间件2")
next()
},
function (req, res, next) {
console.log("中间件3")
});
终端全部显示
注意下面这样两个中间件都不会正常运行
app.get("/user/login", function (req, res, next) {
console.log("登录中间件")
next()
})
app.get("/user/login", function (req, res, next) {
console.log("最后一个中间件")
res.send("nice ")
})
正确的写法应该是
app.use(
function (req, res, next) {
console.log("中间件1")
res.send("中间件1")
next()
},
function (req, res, next) {
console.log("中间件2")
next()
},
function (req, res, next) {
console.log("中间件3")
next()
});
app.use(
function (req, res, next) {
console.log("中间件4")
next()
});
app.get(
"/user/login",
function (req, res, next) {
console.log("登录中间件")
next()
}, function (req, res, next) {
console.log("最后一个中间件")
}
)
若没有第二个app.use里面的next,那么就不执行后面get请求了
中间件也可以是写在外面的js文件里面,然后导入使用
比如新建login.js
function fn(req, res, next) {
console.log("登录中间件")
next()
}
module.exports = fn;
之后在index.js中引入使用
let login = require("./login.js");
app.get(
"/user/login",
login,
function (req, res, next) {
console.log("最后一个中间件")
}
)
附上一个终端运行截图
通过路由挂载中间件
app.use(中间件监听的路径,中间件函数)
参数两个:路径,*是匹配任何路径;回调函数,找到路径之后会执行回调函数。回调函数一般都是第三方封装好的函数,这个函数叫做中间件。
// 通过路由挂载中间件
app.use("/user/login", function (req, res) {
console.log("打开/user/login")
res.send("用户登录成功")
})
因为之前运行脚本开启了3000端口,需要关闭重启才能在规定目录看到想看的。
我们需要注意的是:修改了 server.js 文件,需要停止服务(ctrl + c),再次启动才会生效;
有了 nodemon,就不用频繁手动重启 server.js ,因为 nodemon 可以监视源码改动,并自动重启服务。这样我们只需要专注写代码逻辑,不需要过多关注服务。
全局安装nodemon
npm install -g nodemon
package.json 中的 nodemonConfig 选项中配置nodemon
"nodemonConfig": {
"watch":["src/"],
"ext":"js json"
}
之后修改代码不需要重启服务只需刷新页面即可
使用匹配所有路径,下面的就不匹配了
app.use("*", function (req, res) {
console.log("匹配所有路径")
res.send("匹配所有路径 ")
})
// 通过路由挂载中间件
app.use("/user/login", function (req, res) {
console.log("打开/user/login")
res.send("用户登录成功hahah ")
})
http请求
http有很多请求方式,常见的:
get请求、post上传、put修改、patch修改、delete删除
使用app.get 可以监听get请求,其他类似
postman可以发起不同的请求,可以作为请求测试工具。
postman使用描述
服务器启动的时候,使用postman进行测试
新建collection集合 Blog接口测试
接着在集合里面添加请求add request
路由中间件
专门处理路由的中间件就是路由中间件,路由中间件是express内置的一个中间件。后端编写接口,前端调用接口。
后端接口功能包括用户相关操作(登录,注册),文章相关操作(发布文章,删除文章,修改文章,文章列表,文章详情等等)
借助路由中间件将用户和文章相关操作分散在不同的js文件中
这种路由主要对应用级别的路由做了细分处理,就是路由级别的路由
操作步骤
- 获取路由中间件,新建routes/users.js
- 处理get请求,router.get(监听请求的路径,处理回调函数)
- 处理post请求,router.post(监听请求的路径,处理回调函数)
- 导出router对象
代码展示
users.js是用户相关操作
const express = require("express");
//创建一个路由中间件
const router = express.Router();
// /user/reg
//配置路由
router.get("/reg", function (req, res) {
console.log("reg请求");
// 向服务器发送json数据
res.send({ username: "zhangsan" });
})
// 导出路由中间件
module.exports = router;
之后导入app.js(一开始我把index.js作为入口文件,后来将index移动到demo文件夹,新建了app.js作为新的入口文件)
let userRouter = require("./routes/users");
app.use("/users", userRouter)
之后启动服务器运行截图
路由中间件请求参数获取
get请求参数的获取
req.query
下面代码是users.js中的,因为users.js是app.js中的路由所以下面是监听/ users / login路径的get请求,如果在前端请求的url里面输入
http://localhost:3000/users/login?username=Angle&password=123456,在终端可以看到打印的请求的query参数
// 监听get请求 / users / login
//url中的搜索参数
router.get("/login", function (req, res) {
// 获取get请求的query参数
console.log(req.query);
// 返回json数据
res.json({ name:小米糕});
});
get请求的另外一种传参
获取get请求的url中的参数
router.get("/info/:id", function (req, res) {
console.log(req.params);
res.json(req.params);
})
post请求参数的获取
//user.js
router.post('/profile', (req, res, next) => {
console.log(req.body)
res.json(req.body)
})
//app.js
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
上面users.js中req.body包含在请求正文中提交的数据键值对。默认情况下,它是 undefined
app.js中解析前端请求的请求体的数据 ,解析json和x-www-form数据
post请求需要借助工具apipost或者postman来发送
我是用postman
两个小时遇到的问题是发送post请求服务器返回undefined。
静态资源托管中间件
html,css,js图片等都是静态资源,静态资源一般不需要处理直接返回给浏览器。
网页中发起请求,请求接口返回的数据叫做动态资源,比如JSON数据。
动态资源一般后端会处理,比如解析前端的请求过来的url和参数,根据url和参数返回不同的json数据。
使用express创建一个静态资源服务器,express.static()
当前目录下的文件夹public为静态资源文件夹,有请求发过来会先到静态资源文件夹下找有没有静态文件,有的话就直接返回静态文件,比如访问http://localhost:3000/a.js,就会去pubilc文件夹找a.js文件,在app.js里server.use(express.static(‘public’));
app.use(express.static("public"));
可以在浏览器地址栏输入http://localhost:3000/cool.jpg查看静态资源图片
这里一开始实践的时候我在地址栏输入之后回车get不到,后来检查发现我的public在demo子文件夹里面,于是删除,重新在Blog文件夹下面新建public文件夹。之后可以正常看。
假如在public文件夹下面还有多个文件夹,比如图片放在img文件夹。
那么访问的时候需要在url里面加上public的子文件夹路径。
http://localhost:3000/img/cool.jpg
express脚手架创建项目
express内置了许多中间件,逐个配置较复杂,一个后端项目需要一些别的中间件比如日志插件,错误处理插件等等,借助通用的项目模板可以简化配置过程。
通过应用生成器工具express-generator可以快速创建一个应用的骨架。
安装express-generator
npm install express-generator -g
创建项目
express -e Dreamlab
-e是指定了项目中的模板引擎。
前后端不分离
MVC是一种经典的设计模式,全名Model-View-Controller即模型-视图-控制器。这种模式用于应用程序的分层开发。大致就是模型和视图需要通过控制器来进行粘合。例如,用户发送一个HTTP请求,此时该请求首先会进入控制器,然后控制器去获取数据并将其封装为模型,最后将模型传递到视图中展现。,需要说明的是这个View可以采用Velocity,Freemaker等模板引擎,可使开发过程中人员分工更明确提高开发效率。
前后端分离
前后端分离(解耦)的核心思想:前端html页面通过ajax调用后端的Restful API并使用Json数据进行交互。
前端负责开发页面,通过接口(ajax)获取数据,采用Dom操作对页面进行数据绑定,最终是由前端把页面渲染出来。
在互联网架构中一般有两种服务器:web服务器:一般指像nginx,apache这类的服务器,他们一般只能解析静态资源,
应用服务器:一般指像tomcat,jetty,resin这类服务器可以解析静态资源和动态资源,但是解析静态资源的能力没有web服务器好。
一般只有web服务器才能被外网访问,应用服务器只能内网访问。
模板引擎是一个将页面模板和要显示的数据结合起来生成html页面的工具。如果说express中的路由控制方法相当于MVC中的控制器,那么模板引擎相当于MVC中的视图。
模板引擎的使用
下面两行代码设置模板文件的存储位置和使用的模板引擎
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
通过调用res.render()渲染模板,将其产生的页面直接返回给客户端,接收两个参数一个是模板名称,views目录下的模板文件名,拓展名.ejs可选。第二个参数是传递给模板的数据对象,用于模板翻译。
routes/index.js
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
get请求路径的时候使用{ title: ‘Express’ }渲染views目录下的模板index模板,把渲染后的数据返回给客户端。其实就是把一个对象传递给了index.ejs文件,得到一个html字符串,把html字符串返回给了前端。
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>
restful设计规范
restful-api是目前比较成熟的api设计理念。
域名
api的根入口点应尽可能保持足够简单,例如
api.example.com/(子域名下)
example.com/api/(主域名下)
域名应该考虑拓展性,如果不确定API后续是否会拓展,应该将其放在子域名下,这样可以保持一定的灵活性。
路径
路径又称为端点,表示API的具体地址路径设计遵循下面约定:
- 明明必须全部小写
- 资源(resource)命名必须是名词复数形式
- 连字符使用"-“而不是”,"字符可能会在某些浏览器或屏幕中被部分遮挡或全部隐藏
- 易读
例如:
反例:
api.example.com/getUser
api.example.com/addUser
正例:
api.example.com/students
api.example.com/students/girl…
HTTP请求
对于如何操作资源,有相应的HTTP请求对应,常用有下面5个(括号对应sql语句)
- get(select) 从服务器取出一项或者多项资源
- post(create)在服务器新建一个资源
- put(update)在服务器更新资源(客户端提供改变后的完整资源)
- patch(update)在服务器更新资源(客户端提供改变的属性)
- delete(delete)从服务器删除资源
下面通过几个简单增删改查demo演示上面的请求
user.js里面
router.get('/', function (req, res, next) {
res.send('respond with a resource');
res.json(
{
code: 1, msg: "查询成功",
data: [
id: 1, username: "zhangsan",
id: 2, username: "lisan",
],
}
)
});
Postman中几个body请求格式区别及使用说明
Params与Body
二者区别在于请求参数在http协议中位置不一样。
Params 它会将参数放入url中以?区分以&拼接
Body则是将请求参数放在请求体中
get请求测试
输入地址get请求,选择body然后点击发送请求
中间很多次不清楚什么原因一直报错,postman返回的消息不是json数据,后来重启了几次注释,开启注释,重新启动,发送get测试成功返回json数据,如果没有语法错误和符号错误,重启服务器可能可以解决这个问题。
post请求测试
router.post('/', function (req, res, next) {
console.log(req.body)//请求的参数
res.json({
code: 1,//code1操作成功0操作失败
msg: '新建成功',
data: { id: 1, username: "zhangsan" },
});
});
get请求动态id相关信息
一开始多次get请求都是返回404,然后重启服务器就正常了
patch修改请求
router.patch('/:id', function (req, res, next) {
// 1.获取参数
console.log(req.params);
console.log(req.body)
// 2.数据库操作
// 3. 返回json数据
res.json({
code: 1,
msg: "更改成功",
data: { id: 1, username: "zhangsan" },
});
});
删除操作
router.delete('/:id', function (req, res, next) {
// 1.获取参数
console.log(req.params);
// 2.数据库操作
// 3. 返回json数据
res.json({
code: 1,
msg: "删除成功",
});
});
(删除请求postman中可以不添加password那个参数,虽然这个测试只是一个小demo)