三、npm包管理

1、介绍

npm是NodeJs项目模块管理工具,它已经集成了nodejs安装包中(自5.2以后新增了npx指令用于解决调用项目内部安装的模块繁琐的问题),使用npm可以实现从NPM服务器下载别人编写的第三方包到本地使用。

npm仓库地址:npm

当然除了npm以外还有Facebook贡献的Yarn,功能和npm一样,也可以作为包管理工具。

问题:npm帮我们下载和安装的包,计算机关机后再开机还是否可以继续使用?

npx(了解)

  • 为了临时帮我们去做安装操作(react)

  • 更好的执行模块的内部命令

node-modules/.bin/mocha --version
# 简化成了:
npx mocha --version

2、切换npm源(可选)

源:源站、镜像源、镜像(专门提供npm包下载资源的服务器)。

npm使用国外镜像源地址,再有的时候可能网络不是很通顺,这时可以使用国内镜像源来完成npm下载模块功能。

  • 切换到阿里提供的npm镜像源(方式1)

地址:NPM镜像-NPM下载地址-NPM安装教程-阿里巴巴开源镜像站

npm install -g cnpm --registry=https://registry.npm.taobao.org

执行完毕上述命名后,在系统中提供了一个cnpm包管理工具,功能和npm一样,所不同的是cnpm镜像源地址为阿里提供的源地址。

推荐,日后工作是安装软件都使用cnpm

  • 使用nrm管理npm镜像源(方式2,与方式1任选其一

nrm 是一个 npm 源管理器,允许你快速地在 npm源间切换。npm默认情况下是使用npm官方源(npm config list 来查看),如果直接修改npm源,如果后续需要连接到官方源才能工作,这样来回切换源就变得麻烦了,nrm通过简单的命令就可以解决此问题。



# 安装  通过cnpm来安装,cnpm使用的就是国内镜像源
npm i -g nrm
# i:install
# -g:global,全局安装(后续不管在什么地方打开命令行都可以使用该命令)

注:-g表示global全局,让nrm不限于到某一个项目中,而是在所有的项目中都可使用

安装好nrm后可以通过nrm ls命令来查看效果:

*表示当前正在使用的镜像源

如果想切换成其他的镜像源,可以通过nrm use 源名称进行切换,如需要切换成edunpm的源,则可以执行指令:



nrm use edunpm

3、npm相关命令



# 初始化生成package.json文件(创建项目的)
npm init -y[不询问]
npm init
# 查看本项目已安装模块
npm list
# 安装模块
npm install 模块名[@版本号 可选]  或  npm i 模块名[@版本号 可选]
# 卸载已安装模块
npm uninstall 模块名
# 查看全局node_modules的地址
npm root -g
## 安装参数
##--save -S     记录生产环境所需模块 默认
##--save-dev -D 记录开发环境所需模块
##-g 安装到全局
# 生产环境:代码已经上线了的运行环境
# 开发环境:开发人员在开发的时候环境

4、自定义npm命令

目的:npm允许我们执行npm以调用三方的模块,但是由于通过npm调用三方模块的指令写起来比较长,而且可能频繁被使用,这样用起来非常麻烦,因此可以通过自定义命令对原先非常长的命令做一个简化(别名)。

通过package.json文件中的scripts自定义脚本命令:



{
  "scripts": {
    "test": "echo hello"
  }
}

随后就可以在命令行中运行(npm run 自定义指令名称):



npm run test

如果需要更多的自定义命令,只需要按照上述的格式进行套用即可。

后续的vue框架中就会使用到,还有webpack也会用到。

5、自动重启应用

在编写调试Node.js项目,修改代码后需要频繁的手动重启应用,非常繁琐。nodemon这个工具,它的作用是监听代码文件的变动,当代码改变之后,自动重启



# 全局安装nodemon
npm i -g nodemon
# 执行node脚本
nodemon app.js

该工具应用非常广泛,后续的框架也是这样的应用。

四、Express

1、介绍

网址:Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网

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

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

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

2、基本使用

2.1、安装

在项目目录中,打开cmd命令窗口,执行如下命令:



npm init -y     # 如果没有项目则先初始化
npm i -S express

2.2、创建web服务

基本遵循之前的四个步骤:

  • 导入需要使用的express包

  • 创建web实例

  • 定义允许访问的地址(定义路由)【重复性步骤】

    • 原先的输出:res.end()

    • 现在的输出:res.send()

  • 启动服务(监听端口)



const express = require('express')
// 创建web服务
const app = express()
// 监听 get请求
// req 请求对象
// res 响应对象
app.get('请求URI',(req,res)=>{
    // 向客户端响应数据
    res.send({id:1,name:'张三'})
})
// ....
// 监听POST请求
app.post('请求URI',(req,res)=>{})
// 其他app.形式的api方法,put/delete/use 等
app.put()
app.delete()
// ....
// 启动服务
app.listen(8080,()=>{})

案例:手写一个服务器软件,启动后要求用户访问根“/”输出hello world,用户访问/html5输出h5,用于通过post方式访问/post则输出post


// 1. 导入模块
const express = require("express");
// 2. 创建web实例
const app = express();
// 3. 监听请求
// 用户访问根“/”输出hello world
app.get("/", (req, res) => {
    // res.end('xxx')
    res.send("hello world");
});
// 用户访问/html5输出2003
app.get("/html5", (req, res) => {
    res.send("h5");
});
// 用于通过post方式访问/post则输出post。
app.post("/post", (req, res) => {
    res.send("post方式");
});
// 4. 启动服务
app.listen(8080, () => {
    console.log("server is running at http://127.0.0.1:8080");
});

地址在匹配的时候是自上而下,必须同时匹配方法路径

2.3、访问调试(了解)

在测试请求的时候,GET类型的请求我们可以直接通过地址栏访问URL来进行,对于POST/PUT/DELETE等特殊类型的请求,我们往往无法通过直接访问地址来实现,这个时候就需要使用三方测试工具了。常见的测试工具有:

  • postman

  • apipost

  • apizza

以上三个,任选其一即可,其中第三个为在线版本。后续课程中以postman为例。

在后续的前后端分离式开发中,常见的增删改查不再是纯粹的GET+POST的两种请求方式,而是分的更加细化:(==restful规范==,规范不是标准,不必一定执行)

  • GET:查询请求类型

    • 取全部的数据(列表)

      • http://www.abc.com/manager/user

    • 取单个的数据(详情)

      • http://www.abc.com/manager/user/225

  • POST:新增请求类型

    • 新增是不带条件

      • http://www.abc.com/manager/user

  • PUT:修改请求类型

    • 修改是要条件的

    • 修改条件的传递是通过地址栏传递的(restful规范)

      • http://www.abc.com/manager/user/225

    • 修改的数据主体是通过请求体传递的(请求体发送方式与post一致)

  • DELETE:删除请求类型

    • 删除需要条件的

      • http://www.abc.com/manager/user/225

    • 条件通过地址栏传递的

这种对请求方法(请求动词)约束的规范叫做restFul规范(整理什么是restful)。该规范不是硬性要求,但是接口设计的时候一般都会遵守,其规范的不仅仅是请求方式,在请求地址形式、响应状态码等方面也存在规范要求(具体的要求项目时再说)。

在Express中,符合restFul规范的请求动词也是被支持的,例如如下代码:



const express = require('express')
const app = express()
app.get('/',(req,res) => {
    res.send('get请求')
})
app.post('/',(req,res) => {
    res.send('post请求')
})
app.put('/',(req,res) => {
    res.send('put请求')
})
app.delete('/',(req,res) => {
    res.send('delete请求')
})
app.listen(8080,() => {
    console.log('Server is running at http://127.0.0.1:8080')
})

注意点:如果有地址都一样,但是需要支持所有的请求动词这种需求,则可以简写成以下代码:



app.all('/',(req,res) => {
// 业务代码,只要路径匹配上即可
})

but,这种方式尽量少用!!甚至别用。

2.4、获取query字符串

获取get传值的参数。

通过 req.query 对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数:



app.get('/',(req,res)=>{
    console.log(req.query)  // 获取到的直接就是个对象
})

2.5、动态参数传递

Express也支持类似于Vue中动态路由的形式传递参数,传递的参数通过 req.params 对象可以访问到:



// 必须的路由参数(不传就匹配不上,返回404错误)
app.get('/:id',(req,res)=>{
    console.log(req.params.id)
})
// 可选的路由参数(传递与否都不会报错)
app.get('/:id?',(req,res)=>{
    console.log(req.params.id)
})

这个传参方式是符合restful传参规范的。

2.6、静态资源托管

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()方法一般写在具体的路由监听之前。

3、路由

3.1、概述

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

3.2、定义路由

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

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



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

3.3、路由模块化(重点)

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

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

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

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

路由模块化处理可以分为以下步骤来完成

  • 创建独立js空白文件(最后是统一放在一个目录下,一般是router目录)

    • 该文件即路由模块化文件

  • 在第一步js文件中使用express.Router()方法创建路由模块对象

  • 使用路由对象完成路由规则的对应的业务编写

  • 使用模块化导出(module.exports = router

  • 在主入口文件中能够使用app.use方法来注册定义的路由模块

    • 支持前缀的使用

    • app.use("前缀",路由模块化路由)

在vue中,路由模块的思路和这个是一模一样的。(区别在于nodejs遵循commonjs规范,vue遵循的是es6的模块化规范)

步骤1:创建出空白的js文件(JavaScript的模块化文件)

backend.js:后台路由集合文件

frontend.js:前台路由集合文件

步骤2:在第一步js文件中使用express.Router()方法创建路由模块对象

步骤3:使用路由对象完成路由规则的对应的业务编写

步骤4:使用模块化导出

仿照其中写好的模块化文件backend.js快速把前台的frontend.js写好,得到如下代码:

步骤5:在主入口文件中能够使用app.use方法来注册定义的路由模块

测试:几个分模块的接口地址都可以正常的被访问到

4、中间件(重点)

4.1、含义

中间件(middleware)可以理解为业务流程的中间处理环节。如生活中吃一般炒青菜,大约分为如下几步骤:

express中,当一个请求到达的服务器之后,可以在给客户响应之前(回调函数之前)连续调用多个中间件,来对本次请求和返回响应数据进行处理

  • 所处的位置:请求之后,响应之前,由于其在两者之间,所以被称之为中间件

  • 中间件可以有多个,也可以有一个,也可以没有

  • 中间件有什么用:处理数据

4.2、中间件的分类

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

  • 内置中间件,也就是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.请求方法(地址,[中间件.....,]回调函数)

4.3、内置中间件

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



app.use('前缀',express.static('托管目录地址'))   //前缀我们叫虚拟目录

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

  • express.json()

    • 作用:接收json格式提交的数据

    • 兼容性问题:express >= 4.16.0

    • app.use(express.json())

    • 其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上

  • express.urlencoded()

    • 作用:处理post表单数据

    • 兼容性问题:express >= 4.16.0

    • app.use(express.urlencoded({extended: false}))

    • 其在接收完数据后,会将数据的对象形式挂载到req请求对象的body属性上

    • 所有中间件处理数据都是挂在req.body上

注意,

  • 后面提及的这2个常用内置中间件存在兼容性问题。

  • 上述2个中间件都说把数据处理之后挂到req.body上,但是实际上并不会出现我们想的覆盖的问题。

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

注意:在使用postman发送JSON格式数据的时候请按照以下图示进行

并且需要注意,JSON数据数据名必须用双引号引起来,值如果是字符串的话也需要用双引号引起来,单引号不行!!!

以express.json()为例:

以express.urlencoded()中间件为例,如果发送post请求,则postman应该这样的设置:



// 局部中间件,处理表单提交的数据
app.post("/post", express.urlencoded({ extended: false }), (req, res) => {
    // 输出得到的请求体
    console.log(req.body);
});

注意:关于urlencoded中间件中的配置项extended值的说明

  • 值默认为true,但是不建议使用默认的true

  • 值true与false的区别

    • false:使用querystring库去解析post数据

      • 去除获取到的数据对象的内置方法

      • 接收到的数据只有字符串与数组的形式

    • true:使用qs库去解析post数据

      • 使得获取到的数据对象更加面向对象化

4.4、自定义中间件

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



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

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

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

需求:自己手动模拟一个类似于express.urlencoded这样的中间件,以解析post提交到服务器的表单数据。

步骤分析:

  • 定义中间件(本质:函数)

  • 监听req的data事件

    • 在中间件中,需要监听req对象的data事件来获取客户端发送到服务器的数据。如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后分批次发送给服务器。所以data事件可能会被触发多次,每次触发data事件时,收到的数据只是全部数据的一部分,因此需要做数据的拼接才能得到完整的数据。

  • 监听req的end事件

    • 当请求体数据传输完毕后会触发end事件,拿到全部数据后可以继续处理post数据

  • 使用querystring模块来解析请求体数据

  • 将解析出来的请求体对象挂载到req.body上

  • 将自定义中间件封装为模块(可选,建议做)

    • 为了优化代码的结构,我们可以把自定义的中间件函数封装成独立的模块

实现代码

独立的自定义中间件模块:cs-body-parse



// 引入querystring模块
const querystring = require("querystring")
// 核心代码
var csBodyParse = (req, res, next) => {
    let data = []
    req.on("data", (buffer) => {
        data.push(buffer)
    })
    req.on("end", () => {
        let post = querystring.parse(Buffer.concat(data).toString())
        // 挂载到req.body上
        req.body = post
        // 继续后续的请求处理
        next()
    })
}
// 导出
module.exports = csBodyParse

应用入口文件:app.js



// 自定义中间件服务器文件(入口文件)
const express = require('express')
const app = express()
// 引入自己封装的中间件模块cs-body-parse
const csBodyParse = require('./modules/cs-body-parse')
// csBodyParse的使用
app.use(csBodyParse)
// 路由
app.post('/post',(req,res) => {
    console.log(req.body)
})
app.listen(3722,() => {
    console.log('Server is running at http://127.0.0.1:3722')
})

4.5、第三方中间件

在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来实现的。



const express = require('express')
const app = express()
// 引入body-parser
const bodyParser = require('body-parser')
// 使用body-parser中间件
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/post',(req,res) => {
    console.log(req.body);
})
app.listen(3723,() => {
    console.log("Server is running at http://127.0.0.1:3723")
})

==作业:实现一个自定义中间件,要求能够记录用户访问信息,访问信息记录在logs/目录下的.log文件中,文件的名称以当天的日期进行命名,例如“20210421.log”,要求里面的访问记录一条一行,记录以下信息:用户访问时间、访问路径、访问请求类型、访问的浏览器头信息、访问者IP地址==



// 1. 导包
const fs = require("fs");
const path = require("path");
// 时间处理包(替代date对象的使用)
// 安装:npm i moment
const moment = require("moment");
// 2. 中间件的自定义函数
function log(req, res, next) {
    // 用户访问时间、访问路径、访问请求类型、访问的浏览器头信息、访问者IP地址
    // moment语法:moment([时间戳,不写默认当前时间戳]).format(时间格式表达式)
    // 格式:YYYY表示四位年份,MM表示两位的月份,DD表示两位的日期,HH表示小时,mm表示两位分钟,ss表示两位秒数。具体其它的字母表达式可以查看官网:http://momentjs.cn/docs/#/displaying/
    let time = moment().format("YYYY-MM-DD HH:mm:ss");
    let url = req.url;
    let method = req.method;
    let userAgent = req.headers["user-agent"];
    // req.ip默认获取的是ipv6形式
    let ip = req.ip;
    let str = `${time} - ${url} - ${method} - ${userAgent} - ${ip}\n`;
    // 生成文件名称
    let fname = moment().format("YYYYMMDD") + ".log";
    // console.log(__dirname);
    let fpath = path.join(__dirname, "../logs", fname);
    // 写入文件
    fs.appendFileSync(fpath, str);
    next();
}
// 3. 导出中间件
module.exports = log;

4.6、错误类型中间件

4.6.1、异常中间件

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

格式:错误级别中间件的函数参数中,必须有四个形参,分别是(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)
})

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

默认情况下,程序如果崩溃了,会出现大致如下的错误:

这样的错误显示,既不友好,也不安全。以下是经过异常中间件处理好的显示页面:

整体代码如下:



// 引入需要的模块
const express = require('express')
const fs = require('fs')
const app = express()
// 路由
app.get('/',(req,res) => {
    let url = 'readme1.txt'
    // 读文件
    try{
        // 先去尝试一下读文件
        let data = fs.readFileSync(url)
        res.send(data)
    }catch(e){
        // 尝试失败,抛出异常
        throw new Error('文件读取失败')
    }
})
// 异常中间件
app.use((err,req,res,next) => {
    // 接受异常,继续处理异常
    res.send('ERROR:' + err.message)
})
// 启动服务器
app.listen(3724,() => {
    console.log("Server is running at http://127.0.0.1:3724");
})

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

4.6.2、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中间件与异常中间件,谁先谁后无所谓。

页面:404模板网_重新定义404页面

4.7、中间件的局部使用

  • app.请求方法(地址,[中间件.....,]回调函数)

    • 3个参数

      • url,请求地址

      • 数组,多个中间件的集合

      • 回调函数,用于处理请求给出响应



app.post('/',[express.json(),express.urlencoded({extended:false})],(req,res) => {
    
})
  • app.请求方法(地址,中间件1,中间件2,.....,回调函数)

    • 至少3个参数,至多N+2(N为中间件的个数)



app.post('/',express.json(),express.urlencoded({extended:false}),(req,res) => {
    
})

4.8、小结

在express中使用中间件时,需要注意以下几点:

  • 在定义路由之前注册中间件(除了错误中间件)

  • 一个请求中可以连续调用多个中间件

  • 在一次请求链路中,多个中间件与最终路由方法中,他们共用一份reqres

  • 每个中间件最后必须调用next()(除了错误中间件,必须要有参数传递)

  • 不要在中间件的next()后继续写任何代码(没意义)

  • 404中间件与异常中间件的顺序无所谓(只要确保都在路由后面即可)

Day 73 Npm包管理 +Express+路由+中间件相关推荐

  1. express 路由中间件_Express通过示例进行解释-安装,路由,中间件等

    express 路由中间件 表达 (Express) When it comes to build web applications using Node.js, creating a server ...

  2. NPM包管理器跟换国内镜像CNPM

    NPM包管理器跟换国内镜像 CNPM npm 是node.js 环境下的包管理器,非常强大智能. 生活这这片神奇的土地上,各种奇葩手段屡见不鲜啊. 为什么要换源? npm 官方站点 http://ww ...

  3. npm 包管理器_导演电影解释了节点软件包管理器(NPM)

    npm 包管理器 by Kevin Kononenko 凯文·科诺年科(Kevin Kononenko) If you understand the general way that Hollywoo ...

  4. npm 包管理器_纱包管理器:npm的改进

    npm 包管理器 From bower to npm, package management for the web has come a long way. 从Bower到npm ,Web的软件包管 ...

  5. Node.js学习之路--npm包管理工具操作汇总

    因为在学习使用Express(第三方Web开发框架)时需要下载这个包,所以使用npm包管理工具这个相关的操作,现在将其相关的操作进行汇总. npm命令在cmd或者powershell中进行;注意:使用 ...

  6. 前端入门(二)npm包管理+模块化+bable转码器+webpack打包+vue-element-admin

    一.npm包管理工具 NPM全称Node Package Manager,是Node.js包管理工具,相当于前端的Maven .不需要单独安装,和nodejs一起 在命令提示符输入 npm -v 可查 ...

  7. (18) Node.js npm包管理工具

    一.npm概述 npm (Node Package Manager)是 Node.js 的包管理工具. 什么是包?包就是一坨代码,就是 Node.js 的第三方模块. 例如:JQuery模块,Boot ...

  8. node.js中npm包管理工具

    现在安装node.js,默认就会帮我们装上了npm包管理工具,npm主要用来下载,安装,管理第三方模块. 创建一个包描述文件: npm init [-y] 查看包的信息 npm info <pa ...

  9. ❤️《大前端—NPM包管理器》

    <大前端-NPM包管理器> 1.简介 ​ 官方网站:https://www.npmjs.com/ ​ NPM全称Node Package Manager,是Node.js包管理工具,是全球 ...

最新文章

  1. MySQL(MariaDB)常用DOM命令
  2. 简明深度学习方法概述 Deep Learning:Methods and Application
  3. 矩形并的面积(51Nod-2488)
  4. zabbix 自定义监控项 获取nginx监控状态
  5. MySQL锁定机制简介
  6. Basis Cash动态铸币税阈值和债券发行上限提案正式生效
  7. 时序动作定位:Rethinking the Faster R-CNN Architecture for Temporal Action Localization(TAL-Net)
  8. Github Trending被中文项目“占领”,国外开发者不开心了!
  9. 吴恩达神经网络和深度学习-学习笔记-24-在不同的划分上进行训练和测试+不匹配数据划分的偏差和方差
  10. 内层元素设置position:relative后父元素overflow:hidden overflow:scroll失效 解决方法
  11. 阿里矢量图标库 - 如何修改和使用自定义字体名称
  12. Packet Tracer官网下载
  13. 三毛3--亲爱的婆婆大人
  14. 做出胶卷的效果——图片循环滚动播放
  15. js 改变匹配到的字符串的颜色 — replace
  16. Merge用法:Oracle 10g中对Merge语句的增强
  17. STM32学习笔记(16)-数据的保存与毁灭-BKP功能(1)
  18. C++多线程启动、暂停、继续与停止
  19. 战斗系统之属性设计(一)
  20. etcc oracle ebs,Oracle EBS日志 log

热门文章

  1. oracle cosh 函数,oracle cosh exp floor in log等函数
  2. windows10如何安装IIS6.0
  3. python中setup是什么意思_python中setuptools指的是什么
  4. BeautifulSoup安装
  5. Neuroimage | 孤独症与功能近红外光谱(fNIRS)综述
  6. 分享Python 的十个小技巧
  7. Windows的EventLog
  8. android 源码下载
  9. CSAT:你想要了解的顾客满意都在这里
  10. 数据库原理——关系模式的范式的简明判断