5) 搭建服务器Express【Node】

服务器:1. 接收请求报文 2. 发送响应报文
服务器这辈子只干一件事——等。等前端向它发请求要数据,所以服务器一直在"监听请求、处理请求、回复请求"的死循环里

一、Express框架

Express是一个基于 Node.js 的轻量级Web 应用框架,它简化了在 Node.js 中构建服务器端应用的过程。通过使用 Express,你可以快速地搭建一个强大而灵活的 Web 服务器。(或者说"Express是node中的服务器软件,通过Express可以快速地在node中搭建一个web服务器",node中的HTTP模块也可以搭建服务器,但是这个模块太底层了,Express相当于是对HTTP模块的封装、简化)
Express 提供了一系列的功能和工具,使得处理路由、中间件、请求和响应等任务变得更加容易。它允许你以模块化和清晰的方式组织你的代码,同时支持多种模板引擎,使得动态生成 HTML 页面变得简单。

1. 搭建服务器步骤:

  1. 创建并初始化项目:npm init -y
  2. 安装Express框架:npm install expressyarn add express (添加为项目依赖)

如果yarn报错的话,检查一下package.json文件里是否定义yarn的工作区:
若没有,就直接配置"workspaces":[ "yarn的工作区目录(相对于package.json文件的路径即可)" ]
例如:"workspaces":[ "Node_express20240112/*" ] 表示yarn的工作区包含 Node_express20240112 目录下的所有子目录。
虽然但是……我最后还是用npm了,yarn的报错我不想解决了,烦人o(╥﹏╥)o

在这里插入图片描述

  1. 创建index.js入口文件,并编写代码
  • 引入Express框架 const express = require("express");

  • 创建一个express应用的实例,通过这个实例对象,可以配置和定义路由、中间件、启动服务器

    const app = express();
    
  • 开启服务器 app.listen(3000);(监听本地端口号为 3000 的请求,并启动Express应用)

    图片.png 此服务器的地址 协议名://ip地址:端口号/路径,如果此时前端向http://localhost:3000/或者http://127.0.0.1:3000发axios请求,用Express搭建的服务器就开始干活啦。
    app.listen(5005,()=>{})此回调函数在服务器启动后,被自动调用 图片.png

  • 配置一个后端路由来处理前端的请求(详见下一节)

  • 数据以JSON格式返回
    在这里插入图片描述

  • 停止服务器:Ctrl+C

    服务器就是一台计算机。别人访问服务器里的软件时,需要提供端口号,3000的端口号就让express服务器来提供服务
    图片.png

// node采用CommonJS模块化,引入express这个包
const express = require("express");

// 创建一个服务器实例对象 相当于new Vue({})创建一个Vue实例对象??
const app = express();

// 配置一个后端的路由
// 如果前端的路径是/student,匹配上后端的路由信息,后端就执行回调函数,将学生信息返回给前端
app.get("/student", (req, res) => {
  // 响应报文的send方法,给前端返回信息
  res.send({
    name: "孙悟空",
    gender: "男",
    age: 18,
  });
});

// 此服务器开启在本地5005端口
app.listen(5005, (err) => {
  if (!err) {
    console.log("服务器启动成功");
  }
});

二、路由

区分后端路由和前端路由

  • 后端路由用于指定在收到特定 HTTP 请求时执行的处理程序函数
  • 前端路由是指在单页面应用(SPA)中,根据URL的变化切换视图而无需重新加载整个页面的机制。在前端路由中,JavaScript 负责根据 URL 的不同渲染不同的内容,而不是由服务器返回不同的 HTML 页面

1. 后端路由

  1. 路由可以根据不同的请求方式和请求地址,来处理用户的请求

    (给服务器配置路由,才可以正常访问服务器)

  2. 路由的设置方式

    app.get(路径,回调函数)
    例如app.get("/",()=>{}表示前端向根目录发起get请求时,这个回调函数就会执行
    根目录——服务器中的/ 相当于 http://localhost:3000

// 在服务器中,/表示根目录,=== http://localhost:3000 (访问本地服务器的3000端口)
app.get("/", () => {
  console.log("有人访问我了");
});

路由中的路径:后端路由中的路径写什么,浏览器的地址栏(前端)就得写什么路径,不然就访问不到了

2. 后端路由的回调函数干两件事

配置路由时的回调函数,它接收3个参数:request、response、next
  1. 读取请求报文
    拿到请求报文request(一个对象) req.url req.xxx

  2. (根据请求)返回响应报文
    返回响应报文response(一个对象,通过对象的方法……)

res.send()设置并发送响应体(向客户端发送信息)
res.sendStatus()向客户端发送响应状态码
res.status()设置响应状态码,不发送给客户端

三、中间件

请求到达路由之前,会经过中间件,相当于前端的路由守卫,都可以在处理请求之前执行一些逻辑

  1. 在express中,使用app.use()来定义一个中间件

    app.use(["路径",](req,res,next)=>{})

  • 如果use方法没有"路径"参数,表示该中间件拦截所有的请求(一夫当关,万夫莫开),相当于路径写的是根目录app.use("/",(req,res,next)=>{})
  • 如果use方法有"路径"参数,表示该中间件只拦截对应路径的请求
  1. 回调函数中的参数next是一个函数,调用next()后,控制权才会传递给下一个中间件或路由处理程序

    如果在中间件中没有调用 next(),请求处理流程将被中止,
    后续中间件 或者 路由处理程序 将不会被执行
    
// 此中间件会报错,因为不能在响应处理完之后res.send()再调用next()

// 配置一个中间件
app.use((req, res, next) => {
  res.send("在next之前send是错误的");  // 给前端返回数据
  next(); // 放行
});

中间件的应用:查看是否有权限(身份验证中间件)、错误处理中间件、多个路由要处理的逻辑可以写在中间件里

  1. 路由与中间件的区别

  2. 路由不区分请求的方式,只看路径;

    中间件和路由的区别:

    • 中间件会处理所有请求(post和get)
    • 中间件中不写路径的话,默认设置为根目录

可以在所有路由后面,配置一个处理错误路由的中间件

app.use((req,res)=>{
  // 只要这个中间件执行,说明上边的地址都没有匹配
  res.status(404)
  res.send("<h1>您访问的地址不存在</h1>")
})

四、nodemon

  • 服务器代码修改后,必须要重启服务器才有效
  • nodemon 是一个 Node.js 应用程序,用于监视文件变化并在文件更改时自动重启应用程序。这对于开发过 程中的实时反馈非常有用,因为你不必手动停止和重新启动应用程序。
  • nodemon使用步骤
  1. 安装

    全局安装npm install -g nodemon``yarn global add nodemon
    或者作为项目的开发依赖安装 npm install --save-dev nodemon

–save–dev可简写为-D,若nodemon作为项目开发依赖而安装的话,看我项目的人就很容易知道我用过nodemon!!

  1. 启动nodemon应用程序

    若是全局安装,在终端输入nodemon index.js,就开启了服务器
    若是作为开发依赖的安装, 要么在终端直接输入npx nodemon;要么配置package.json文件"script":{"start":"npx nodemon index.js"},然后在终端输入npm start时,就开启了服务器

    nodemon默认启动根目录下的index.js(所以npx nodemon index.js里的index.js可以省略)
    或者输入`nodemon xxx`运行指定的js文件
    
  2. 移除

    移除全局安装的nodemon:npm remove nodemon -g``npm uninstall -g nodemon
    移除项目的开发依赖nodemon:npm uninstall nodemon

    图片.png

五、静态资源

1. 浏览器看不到服务器的任何东西

  • 客户端看到的东西都是服务器返回的res.send(),返回网页看到网页,返回图片看到图片

  • 服务器中的所有东西,都无法被浏览器直接访问

    不仅服务器的根目录,连服务器本身都不会被用户看到,后面我们会学到 Apache 和Nginx(“engine x”) 做为反向代理服务器(它架在服务器和客户端之间),它接收来自客户端的请求,并将这些请求转发给后端服务器

    同向代理服务器: 代理客户端请求其他服务器  
    

2. 浏览器可访问服务器的静态资源

可以使用 express.static 中间件可以将特定目录下的文件作为静态资源提供给浏览器直接访问

express.static(root[,options])是用于提供静态文件的 Express 中间件函数。它的作用是将包含静态资源的目录映射到服务器的指定路径,使得这些静态文件可以通过浏览器直接访问

 (如果希望浏览器可以看到index.html,则需要将页面所在的目录设置为静态资源的目录)
// 将 public 目录下的文件作为静态资源提供给浏览器
// app.use(express.static("public"))  public为相对路径
app.use(express.static(path.resolve(__dirname,"./public"))) 
/*
   将public文件夹设置为静态资源,app.use没有传递路径,且写在最前面,所以当浏览器访问服务器时,
   首先经过该中间件,因此浏览器会默认先去public文件夹的index.html寻找是否有匹配的静态资源
*/ 

//中间件写在路由前面,浏览器在访问服务器时,会自动去public寻找是否有匹配的静态资源 

app.get()
中间件和中间件函数有啥区别吗??没有
若将public文件夹设置为静态资源,可以这样理解:`public === http://localhost:3000/`

六、POST和GET请求

// 前端用get请求,向"/login"提交一个form

app.get("/login",(req,res)=>{
  // 验证用户输入的用户名或者密码是否正确
  // get请求通过 查询字符串(res.query) 发送请求参数
  if(req.query.username === "孙悟空" && req.query.password === 123){
    /*
      因为前端发数据时,
      输入用户名的input,其name属性 = "username",
    	输入密码的inoput,其name属性 = "password"
      服务器通过属性名,获取对应input的内容
    */
    res.send("<h1>登录成功</h1>")
  }else{
    res.send("<h1>用户名或密码错误</h1>")
  }
})

但是,登录通常用post请求(get请求会将密码和用户名展示到url)

1. GET请求发送参数

1.1 查询字符串(Query String)

  • 在Express中,查询字符串是 ? 开头,紧跟着 key=value 形式的键值对,多个键值对之间用 & 分隔。例如,http://example.com/path?name=John&age=25 中,name 和 age 就是查询字符串的键。
  • 通过req.query 对象来访问查询字符串的参数。这个对象包含了所有查询字符串参 数的键值对
app.get("/hello", (req, res) => {
  // 前端路由向http://localhost:3000/hello 发送请求,这个回调函数才会把111返回给前端
  console.log(req.query);
  res.send("111");
});

1.2 路由参数(Route Parameters)

  • 在Express中,路由参数是通过在路由路径中定义的占位符来表示的,通常以冒号 : 开头。例如,/users/:userId 中的 :userId 就是一个路由参数。
  • req.params 对象包含了路由参数的键值对
// 当用户访问/hello/xxx 时
// 在路径中以冒号命名的部分我们称为param,在发送get请求时它可以被解析为请求参数,这些参数可以通过req的params属性获取
app.get("/hello/:id/:name/:age", (req, res) => {
  console.log(req.params);
  res.send("111");
});

// 浏览器传什么,在服务器就用id、name、age接到什么

2. POST请求的参数

  • 在传统的 RESTful API 设计中,一般来说,POST 请求通常用于在服务器上创建资源,而不是用于查询或检索资源。因此,通常不使用 查询字符串 或 路由参数 来传递数据,而是使用请求体request.body来发送数据
  • 默认情况下,Express不会解析请求体,因此要用中间件来增加功能(解析 POST 请求的请求体),才能通过 req.body访问参数(此中间件一定要写在所有 POST 路由前面)
app.use(express.json()); // 如果前端返回JSON格式的数据(ajax请求)
app.use(express.urlencoded({ extended: true })); // 解析 URL-encoded 格式的请求体

七、模版

1. 模版

  1. 希望用户在访问students路由时,可以给用户返回一个显示有学生信息的页面

    静态资源public文件夹中的index.html属于静态页面,创建的时候是什么样,返给用户看到的就是什么样,不会根据服务器中的数据变化而变化

  2. 希望有一个东西,长得像网页,并且里边可以嵌入变量——在node中,称之为"模版"

  3. 在node中有很多个模版引擎

2. EJS模版引擎**(Embedded JavaScript)**

  • EJS是node中的一款模版引擎

2.1 使用步骤:

  1. 安装为项目依赖 npm install ejs
    在这里插入图片描述

  2. 配置express应用实例的模版引擎为ejs
    app.set("view engine", "ejs");

// 使用app.set('views', '新的路径')来配置模板文件的根目录。
app.set('views', path.resolve(__dirname,"./views")); 
  1. 使用ejs:在根目录下新建views/students.ejs(扩展名ejs === html)

2.2 模版引擎如何被浏览器访问

  1. 只有public文件夹才能被直接访问
  2. 模版引擎被express渲染response.render()后才能被用户访问到

response对象的render方法用于渲染一个模版引擎,并将其返给用户
res.render("模版名stuendt(不加扩展名)",{ name:"孙悟空",age:18 })

  1. render()方法的第二个参数,是一个配置对象,EJS模版中可以将配置对象中的数据返回给浏览器
<body>
   <h1> <%= name %> </h1>
   <h1> <%- age %> </h1>

  <%= name %>能避免 xss 攻击(注入攻击) 
  <%- age %>不能避免 xss 攻击
  <%  %> 在其中可以编写js代码,js代码会在服务器中执行(ejs文件在服务器执行)

  <%# %>是服务器端的注释,<!-- -->是浏览器端的注释

</body>

res.redirect("")重定向是告诉浏览器“你向另一个地址再发起一次请求”
(浏览器给服务器的一个地址发起请求后,服务器返回浏览器,让浏览器向服务器的另一个地址发起请求)

八、路由器

  • 是express创建的一个对象const router = express.Router()
  • router实际上是一个中间件,可以在该中间件上绑定各种路由、以及其他中间件

1. 怎么使用路由器

  1. 新建/routers/user/index.js 新建/routers/goods/index.js
// 引入express矿建
const express = require("express")
// 创建一个路由器对象
const router = express.Router();

// 给路由器绑定路由
router.get("/list",(req,res,next)=>{
  next()  //交给下一个路由
})

// 写一些与list相关的路由
router.post("/students",()=>{})

// 对外暴露一个路由器
module.exports = router
  1. 在入口文件里
// 引入express矿建
const express = require("express");
// 引入核心模块path
const path = require("node:path");

// 创建一个express应用实例
const app = express();

// 引入路由器对象
const userRouter = require(path.resolve(__dirname,"./router/user"));
const goodsRouter = require(.......);

// 将views文件夹设置为模板
app.use("views engine","ejs");
// 将public文件夹下的文件设置为静态资源
app.use(express.static(path.resolve(__dirname,"public")));
// 设置解析POST请求的请求体的中间件
app.use(express.urlencoded({ extended:true }));


// 注册路由器对象成为应用的中间件
// 注意,这里的中间件一定记得写路径,否则路由器多了(且其中的路由有重名的),会造成混乱
app.use("/user",userRouter);
app.use("/goods",goodsRouter);

// 在所有路由的后面,当浏览器访问不存在的路由时,进行处理
app.use((req,res)=>{
  res.status(404)
  res.send("您访问的页面不存在")
})


// 开启服务器
app.listen(3000,()=>{
  
});
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值