美文网首页
第十节: Node框架: Express

第十节: Node框架: Express

作者: 时光如剑 | 来源:发表于2020-12-26 17:55 被阅读0次

    1. Express 介绍(了解)

    Express 是一个基于 NodeJS平台的极简.灵活的web应用开发框架,可以实现非常强大的web服务器功能

    1.1 原生Node服务器缺点
    1. 路由不方便制作,尤其是正则表达式的路由
    2. 静态资源服务器不方便
    3. 页面呈递不方便
    1.2 Express框架的特点
    1. 提供了中间件来控制HTTP请求
    2. 定义路由表用于执行不同的HTTP请求动作(url=资源)映射
    3. 可以通过向模板传递参数来动态渲染HTML页面
    4. 拥有大量的第三方中间件对功能进行扩展

    2.Express 安装及基本使用

    2.1 安装express
    npm install express --save
    
    2.2 express的使用
    // 引入express框架
    const express = require('express');
    // 创建网站服务器
    const app = express();
    
    // 这里路由清单
    app.get('/', function (req, res) {
      // 返回数据用send方法
      res.send("我是首页")
    })
    app.get('/music', function (req, res) {
      res.send("音乐频道")
    })
    app.get('/news', function (req, res) {
      res.send("新闻频道")
    })
    
    
    // 监听端口
    app.listen(3000);
    console.log("Server start at 3000 port")
    

    3. 中间件(Middleware)

    Express 是一个自身功能 极简,完全是由路由和中间件构成的一个web开发框架,从本质上来说,一个Express 应用就是在调用各种 中间件

    3.1. 什么是中间件

    中间件就是匹配路由之前或者匹配路由完成时所作的一系列的操作,我们可以把他叫做中间件,

    中间件函数,可以访问请求对象,响应对象,也可以调用下一个中间件,一般被命名为next的变量.(next 尾函数, 执行下一个任务)

    express框架就是一个有中间件构建起来的框架,整个框架全是中间件

    中间件2.png
    3.1.1 中间件的功能:
    1. 执行任何代码
    2. 修改请求和响应对象
    3. 终结请求-响应循环
    4. 调用堆栈中的下一个中间件
    3.1.2 中间件的结构

    使用中间件语法

    ​ app.use([path , ] callback [, callback...])

    参数

    1. path 可选, 为路由的url, 如果省略将匹配到所有路径,

    2. callback 中间件函数, 当路由匹配成功执行函数, 函数接受三个参数

      ​ calllback(request, response, next)

      • request: HTTP请求对象
      • response:HTTP响应对象
      • next:处理完后交给下一个路由。若不调用则处理到此为止,不进行后续操作

    示例

    app.use('/', function(req,res,next){
        
    })
    
    3.2. 尾函数 next

    如果在中间里不调用next函数,整个请求响应流程就会中断,不会再往后面执行

    app.use(function (req, res, next) {
      console.log(111)
      console.log(222);
      next();  //  如果不调用next,将不会执行下一个中间件,不会打印333,444
    })
    app.use(function (req, res, next) {
      console.log(333)
      console.log(444)
    })
    
    3.3. 下一个中间件执行完毕

    中间件类似于过滤器,用于在客户端和应用程序之间处理请求和响应的方法。中间件的执行类似剥洋葱,但并非一层层的执行,而是以next为分界,先执行本层next之前的部分,当下一层中间件执行完毕后再执行本层next之后的部分。

    中间件洋葱图

    中间件.png

    调用尾函数就会执行下一个中间件,下一个执行完毕后回来继续执行自己的函数

    app.use(function (req, res, next) {
      console.log(111)
      next()
      console.log(222);
    })
    app.use(function (req, res, next) {
      console.log(333)
      console.log(444)
    })
    /*
    打印
    111
    333
    444
    222
    */
    
    3.4. 中间件分类
    3.4.1 内置中间件
    express.static()

    express.static 是用来处理静态资源文件。

    // 推荐使用绝对路径
    app.use(express.static(path.join(__dirname,'public')))
    

    我们也可以给静态资源目录制定一个虚拟路径

    // 第一个参数就是给静态资源目录制定的虚拟路径
    app.use('/static',express.static(path.join(__dirname,'public')))
    
    express.json()

    处理json方式的传参

    内置的json中间件负责把带有JSON的请求中(即Content-Type=‘application/json’)的数据提取出来,它基于body-parser

    app.use(express.json());
    
    express.urlencoded()

    处理表单方式的传参

    urlencoded负责将通过urlencoded发送请求(即Content-Type=‘application/x-www-form-urlencoded’)的数据提取出来,它基于body-parser
    经过此中间件后,req.body为解析后的json串,

    实例:

    const express = require("express")
    const bodyparser = require("body-parser")
    
    const app = express()
    
    // 处理post请求表单传参方式的数据
    app.use(express.urlencoded())
    // 处理post 请求json 传参方式的数据
    app.use( express.json())
    
    // 
    app.use(function(req,res,next){
        console.log(req.body)
        res.send("首页")
    })
    
    app.listen(3000, function(){
        console.log("Server start at 3000 port")
    })
    
    3.4.2 自定义中间件

    在上面中间件结构中,我们知道了,中间件使用时的第二个参数是一个Function,然而,要自定义一个中间件,就是倒腾一番这个Function。

    这个function总共有三个参数(req,res,next);

    当每个请求到达服务器时,nodejs会为请求创建一个请求对象(request),该请求对象包含客户端提交上来的数据。同时也会创建一个响应对象(response),响应对象主要负责将服务器的数据响应到客户端。而最后一个参数next是一个方法,因为一个应用中可以使用多个中间件,而要想运行下一个中间件,那么上一个中间件必须运行next()。

    app.use(function (req, res, next) {
      console.log(111)
    })
    
    3.4.3 第三方中间件

    有关第三方中间件,这里我们分析几个比较重要和常用的,知道这几个的使用,其它的也就会了。

    1. body-parser :解析body中的数据,并将其保存为Request对象的body属性。
    2. cookie-parser :解析客户端cookie中的数据,并将其保存为Request对象的cookie属性
    3. express-session :解析服务端生成的sessionid对应的session数据,并将其保存为Request对象的session属性

    例子:

    const express = require("express")
    const bodyparser = require("body-parser")
    
    const app = express()
    
    // 处理post请求表单传参方式的数据
    app.use(bodyparser.urlencoded({extends:true}))
    // 处理post 请求json 传参方式的数据
    app.use(bodyparser.json())
    
    // 
    app.use(function(req,res,next){
        console.log(req.body)
        res.send("首页")
    })
    
    app.listen(3000, function(){
        console.log("Server start at 3000 port")
    })
    
    3.4.4 错误处理中间件

    在程序执行过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件是一个集中处理错误的地方

    // 自己手动的抛出一个错误
    app.get("/", function(req,res,next){
        throw new Error('服务器发生错误')
    })
    
    // 当程序出错的时候就会走错误中间件
    app.use(function(err, req,res,next){
        // console.dir(err)
        res.status(500).send(err.message)
    })
    
    

    上面的错误例子是同步的错误, 如果是异步语句发生错误要调用next()并且传入错误参数

    异步处理发生错误使用next手动触发错误中间件

    app.get("/", function(req,res,next){
        fs.readFile("./index.html",'utf8',(err,result) => {
            // console.log(err,result)
            if(err){
                next(err)
            }else{
                res.send(result)
            }
            
        } )
    })
    
    app.use(function(err, req,res,next){
        // console.dir(err)
        res.status(500).send(err.message)
    })
    
    3.5.中间件的应用
    3.5.1 路由保护

    客户端在访问登录页面的时候,可以先使用中间件判断用户登录状态, 用户如果未登录,则拦截请求, 直接响应, 禁止用户进入登录页面

    app.use(function(req,res,next){
        // 判断用没有登录, 登录就走后面,没登录,就提示用户登录
        if(cookiename){
            next()
            return;
        }
        res.send("你还没有登录,请登录")
    })
    
    3.5.2 网站维护公告

    在所有路由之前最上面定义接受所有组件的中间件, 直接为客户端响应,网站正在维护中

    app.use(function(req,res,next){
        let hours = new Date().getHours()
        console.log(hours)
        if( 22 <= hours && hours <= 23  ){
            res.send("网站正在维护,请于早上8点访问")
        }
        next()
    })
    
    3.5.3 自定义404 页面

    在所有路由后面定义中间件, 处理404

    app.use(function(req,res,next){
        res.status(404).send("404, 你访问的页面不存在")
    })
    

    4. Express 请求与相应

    4.1. Express 响应对象

    响应对象是指服务器向客户端响应数据的对象,包含所有要响应的内容

    4.1.1 send() 方法 (重点)

    向页面发送各种数据

    语法:

    ​ res.send(data) 可以返回任意类型数据

    参数

    ​ 响应内容

    示例:

    res.send(Buffer.from("hello world"));    // 流数据
    
    res.send({"name":"json"});              // JSON 数据
    
    res.send(`"<p>普通文本</p>"`)                 //  普通文本
    

    注意: 如果返回数字: 会被当做状态码

    1. res.send(1); 这样写会报错()
    2. send() 方法只能出现一次,重复无效还报错,因为send内含end()结束响应

    设置状态码,并返回内容

    res.status(200).send("text")
    

    send方法的好处

    1. send方法内部会自动检测响应内容的类型
    2. send方法会自动设置http状态码
    3. send方法会帮我们自动设置响应内容的内容类型及编码
    4.1.2 sendFile() 方法

    响应文件

    除了可以直接返回内容外,我们还可以直接返回文件

    语法:

    res.sendFile( path, callback)
    

    path: 返回文件的路径,必须是绝对路径,否则会报错

    callback : 回调函数, 接受一个错误的参数

    app.get("/data",(req,res) => {
        res.sendFile(path.join(__dirname,"./students/100002.json"),(err)=>{
            console.log(err)
        })
    })
    
    4.1.3 json() 方法

    我们除了可以利用send返回JSON数据外,还可以单独使用json方法响应JSON数据

    返回JSON数据,会自动设置响应头

    语法:

    res.json(jsonData)     // 返回json对象,一般针对ajax应用
    

    实例:

    // 可以是真正的JSON数据
    res.json(JSON.stringify({name:"aa"}))
    
    // 如果是对象会自动被转为JSON数据
    res.json({name:"小明",age: 8})
    

    当然了,也可以通过sendFile 方法响应json数据

    res.sendFile(`${__dirname}/data.json`)
    
    4.1.4 redirect () 重定向路径

    重定向到指定的URL路径(浏览器地址变为设置的地址)

    语法

    res.redirect("/user")
    
    // 第一个参数也可以是状态码
    res.redirect(301, "/user")
    

    示例:

    app.get("/data",(req,res,next) => {
        // 路由重定向到/json路由上
        res.redirect(301,"/json")
    })
    app.get("/json",(req,res) => {
        res.json({name:'aa'})
    })
    
    4.1.5 download 下载

    语法

    res.download("./xxx.zip") 下载当前目录下的xxx.zip文件

    app.get("/data",(req,res) => {
        res.download(`./students.zip`)
    })
    
    
    4.1.6 end () 结束响应方法

    就是结束前后端的连接

    res.end()
    

    结束响应的时候也可以同时响应数据

    res.end("结束")
    
    4.1.7 res.jsonp ()

    传送JSONP响应,

    语法:

    res.jsonp(jsonData)
    

    jsonp返回给前端的依然是json数据, 但是前端在使用jsonp的使用需要路径传递上callback参数

    如果不传递得到的就是json数据

    如果传递了callback那么得到的就是以callback值为函数名执行

    示例:

    app.get("/jsonp",(req,res,next) => {
        console.log(11)
        res.jsonp({name:'wuwei'})
    })
    

    不加callback参数的请求结果

    {
        "name": "wuwei"
    }
    

    添加callback参数的请求结果

    typeof wuwei === 'function' && wuwei({"name":"wuwei"});
    
    4.1.8 render 视图模板
    1. 将渲染的视图发送给客户端
    res.render("index")
    
    1. 将视图和数据合并后发送给客户端
    res.render("index",{
        username: "HAHA"
    })
    

    例子:

    // 路由
     var data = [
         {name: "小明",age: 10},
         { name: "小红",age: 12},
         {name: "小蓝",age: 11},
       ];
    
       res.render("list", {
         users: data
       })
     });
    
    
    <!-- ejs模板 -->
    <% for(var item of users){ %>
        <li>姓名:<%= item.name%>-年龄:<%= item.age %></li>
    <% } %>
    
    4.2. Express 请求对象

    req(request) 对象包含了一次请求中的所有数据(http 请求头信息,请求参数)

    4.2.1 获取url地址中的参数(重点)

    Express 框架中使用req.query 就可以获取URL方式传递的请求传值,并且会将GET参数转为对象返回

    语法:

    语法:

    ​ req.query

    使用:

    ​ url: http://localhost:3000/user?name=07
    ​ 获取参数: req.query

    示例

    // 例如: http://localhost:3000/user?name=07
    app.get("/user", (req,res) => {
        console.log(req.query);  // {name: 07}
        res.send(req.query)
    })
    
    4.2.2 获取POST的参数(重点)

    Express中接收POST请求参数需要借助第三方的包,body-parser

    语法:

    req.body

    比如

    // 首先需要引入第三方的包,
    const bodyparser = require("body-parser");
    
    // 拦截所有请求
    // 配置body-parser模块
    // extended: false ,方法内部使用querystring内置模块处理请求参数格式
    // extended: true  方法内部使用第三方qs模块处理请求参数
    app.use(bodyparser.urlencoded({extended: false}))
    app.use(bodyparser.json())
    
    // 还有exprss 框架提供的处理post的内置中间件
    app.use(express.urlencoded())
    app.use(express.json())
    
    
    // 接受请求
    app.post('/add', (req,res) => {
        // 请求参数 
        console.log(req.body)
    })
    
    
    4.2.3 路由参数

    动态路由(伪静态页面)

    语法:

    req.params.参数名

    定义路由

    // :id 就是一个占位符,路由参数的属性
    app.get("/first/:id", function (req, res) {
      var id = req.params.id;
      res.send("你动态路由参数是:" + id)
    });
    
    4.2.4 RESTful 路由风格

    所谓的路径参数就是最近比较火的RESTful路由风格

    以前的操作是一个路由对应一个功能,

    如果我要对同一个学生操作增删改查,就得定义如下的路由

    http://127.0.0.1/showstudent?id=10001   // 显示学生信息路由http://127.0.0.1/delstudent?id=10001    // 删除学生信息路由http://127.0.0.1/addstudent?id=10001    // 新增学生信息路由http://127.0.0.1/updatestudent?id=10001 // 修改学生信息路由
    

    现在比较流行的是RESTful路由风格处理

    简单理解就是:对于同一个学生的操作,都在同一个URL下进行,通过判断HTTP类型的不同,来决定做不同的事情

    http://127.0.0.1/students/10001   // GET请求    访问学生信息
    http://127.0.0.1/students/10001   // POST请求    修改学员信息
    http://127.0.0.1/students/10001   // put请求,     查看学员是否被占用
    http://127.0.0.1/students/10001   // delete请求   删除学员信息
    

    那么路径上的100001就是我们参数的部分, 这样比原本传统的参数要美观,

    我们管这种参数叫路由参数

    4.3. 路由路径的匹配
    4.3.1 字符串匹配模式
    // 1. 固定匹配
    /*
     /index 可以匹配成功
     /index/ 可以匹配成功
     /index/a 不可以匹配
    */
    app.get("/index", (req,res) => {})
    
    
    // 2. 匹配所有路径
    app.get("*", (req,res) => {})
    
    
    // 3.  匹配以/index开头的路径
    /*
        /index  可以匹配成功
        /index123  可以匹配成功
        /index/a/b 可以匹配成功
    */
    app.get("/index*", (req,res) => {})
    
    
    // 4.  匹配以 /index/ 开头的路径
    /*
        /index     不能匹配成功
        /index/    可以匹配成功
        /index/a/b 可以匹配成功
    */
    app.get("/index/*", (req,res) => {})
    
    // 5.  ? 前面的字符或组出现0~1 次
    /*
        /abc      可以匹配成功
        /ac     可以匹配成功
    */
    app.get("/ab?c", (req,res) => {})
    
    // 6.  + 前面的字符或组出现1~多 次
    /*
        /abc      可以匹配成功
        /abbbc    可以匹配成功
        /ac       不能匹配成功
    */
    app.get("/ab+c", (req,res) => {})
    
    
    // 7.  * 表示任意个数的任意字符
    /*
        /abc      可以匹配成功
        /ab123c    可以匹配成功
    */
    app.get("/ab*c", (req,res) => {})
    
    
    // 8.  ()里的内容为一组
    /*
        /abcd      可以匹配成功
        /ad         可以匹配成功
        /abd          不能匹配成功
        /acd          不能匹配成功
    */
    app.get("/a(bc)?d", (req,res) => {})
    
    4.3.2 正则匹配模式
    //1. 包含index的路径
    /*
        /index  可以匹配
        /aindexb 可以匹配成功
        /a/index/b  可以匹配成功
    */
    app.get(/index/, (req,res) => {})
    
    // 2. 以.html后缀名结尾的路径
    /*
        /index.html  可以匹配
        /index/a.html 可以匹配成功
    */
    app.get(/.*\.html$/, (req,res) => {})
    
    4.4. 路由器的处理程序
    4.4.1 单处理程序
    // 方式一
    app.get("/", (req,res) => {})
    
    // 方式二
    let handler = (req,res) => {}
    app.get("/", handler)
    
    4.4.2 多处理程序

    除非是最后一次处理,否则请不要执行res.send()等发送指令

    除非是最后一次处理,否则请不要忘记next形参 与next() 指令

    app.get("/", (req,res,next) => {
        console.log("handle 1")
        next()
    },(req,res,next) => {
        console.log("handle 2")
        next()
    },(req,res) => {
        res.send("Hello wrold")
    })
    

    或者可以这么写

    let handler1 = (req,res,next) => {
        console.log("handle 1")
        next()
    }
    let handler2 = (req,res,next) => {
        console.log("handle 2")
        next()
    }
    let handler3 = (req,res,next) => {
        res.send("Hello wrold")
    }
    app.get("/", [handler1,handler2,handler3])
    

    5. express 子应用程序

    express模块 每一次执行都会创建一个应用

    将子应用通过主应用的use方法挂在到主应用上

    6. express 路由

    6.1 路由的理解

    路由是指接收用户请求,处理用户数据,返回结果给用户的一套程序,

    后端路由的核心是URL

    app.get("/",(req,res,next) => {
        
    })
    
    6.2 express路由的使用

    虽然现在可以通过app.get或app.post 定义路由了, 但是在一个项目中, 路由的数量是非常多的, 如果把所有的路由都放在同一文件中,那将是非常可怕的事情,

    所以express 为了解决这个问题, 提供了模块化构建路由的方式, 我们可以根据条件将路由进行分类

    express 路由使用语法

    ​ app.METHOD(PARH, HANDLER)

    参数了解

    1. app: express 实例
    2. METHOD: http 请求方式
    3. PATH: 请求路径
    4. HANDLER: 路由匹配后执行的函数

    示例:

    app.get("/about.html",function(req,res){
        res.send("<h1>个人简介</h2>")
    })
    
    6.3 Router (重点) 路由模块化

    express.Router 类可以创建模块化(独立的),可以挂载的路由对象.Router对象是一个完整的中间件和路由系统,因此常称其为一个"mini-app"

    需求: 创建一个student路由模块,接受所有 student目录下的所有请求,响应数据

    1. 创建student模块

    2. 编写路由模块的代码

       // 1. 引入模块
       const express = require("express")
      
       // 2. 实例化路由对象
       const router = express.Router();
      
       // 3. 编写路由线路挂载到路由对象上
       router.get("/first.html", function (req, res) {
         res.send("<h2>一年级</2>")
       });
      
       router.get("/second.html", function (req, res) {
         res.send("<h2>二年级</2>")
       })
      
       // 4. 暴露对象
       module.exports = router;
      
    3. 将编写好的路由模块引入到主模块,由主模块分配对应的请求到该模块去处理

      // 1. 引入vip路由模块
      var studentRouter = require('./routes/student');
      // 2. 分配student目录下的路由给studentRouter模块
      app.use('/student', studentRouter);
      

    相关文章

      网友评论

          本文标题:第十节: Node框架: Express

          本文链接:https://www.haomeiwen.com/subject/znumgktx.html