热搜:前端 nest neovim nvim

一篇搞定Express

lxf2023-05-17 01:41:02

www.expressjs.com.cn/

  • Express是一个流行的Web应用程序框架,用于使用Node.js构建Web应用程序。它允许开发人员使用MVC(模型-视图-控制器)模式来构建可扩展的Web应用程序,其中路由器、中间件和模板引擎等组件使其易于编写和维护代码。

  • Express具有灵活的路由机制,可以轻松地处理URL和HTTP请求,与其他Node.js模块和中间件无缝集成,提供了许多有用的HTTP实用程序方法,例如获取HTTP请求正文、解析Cookie、设置响应头和重定向等。

  • Express拥有庞大的社区支持和广泛的文档资源,可以轻松地解决大多数编程问题。同时,它还提供了许多扩展和插件,例如Express-session、Passport、Multer等,可以轻松地实现身份验证、文件上传等功能。

1.运行原理

底层:http模块

Express框架建立在node.js内置的http模块上。http模块生成服务器的原始代码如下

var http = require("http");
​
var app = http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("hello 少七!");
});
​
app.listen(8888, "localhost");

Express框架的核心是对http模块的再包装。上面的代码用Express改写如下

var express = require('express');
var app = express();
​
app.get('/', function (req, res) {
  res.send('hello 少七!');
});
​
app.listen(3000, () => {
  console.log('App listening on port 3000!');
});

Express框架等于在http模块之上,加了一个中间层

什么是中间件

  • press中间件是指在处理HTTP请求过程中,可以插入一些模块化的处理函数,用于对请求进行预处理、响应处理等。中间件函数可以访问请求对象(req)、响应对象(res)和next函数,在处理HTTP请求过程中,将控制权交给下一个中间件函数
  • 简单说,中间件(middleware)就是处理HTTP请求的函数。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。App实例在运行过程中,会调用一系列的中间件
  • 每个中间件可以从App实例,接收三个参数,依次为request对象(代表HTTP请求)、response对象(代表HTTP回应),next回调函数(代表下一个中间件)。每个中间件都可以对HTTP请求(request对象)进行加工,并且决定是否调用next方法,将request对象再传给下一个中间件。
  • 一个不进行任何操作、只传递request对象的中间件,就是下面这样
function uselessMiddleware(req, res, next) {
  next();
}
  • 上面代码的next就是下一个中间件。如果它带有参数,则代表抛出一个错误,参数为错误文本
  • 抛出错误以后,后面的中间件将不再执行,直到发现一个错误处理函数为止
function uselessMiddleware(req, res, next) {
  next('抛出错误!');
}

2.Express 方法

2.1.Express路由简介

路由表示应用程序端点 (URI) 的定义以及响应客户端请求的方式。它包含一个请求方时(methods)、路径(path)和路由匹配时的函数(callback);

app.methods(path, callback);

app 是一个express的实例对象,methods 是HTTP请求方法(例如GET、POST、PUT、DELETE等),path 是路由路径(例如/users),callback 是在路由匹配时执行的函数。

2.2.Express路由方法

Express的路由方法指定了应用程序响应请求的HTTP方法和请求URL的组合。这些方法根据HTTP请求的不同类型来指定,包括GET、PUT、POST、DELETE等

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构如下: app.METHOD(path, [callback...], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数。

下面是一个基本的路由示例:

var express = require('express');
var app = express();
​
app.get('/', (req, res) => {
  res.send('GET request to the homepage');
});

app.post('/', (req, res) => {
  res.send('POST request to the homepage');
});

app.put('/user', (req, res) => {
  res.send('PUT request to the homepage');
});

app.delete('/user', (req, res) => {
  res.send('DELETE request to the homepage');
});

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
  //写完一个send,后面所有跟路由有关的都不会执行
  //会自动响应对应的数据类型
  //   res.send([1, 2, 3]);
  //   res.send({ ok: 1 });
  //   res.json({ ok: 1 });
   // 使用混合使用函数数组处理时如果前面有res.send();那么后面和路由处理相关代码都不生效
  res.send('hello world');
  res.send(`
        <html>
            <h1>hello world</h2>
        </html>
    `);
});

路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。

2.3.all方法和HTTP动词方法

针对不同的请求,Express提供了use方法的一些别名。比如,上面代码也可以用别名的形式来写

var express = require("express");
var http = require("http");
var app = express();
​
app.all("*", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  next();
});
​
app.get("/", function(request, response) {
  response.end("Welcome to the homepage!");
});
​
app.get("/about", function(request, response) {
  response.end("Welcome to the about page!");
});
​
app.get("*", function(request, response) {
  response.end("404!");
});
​
http.createServer(app).listen(1337);
  • 上面代码的all方法表示,所有请求都必须通过该中间件,参数中的“*”表示对所有路径有效。get方法则是只有GET动词的HTTP请求通过该中间件,它的第一个参数是请求的路径。由于get方法的回调函数没有调用next方法,所以只要有一个中间件被调用了,后面的中间件就不会再被调用了
  • 除了get方法以外,Express还提供post、put、delete方法,即HTTP动词都是Express的方法
  • 除了get方法以外,Express还提供post、put、delete方法,即HTTP动词都是Express的方法
  • 这些方法的第一个参数,都是请求的路径。除了绝对匹配以外,Express允许模式匹配
app.get("/hello/:who", function(req, res) {
  res.end("Hello, " + req.params.who + ".");
});

3.路径匹配

3.1.字符串路径

// 匹配根路径的请求
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');
});

3.2.字符串模式路径

使用字符串模式的路由路径示例:

// 匹配 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');
});

3.3.正则表达式路径

使用正则表达式的路由路径示例:

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function(req, res) {
  res.send('/a/');
});
​
// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.*fly$/, function(req, res) {
  res.send('/.*fly$/');
});

可以为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next('route') 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。

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

使用多个回调函数处理路由(记得指定 next 对象):

app.get('/example/b', function (req, res, next) {
  console.log('response will be sent by the next function ...');
  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])

4.response对象

方法描述
res.download()提示要下载的文件。
res.end()结束响应过程。
res.json()发送JSON响应。
res.jsonp()发送带有JSONP支持的JSON响应。
res.redirect()重定向请求。
res.render()渲染视图模板。
res.send()发送各种类型的响应。
res.sendFile()将文件作为八位字节流发送。
res.sendStatus()设置响应状态代码,并将其字符串表示形式发送为响应正文。

例:

(1)response.redirect方法

response.redirect方法允许网址的重定向

response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com"); 

(2)response.sendFile方法

response.sendFile方法用于发送文件

response.sendFile("/path/to/anime.mp4");

(3)response.render方法

response.render方法用于渲染网页模板。

//  使用render方法,将message变量传入index模板,渲染成HTML网页
app.get("/", function(request, response) {
  response.render("index", { message: "Hello World" });
});

5.路线处理程序

可以提供行为类似于中间件的多个回调函数来处理请求。唯一的例外是这些回调可能会调用next('route')以绕过其余的路由回调。您可以使用此机制在路由上施加先决条件,然后在没有理由继续使用当前路由的情况下将控制权传递给后续路由。

多个回调函数可以处理一条路由(确保指定了next对象)。例如:

app.get('/example/b', function (req, res, next) {
  console.log('the response will be sent by the next function ...')
  next()
}, function (req, res) {
  res.send('Hello from B!')
})

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

const fun1 = (req, res, next) => {
  // 验证用户token过期, cookie过期
  console.log('token验证');
  let isValid = true;
  if (isValid) {
    next();
  } else {
    //将第一个中间件的数据传输到第二个中间件
    res.name = "dselegent";
    res.send('error');
  }
};
const fun2 = (req, res) => {
   console.log(res.name)
  res.send('home');
};
app.get('/home', [fun1, fun2]);
​
app.get('/list', fun1, (req, res) => {
  res.send('list');
});

6.Express中间件

Express中间件是指在处理HTTP请求过程中,可以插入一些模块化的处理函数,用于对请求进行预处理、响应处理等。中间件函数可以访问请求对象(req)、响应对象(res)和next函数,在处理HTTP请求过程中,将控制权交给下一个中间件函数

中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 web 应用中处于请求-响应循环流程中的中间件,一般被命名为 next 的变量。

中间件的功能包括:

  • 执行任何代码。
  • 修改请求和响应对象。
  • 终结请求-响应循环。
  • 调用堆栈中的下一个中间件。

如果当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,否则请求就会挂起。

Express 应用可使用如下几种中间件:

  • 应用级中间件
  • 路由级中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件
  • 自定义中间件

使用可选则挂载路径,可在应用级别或路由级别装载中间件。另外,你还可以同时装在一系列中间件函数,从而在一个挂载点上创建一个子中间件栈。

6.1.自定义中间件

例如处理请求日志、认证授权、错误处理等。以下是一个自定义中间件的示例:

const express = require('express');
const app = express();

// 自定义中间件函数
function myMiddleware(req, res, next) {
  console.log('Middleware function is called');
  next(); // 将控制权交给下一个中间件函数
}

app.use(myMiddleware); // 使用自定义中间件函数

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000/');
});

在上面的示例中,首先定义了一个自定义中间件函数myMiddleware,该函数在处理请求时,将控制台输出“Middleware function is called”。然后使用app.use方法将该中间件函数添加到请求处理链中。当客户端发送请求时,该中间件函数会被自动调用,在输出完中间件函数的信息之后,将控制权交给下一个中间件函数或路由处理函数。

6.2.应用级中间件

应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。例如:

var app = express()
const indexRouter = require('./route/indexRouter');
const LoginRouter = require('./route/LoginRouter');
​
//应用级别(后面的路由都会执行此中间件)
app.use((req, res, next) => {
  // 验证用户token过期, cookie过期
  console.log('token验证');
  let isValid = true;
  if (isValid) {
    next();
  } else {
    res.send('error');
  }
});
​
//应用级别(这里不写路径默认/)
//这些use方法是每次访问都是从上往下执行
//如果是/login/a,会先找到/login开头的这个应用级中间件
//然后再进入这个中间件找/a
app.use(indexRouter);
app.use('/login', LoginRouter);
​

6.3.路由级中间件

app.route()

与应用级中间件类似,路由级中间件可以用于处理特定路由的请求。路由级中间件可以通过Router对象实现,与应用级中间件类似使用app.use和app.METHOD方法添加,不同之处在于需要先创建一个Router对象,然后通过该对象来添加路由和中间件。

这是使用定义的链式路由处理程序的示例app.route()

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')
  })

快速路由器

路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()

使用express.Router该类创建模块化的,可安装的路由处理程序。一个Router实例是一个完整的中间件和路由系统; 因此,它通常被称为“迷你应用程序”。

以下示例将路由器创建为模块,在其中加载中间件功能,定义一些路由,并将路由器模块安装在主应用程序的路径上。

home.js在app目录中创建一个名为以下内容的路由器文件:

var app = express()
var router = express.Router()
​
// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件
router.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})
​
// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl)
  next()
}, function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})
​
// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get('/user/:id', function (req, res, next) {
  // 如果 user id 为 0, 跳到下一个路由
  if (req.params.id == 0) next('route')
  // 负责将控制权交给栈中下一个中间件
  else next() //
}, function (req, res, next) {
  // 渲染常规页面
  res.render('regular')
})
​
// 处理 /user/:id, 渲染一个特殊页面
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id)
  res.render('special')
})
​
module.exports=  router

然后,在应用程序中加载路由器模块:

var indexRouter = require('./home')
// ...
app.use('/home', index)

该应用程序现在将能够处理对/home和的请求/home/user/123456

router.route方法

router实例对象的route方法,可以接受访问路径作为参数

var router = express.Router();
​
router.route('/api')
    .post(function(req, res) {
        // ...
    })
    .get(function(req, res) {
        Bear.find(function(err, bears) {
            if (err) res.send(err);
            res.json(bears);
        });
    });
​
module.exports=  router

6.4.错误处理中间件

在应用程序中使用中间件,可能会涉及到各种问题,例如请求超时、无效的请求数据、权限不足等。虽然这些问题通常已经在需求和代码中得到处理,但在无法避免出现问题时,可以使用错误处理中间件来捕捉这些问题和错误,向客户端发送错误响应或记录错误日志。

//上面的中间件都没有匹配就会走这里
app.use(function(err, req, res, next) {
  console.error(err.stack)
     //send的状态码默认是200
  res.status(500).send('error')
})

6.5.内置的中间件

express.static 是 Express 唯一内置的中间件。它基于 serve-static,负责在 Express 应用中提托管静态资源。每个应用可有多个静态目录。

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

6.6.第三方中间件

安装所需功能的 node 模块,并在应用中加载,可以在应用级加载,也可以在路由级加载。

下面的例子安装并加载了一个解析 cookie 的中间件: cookie-parser

$ npm install cookie-parser
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
​
// 加载用于解析 cookie 的中间件
app.use(cookieParser())

7.获取参数

在Express中,有多种方法可以获取请求中传递的参数。这些方法的选择取决于请求的类型和参数的格式。以下是几种常见的方法:

7.1.req.params

路由路径可以包含参数,用冒号(“:”)表示,如 /users/:id。当使用这种路径时,参数值可以通过 req.params 对象进行访问。参数的名称是在路径中定义的参数名称。以下是一个示例:

const express = require('express');
const app = express();

app.get('/users/:id', (req, res) => {
  res.send(`User ID: ${req.params.id}`);
});

// 访问 http://localhost:3000/users/123 将输出 "User ID: 123"

7.2.req.query

查询参数是在URL的“?”后面的参数,比如搜索时使用的查询参数。可以使用 req.query 对象来获取这些参数。 以下是一个示例:

const express = require('express');
const app = express();

app.get('/search', (req, res) => {
  const query = req.query.q;
  res.send(`Search query: ${query}`);
});

// 访问 http://localhost:3000/search?q=express 将输出 "Search query: express"

7.3.req.body

当POST或PUT请求包含表单数据或JSON数据主体时,可以通过 req.body 对象获取该数据。要使用 req.body,应用程序必须使用body-parser中间件(可从npm获取)或express.json() 内置中间件。 以下是一个示例:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.post('/login', (req, res) => {
  const username = req.body.username;
  const password = req.body.password;
  // 在这里验证用户名和密码
  res.send(`Logged in as ${username}`);
});

// 使用POSTMAN或HTML表单将"username"和"password"提交到http://localhost:3000/login

以上就是几种常用的获取请求参数的方法,可以根据请求类型和参数格式选择适当的方法。

8.利用 Express 托管静态文件

通过 Express 内置的 express.static 可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。

将静态资源文件所在的目录作为参数传递给 express.static 中间件就可以提供静态资源文件的访问了。例如,假设在 public 目录放置了图片、CSS 和 JavaScript 文件,你就可以:

//直接将public里的index.html当成/的网页
app.use(express.static('public'))

现在,public 目录下面的文件就可以访问了。

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

所有文件的路径都是相对于存放目录的,因此,存放静态文件的目录名不会出现在 URL 中。

如果你的静态资源存放在多个目录下面,你可以多次调用 express.static 中间件:

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

访问静态资源文件时,express.static 中间件会根据目录添加的顺序查找所需的文件。

如果你希望所有通过 express.static 访问的文件都存放在一个“虚拟(virtual)”目录(即目录根本不存在)下面,可以通过为静态资源目录指定一个挂载路径的方式来实现,如下所示:

app.use('/static', express.static('public'))

现在,你就可以通过带有 “/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(express.static('public'))
<link rel="stylesheet" href="/css/index.css" />
    
app.use('/static', express.static('public'))
<link rel="stylesheet" href="/static/css/index.css" />
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!