一小时:手把手教你入门express建议收藏

Posted 贪吃ღ大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一小时:手把手教你入门express建议收藏相关的知识,希望对你有一定的参考价值。

express

简介

Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。搭建web服务器

Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。

使用Express开发框架可以非常方便、快速的创建Web网站的服务器或API接口的服务器

官方网址:https://www.expressjs.com.cn/

简单使用

下载安装:

npm init -y
npm i express -S

使用步骤:

  1. 导入包
  2. 创建服务器
  3. 处理请求
  4. 监听端口
const express = require('express')
const app = express()
app.get(路由,回调) // get是请求方法
app.listen(端口号)

路由方法

请求方法还支持:

get - 查询请求 - 条件在地址栏

post - 新增请求 - 数据在请求主体

put - 修改请求 - 条件在地址栏 - 数据在请求主体

delete - 删除请求 - 条件在地址栏

各个动词方法用来处理对应的请求。不过有一个方法除外:

app.all() // 可以用来处理任意请求方式

虽然all方法可以处理任意请求,但是尽量少用,甚至尽量不要使用。

使用postman进行调试

路由路径

完全匹配

// 匹配根路径的请求
app.get('/', function (req, res) {
  res.send('root');
});

// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
  res.send('about');
});

// 匹配 /random.text 路径的请求
app.get('/random.text', function (req, res) {
  res.send('random.text');
});

不完全匹配

// 匹配 acd 和 abcd
app.get('/ab?cd', function(req, res) {
  res.send('ab?cd');
});

// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', function(req, res) {
  res.send('ab+cd');
});

// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/ab*cd', function(req, res) {
  res.send('ab*cd');
});

// 匹配 /abe 和 /abcde
app.get('/ab(cd)?e', function(req, res) {
 res.send('ab(cd)?e');
});

字符 ?、+、* 和 () 是正则表达式的子集,- 和 . 在基于字符串的路径中按照字面值解释。

正则匹配:

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function(req, res) {
  res.send('/a/');
});

// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.*fly$/, function(req, res) {
  res.send('/.*fly$/');
});

路由多次处理

使用一个回调函数处理路由:

app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});

多次处理:

app.get('/example/b', function (req, res, next) {
  console.log('这处理完之后会交给下一个函数处理');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

使用回调函数数组处理路由:

var cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

var cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

var cb2 = function (req, res) {
  res.send('Hello from C!')
}

app.get('/example/c', [cb0, cb1, cb2])

混合使用函数和函数数组处理路由:

var cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

var cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('response will be sent by the next function ...')
  next()
}, function (req, res) 
        {
  res.send('Hello from D!')
})

响应方法

res.download() // 提示下载文件。	
res.end() // 终结响应处理流程。
res.json() // 发送一个 JSON 格式的响应。
res.jsonp() // 发送一个支持 JSONP 的 JSON 格式的响应。
res.redirect() // 重定向请求。
res.render() // 渲染视图模板。
res.send() // 发送各种类型的响应。
res.sendFile() // 以八位字节流的形式发送文件。
res.sendStatus() // 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。

download示例:

// 响应下载 - res.download(被下载的源文件,下载后的文件名称,回调函数)
res.download("./test.html",'b.html',err=>{
    if(err){
        console.log("下载失败");
    }else{
        console.log("下载成功");
    }
})

json示例:

// 给客户端响应json数据
// res.json(json格式的数据)
let obj = {
    name:"张三",
    age:12,
    wife:"翠花",
    children:['阿大','阿二','小明']
}
res.json(obj)

jsonp示例:

// 给客户端发起的jsonp请求做响应,响应的是json数据
// res.jsonp(json格式的数据)
let obj = {
    name:"张三",
    age:12,
    wife:"翠花",
    children:['阿大','阿二','小明']
}
res.jsonp(obj)

redirect示例:

// res.redirect() 用来跳转路由的 - /a这个路由,其实/b这个路由就正好可以处理他,就可以在/a这个路由处理中,将这次请求交给/b这个路由去处理
res.redirect('/index')

app.get('/index',(req,res)=>{
    let data = fs.readFileSync('./test.html')
    res.end(data)
})

render示例:


send示例:

// res.send() - 用于给客户端响应字符串的 - 字符串中如果是标签,可解析成html - 自动设置数据类型和编码
let html = `
<h2>这是一个h2标签</h2>
`
// res.end 不会自动设置数据类型,也不会设置编码
// res.end(html)
res.send(html)

sendFile示例:

// res.sendFile() 用于给客户端响应一个文件
res.sendFile(__dirname + '/test.html')

sendStatus示例:

// sendStatus是自动设置响应状态码,并将对应的响应状态描述响应给客户端
res.sendStatus(404) // 响应 not found
res.sendStatus(200) // 响应 ok

请求对象的属性

req.url // 请求的路径 - 如果有?传参,这个路径中也会带有参数
req.method // 请求方法
req.path // 请求路径 - 如果有?传参,这个路径中不包含参数
req.protocol // 协议
req.params // 获取get请求的参数 - 针对动态路由传参 - restful风格的参数 - 最终获取到的是对象,对象的键,就是路径指定的名称
req.query // 获取get请求的参数 - 针对传统形式传参 - 使用?参数 - 最终获取到的是对象

静态资源托管

express提供了一个非常好用的方法,叫做 express.static(),通过此方法,可以非常方便地创建一个静态web资源服务器:

app.use(express.static('public'))
// app.use()表示使用(中间件)
// 现在可以访问public目录下所有的文件 
// 如public/aa.jpg文件,则可以通过 : http://xxxx/images/aa.jpg

express还支持给静态资源文件创建一个虚拟的文件前缀(实际上文件系统中并不存在),可以使用 express.static 函数指定一个虚拟的静态目录,就像下面这样:

前缀的使用意义:

  • 可以迷惑别人,一定程度上阻止别人猜测我们服务器的目录结构
  • 可以帮助我们更好的组织和管理静态资源
app.use('/static', express.static('public'))

前缀前面的“/”必须要加,否则就错。【404】

现在你可以使用 /static 作为前缀来加载 public 文件夹下的文件了:

http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html

使用app.use()方法一般写在具体的路由监听之前。

每个应用可有多个静态目录。

app.use(express.static('public'))
app.use(express.static('uploads'))
app.use(express.static('files'))

路由

介绍

路由在生活中如拨打服务电话时,按数字几能处理什么样的处理,它就是类似于按键与服务之间的映射关系。在Express中,路由指的就是客户端发起的请求(地址)与服务器端处理方法(函数)之间的映射关系

express中的路由分3部份组成,分别是请求类型(方法)、请求uri(地址)和对应的处理函数

当一个客户端请求到达服务端之后,先经过路由规则匹配,只有匹配成功之后,才会调用对应的处理函数。在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的函数进行处理。

app.<get/post/put/delete/use>(uri,(req,res)=>{})
// use方法并不是请求类型方法,但是它放的位置与请求方法一致

路由模块化

含义:将原本可能写在一个文件中的路由规则,拆分成若干个路由文件(js文件,一个js文件就是一个模块)。

顾名思义,将路由进行模块化,以模块(js文件)为单位进行管理,物以类聚

核心思想:能拆就拆(拆到不能拆为止,解耦,高内聚,低耦合)。

在开发项目时,如果将所有的路由规则都挂载到入口文件中,程序编写和维护都变得更加困难。所以express为了路由的模块化管理功能,通过express.Router()方法创建路由模块化处理程序,可以将不同业务需求分开到不同的模块中,从而便于代码的维护和项目扩展。

步骤:

  1. 指定那个路径开头的请求,用app.use(路径开头,处理模块)

    app.use('/admin',adminRouter) // /admin开头的请求路径,交给adminRouter模块处理
    app.use('/front',frontRouter) // /front开头的请求路径,交给frontRouter模块处理
    
    
  2. 这个模块未定义,定义这个模块

    let {adminRouter} = require('./admin/admin')
    let {frontRouter} = require('./front/front')
    
    
  3. 导入的文件还未定义,创建文件

    const express = require('express')
    const adminRouter = express.Router() // 创建路由对象
    
    // 通过路由对象处理剩余的请求
    adminRouter.get('/goods',(req,res)=>{
        res.send('这是admin模块的goods页面')
    })
    
    // 导出模块
    module.exports = {adminRouter}
    
    

此时,我们又两种方案去处理请求:

  • app.get/post/use …
  • express.Router().get/post …
  • app.route(路径).get/post()

同一个请求路径使用不同的请求方法多次请求的简写:

app.route('/book')
  .get(function(req, res) {
    res.send('Get a random book');
  })
  .post(function(req, res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });

中间件

中间件(middleware)可以理解为业务流程的中间处理环节,可以理解成中间过滤器。

在这里插入图片描述

中间件的分类

中间件可以分类可分如下几类:

  • 内置中间件,也就是express本身自带无需npm安装

    • express.static()
  • 第三方中间件

    非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中可以通过npm进行安装第三方中间件并配置,从而提高项目的开发效率。例如body-parser (解析post数据的)此中间件可以很方便帮助我们获取到post提交过来的数据。

  • 自定义中间件,开发者自己编写的(中间件的本质其实就是一个function

如果从使用层面去考虑,中间件可以划分为:

  • 应用级别中间件(通过app.get/post/use等方法绑定到app实例的中间件)
    • 全局使用中间件(所有路由都生效)
      • app.use(中间件)
    • 局部使用中间件(当前路由生效)
      • app.请求方法(地址,[中间件…,]回调函数)
      • app.请求方法(地址,中间件1,中间2,中间3…,]回调函数)
  • 路由级别中间件(绑定到express.Router()上的中间件)
    • 其用法与应用级别的中间件没有任何区别,只是一个绑在app实例上,一个绑在router上
      • router.use(中间件)
      • router.请求方法(地址,[中间件…,]回调函数)

内置中间件

express提供了好用的内置中间件,如提供一个静态资源管理的中间件,通过此中间件就可以帮助为我们快速搭建一个静态资源服务器:

app.use('前缀',express.static('托管目录地址'))

在express中,除了内置的express.static()中间件,还内置了另外2个常用的中间件:

  • express.json()

    • 作用:接收json格式提交的数据
    • 兼容性问题:express >= 4.16.0
    • app.use(express.json())
    • 其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上

    使用示例:

    // 有了这个中间件以后,我们可以从客户端给服务器端发送json数据,这个json数据会放到请求对象req的body属性上
    app.use(express.json())
    
    app.post('/p1',(req,res)=>{
        // express.json中间件可以让数据挂在req的body属性上
        console.log(req.body);
        res.send('this p1 request is ended')
    })
    
    

    首先必须是post请求,然后必须有数据,但是数据不能是以前的 x-www-form-urlencoded这样的数据,必须是raw的数据

    然后请求头的content-type 必须是 application/json

  • express.urlencoded()

    • 作用:处理post表单数据
    • 兼容性问题:express >= 4.16.0
    • app.use(express.urlencoded({extended: false}))
    • 其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上

注意,

  • 后面提及的这2个常用内置中间件存在兼容性问题。
  • 上述2个中间件都说把数据处理之后挂到req.body上,但是实际上并不会出现我们想的覆盖的问题。

案例:使用json、urlencoded中间件来接收json数据与表单post数据,发送可以通过postman来进行

自定义中间件

自定义中间件,其本质就是定义一个处理请求的函数,只是此函数中除了有request和response参数外还必须包含一个next参数,此参数作用让中间件能够让流程向下执行下去直到匹配到的路由中发送响应给客户端。也可以通过给request对象添加属性来进行中间件数据的向下传递

function mfn(req,res,next){	
    //. 自己需要定义的逻辑流程
	// 中间件最后一定要执行此函数,否则程序无法向下执行下去
	next()
}

注意:在整个请求链路中,所有中间件与最终路由共用一份reqres

在这里插入图片描述

案例:依据上述的共用特性,自定义一个中间件来接收post提交的表单数据(意义:内置那中间件是不是存在兼容性问题)

第三方中间件

在express中,其允许我们使用第三方的中间件来进行对数据进行处理。比较典型的例如:可以使用第三方中间件来接收post数据。

以使用body-parser中间件来接收post数据为例,步骤如下:

  • 安装三方中间件body-parser
    • npm i -S body-parser
  • 应用文件中导入body-parser
  • 通过中间件调用 app.use(body.urlencoded({extended: false}))
  • 在匹配的路由中通过req.body获数post中数据
    • Express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的。但内置的有版本兼容问题,所以一般项目选择安装使用第三方的中间件

在使用的时候,body-parser库的语法与前面看的express内置的express.urlencoded中间件的语法非常相似,原因是内置中间件是基于body-parser来实现的。

其他第三方中间件:http-proxy-middleware/cors/cookie-session …

异常处理中间件

**作用:**专门用来捕获整个项目发生的异常错误,从而防止项目异常崩溃的问题产生(友好显示异常)。

**格式:**错误级别中间件的函数参数中,必须有四个形参,分别是(err,req,res,next)

问:多出来的err参数有什么作用呢?

答:里面包含了错误的信息,err.message属性中就包含了错误的文本信息,这个信息可以在中间件中输出给用户看。

app.get('/',(req,res) => {
    throw new Error('服务器内部发生了致命的错误!')
    res.send('Welcome to my homepage')
})

app.use((err,req,res,next) => {
    console.log('发生了错误:' + err.message)
    res.send('Error!' + err.message)
})

**案例:**要求指定一个路径(可能路由对应的文件不存在),读取文件内容,输出给用户

注意事项:错误级别中间件要想发挥其作用,必须写在所有的路由的后面,是否是app.listen之前无所谓。

404中间件

**作用:**用于处理404的请求响应

// 假设定义这个路由,但是实际请求的时候请求了/12345,这个时候就会404
app.post("/1234", (req, res, next) => {
    res.send('你请求成功了')
});

// 404的输出
// 该中间件也需要写在最后(与异常中间件的顺序无所谓,只要确保其在所有的路由方法之后就可)
app.use((req,res,next) => {
    // 输出404错误
    res.status(404).send('<h1>404</h1>')
    // 先指定404状态码,然后再输出错误信息
})

404错误中间件也要求在所有的正常请求路由的后面去声明使用,不要放在路由的前面,否则会导致后面的路由都是404错误。

**注意点:**错误级别的中间件,必须在所有路由之后注册,至于404中间件与异常中间件,谁先谁后无所谓。

模板页面:页面:https://404.life/

其他模块

cookie

cookie的原理是在浏览器中开辟了一个用来存储http请求中的数据,第一次保存之后,下次请求只要还是使用的当前浏览器,就能访问到浏览器这个空间中的数据。

cookie会作为键值对,在响应头和请求头之间携带。

cookie的特点:

  1. 域名限制,当前域名下设置的cookie,只能在当前域名下使用
  2. 时效性,cookie不会永久存储在浏览器,具有一定的有效期
  3. 数量限制,正常情况下,每个域名最多不能超过50个cookie
  4. 空间限制,cookie只能存储4kb
  5. 数据类型限制,cookie中只能存储字符串
npm i cookie-parser -S

使用:

// 导入
const cookieParser = require('cookie-parser')
// 中间件
app.use(cookieParser());
// 请求头获取
req.headers.cookie // 获取所有cookie
// 响应头设置
res.cookie(,,{maxAge: 有效期-毫秒}) // 设置cookie

session

cookie是存储在浏览器的,所以安全性不高,所以一些重要数据就不能存储在cookie中,且cookie的存储空间有限制,所以就有了session。

session是存储服务器端的,session需要依赖cookie,session数据存储会在cookie中存放一个sessionid,这个sessionid会跟服务器端之间产生映射关系,如果sessionid被篡改,他将不会跟服务器端进行隐射,因此安全系数更高。且session的有效期比较短。通常是20分钟左右,如果浏览器在20分钟内没有跟服务器进行交互,服务器就会删除session数据。

npm i cookie-session -S

使用:

// 导入:
const session = require('cookie-session')
// session配置
session({
    name:"sessionId",
    secret:"asdfasdfqwer", // 给sessioinId加密使用的秘钥,随便填写
    maxAge:20*60*1000 // 20分钟
})
// 设置session
req.session[] =// 获取session
req.session[]

加密

npm i bcryptjs -S

使用:

var bcrypt = require('bcryptjs');
// 加密
密文 = bcryptjs.hashSync(明文[,数字]); // 数字,将使用指定的轮数生成盐并将其使用。推荐 10
// 验证
bcryptjs一小时:手把手教你入门express建议收藏

强烈推荐,建议收藏一万字手把手教你学习Spring Cloud,带你快速入门

强烈推荐,建议收藏一万字手把手教你学习Spring Cloud,带你快速入门

半小时体验云原生:手把手教你在k8s上部署springboot应用——干货分享,建议收藏

半小时体验云原生:手把手教你在k8s上部署springboot应用——干货分享,建议收藏

十分钟手把手教你设计简单易用的组件级考试题(单选多选填空图片),建议收藏