express的基础使用,利用postman模拟后端路由

简介

Node.js 使 JavaScript\TypeScript 脚本能够脱离浏览器环境在服务端(后端)运行(实际上是对 Chrome V8 引擎进行了封装),为我们开发后端提供了一种选项。不像前端有统一的浏览器标准,如果不遵循的话浏览器就没法正常显示;后端的开发相对就自由许多,开发语言有很多选项,如 Java,PHP,python,C,Go 等,我们科协网站采用的是 TypeScript 语言,有关 TypeScript 的知识在之前课程当中已有介绍。

如想自己动手建立一个简单后端,理论上只需调用 http-server 之类的库就能接收和发送 http 请求了。尽管这些库已经帮我们处理了很多底层问题,但是要想实现复杂一些的功能还是相当麻烦的。Express 是一个基于 Node.js 的开源框架,把接收和发送 http 请求进行了更高级别、更用户友好的封装。官网上称其“快速、开放、极简”,表明 Express 在保证性能的同时,代码书写非常容易且是开源的。它能够使用开发者所选择的各种 http 实用工具和中间件,快速方便地创建强大的 API。

配置环境

安装express

使用yarn来安装,如下

yarn init#取号名字,一路默认
yarn add express

准备工作

在刚才创建的安装有 Express 的工作目录当中,我们新建一个文件夹 src,用于保存源码,并在其中新建入口文件 app.ts。然后在其中导入 Express 包并创建 Express 类的实例:

import express from "express"; // 导入Express包
const app = express(); // 创建Express类的实例

这样导入的 Express 包是 JavaScript 编写的,缺乏 TypeScript 语法所要求的类型声明文件,因此会在 import 那一行出现提醒。我们可以使用如下命令安装类型声明:

yarn add @types/express --dev

安装完成后,我们看到提醒已经消除。

之后我们让程序监听 3000 端口,这样发送至 http://localhost:3000 的请求就能够被我们捕获了。

const port = 3000
app.listen(port, () => {// 开始监听console.log(`listening on port ${port}`);
});

问题来了,我们如何才能运行后端呢?我们可以使用babel

首先安装工具

yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-typescript @babel/node nodemon --dev

其次,我们对 Babel 进行配置。在工作目录下新建 babel.config.json 文件,并向其中写入(删去注释):

{"presets": [// 指定要使用的工具集。更多配置详见官方文档:https://www.babeljs.cn/docs/"@babel/preset-env","@babel/preset-typescript"]
}

然后,我们对 nodemon 进行配置。打开工作目录下的 package.json 文件,向其中新建一个如下字段(删去注释):

"nodemonConfig": {      // 更多配置详见官方文档:https://www.npmjs.com/package/nodemon"watch": [          // 监听的文件目录"src"],"ext": "ts, json",  // 监听的文件后缀"exec": "babel-node --extensions \".ts\" src/app.ts"    // 当运行nodemon命令时执行// babel-node与node的区别在于应用了babel.config.json中配置的工具,src/app.ts是你的入口文件}

最后,我们设定 yarn 的执行脚本以简单运行 node_modules 中的程序。在 package.json 文件中新建一个如下字段(删去注释):

"scripts": {"start": "nodemon"  // 等价于./node_modules/nodemon/bin/nodemon.js}

这样,只要我们在终端中输入 yarn start,就会看到 nodemon 启动并用 Babel 生成和运行了 JavaScript 代码。此时输入 rs 回车,会使后端重启;摁下 Ctrl+C,会使后端停止。

Postman

安装

Postman 是一个 API 调试工具,可以模拟前端向后端发送请求,这在我们开发后端时非常有用。大家可以进入其官网注册账号(好处是你的 HTTP 请求会被备份并同步在不同设备)https://www.postman.com/,然后点击download desktop app 相关选项或者直接链接到https://www.postman.com/downloads/,下载安装程序并安装。

打开 Postman 并登录后,可以在 Collections 中点击加号,新建一个 Collection,然后在上方栏中点击加号,新建一个 Request 并保存到这个 Collection。这样就可以编辑、保存和发送这个 Request 了。针对同一类 API 的 Request 可以保存在同一个 Collection 下以方便管理。建议大家阅读 Postman 官方文档(https://learning.postman.com/docs/getting-started/introduction/)以学习更多Postman的实用知识。

使用

新建一个request,然后填写本地ip:port

http://localhost:3000/

然后就可以发送一个get请求,可以得到一个error,因为我们还没有路由处理这个请求,后面会介绍路由的具体用法

HTTP

HTTP 请求基本结构

要想弄清楚 Express 是如何路由的,有必要先了解 HTTP 请求的格式与结构。HTTP(超文本传输协议)基于 TCP/IP 协议来传递数据,是为客户端与服务器之间的通信而设计的。客户端请求的格式如下:

客户端请求格式

服务器响应的格式如下:

服务器响应格式

Express 根据的是 HTTP 请求方法和 URI/URL 来进行路由,由开发者编写的 API 处理请求后将状态码和其他响应内容发回客户端。下面先来了解一下 HTTP 请求方法:

序号方法描述
1GET向指定资源发出请求,用于获取资源,数据被包含在 URL 中
2HEAD等同于向服务器发送 GET 请求,但不获取响应包体,只获取响应头
3POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中
4PUT向指定资源处上传其最新内容
5DELETE请求服务器删除 Request-URL 所标识的资源
6CONNECTHTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器
7OPTIONS返回服务器针对特定资源的支持,允许客户端查看服务器的性能
8TRACE回显服务器收到的请求,主要用于测试或诊断
9PATCH是对 PUT 方法的补充,用来对已知资源进行局部更新

一般我们常用的是 GET、POST 和 PUT 请求。接着我们来了解一下 HTTP 响应状态码,下表列举了一些可能遇到的状态码及描述:

状态码英文描述中文描述
200OK请求成功
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized客户端请求的身份认证缺失或有误,认证失败
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

HTTP URL

URL = uniform resource locator,即统一资源定位系统,是在网络上标识地址的方法。具体在 HTTP 协议的应用上,形式如下:

http://[host]:[port]/[path]?[searchpart]

其中,<port>默认为 80(对于 HTTPS 协议默认为 443),<path>是一个 HTTP 选择器,<searchpart>是查询字符串。例如:

https://baike.baidu.com/item/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D%E7%B3%BB%E7%BB%9F/5937042?fromtitle=url&fromid=110640

协议是https,主机地址是baike.baidu.com(会被 DNS 解析成 IP 地址),采用默认的443端口;路径为/item/%E7%BB%9F.../5937042,其中带有路径参数/%E7%BB%9F.../5937042;查询字符串为fromtitle=url&fromid=110640

路由

简介

解析url的过程就是路由,我们就是通过匹配url的过程来实现对前端请求的响应的

基本用法

路由的基本格式是:

app.METHOD(PATH, HANDLER);

其中:

  • appexpress 类的实例。
  • METHOD 是 HTTP 请求方法。
  • PATH 是服务器上的路径。
  • HANDLER 是在路由匹配时执行的函数。

注意,这里这个参数的数量是不固定的,后面的用法里面还会有3参数,4参数的情况

例如:

// “/”路径接收到的GET方法匹配至该路由
app.get("/", (req, res) => {res.status(200).send("GET request");
});// “/”路径接收到的POST方法匹配至该路由
app.post("/", (req, res) => {res.status(200).send("POST request");
});// “/”路径接收到的所有方法匹配至该路由
app.all("/", (req, res) => {res.status(200).send("Any request");
});

对于 Handler 函数,Express 会在路由匹配时调用,调用时传进的参数有三个(一般依次命名为 req、res、next):

  • req 对象代表“请求”,常用的属性是 body(请求的包体)、query(请求的查询字符串)和 params(请求的路径参数);
  • res 对象代表“响应”,常用的方法是 status(设定响应状态码)和 send(设定响应内容);
  • next 函数在调用时表示移交给下一个中间件进行处理。

匹配路径

匹配路径可以是普通字符串、字符串模板和正则表达式。

普通字符串如:

app.get("/", (req, res) => {// 可匹配“/”res.send("root");
});
app.get("/about", (req, res) => {// 可匹配“/about”res.send("about");
});
app.get("/random.text", (req, res) => {// 可匹配“/random.text”res.send("random.text");
});

字符串模板如:

app.get("/ab?cd", (req, res) => {// 可匹配“acd”或“abcd”res.send("ab?cd");
});
app.get("/ab+cd", (req, res) => {// 可匹配“abcd”,“abbcd”,“abbbcd”等res.send("ab+cd");
});
app.get("/ab*cd", (req, res) => {// 可匹配“abcd”,“abxcd”,“abRANDOMcd”,“ab123cd”等res.send("ab*cd");
});
app.get("/ab(cd)?e", (req, res) => {// 可匹配“abe”或“abcde”res.send("ab(cd)?e");
});

正则表达式如:

app.get(/a/, (req, res) => {// 可匹配任何带有‘a’的路径res.send("/a/");
});
app.get(/.*fly$/, (req, res) => {// 可匹配“butterfly”,“dragonfly”res.send("/.*fly$/"); // 但不可匹配“butterflyman”,“dragonflyman”
}); // 相当于匹配以“fly”结尾的所有路径

查询字符串

路径中的参数(route parameter)是指将参数直接作为 URL 路径一部分的传参方式,也就是我们前面的路径

查询字符串(query string)是 URL 在路径后面用?分割的部分,二者虽然都是在 URL 中传递参数,但用法不同。

仍举上述 URL 的例子:https://baike.baidu.com/item/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D%E7%B3%BB%E7%BB%9F/5937042?fromtitle=url&fromid=110640。则可通过以下路由解析:

app.get("/item/:param1/:param2", (req, res) => {console.log(req.params.param1); // 统一资源定位系统,中间那段%unicode编码的翻译console.log(req.params.param2); // 5937042console.log(req.query.fromtitle); // urlconsole.log(req.query.fromid); // 110640res.status(200).send("ok");
});

此外还有一些高级用法,如在路径中插入短线(-) 和点 (.),甚至在路径中引入正则表达式:

// Request URL: http://localhost:3000/flights/LAX-SFO
app.get("/flights/:from-:to", (req, res) => {console.log(req.params.from); // LAXconsole.log(req.params.to); // SFOres.status(200).send("ok");
});// Request URL: http://localhost:3000/date/2020.2.1
app.get("/date/:year.:month.:day", (req, res) => {console.log(req.params.year); // 2020console.log(req.params.month); // 2console.log(req.params.day); // 1res.status(200).send("ok");
});

路由处理函数

Express 的路由处理函数十分灵活,可以传递多个函数、函数数组或二者的混合。当需要从上一个处理函数过渡到下一个处理函数时,需调用传入的 next 函数。

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("the response will be sent by the next function ...");next();},(req, res) => {res.send("Hello from D!");}
);

Router

express.Router 类可以用于构建模块化的路由处理函数(中间件)

我们在 src 文件夹下面新建一个 route1.ts 文件,然后写入:

import express from "express";
const router = express.Router(); // 实例化express.Router类router.get("/", (req, res) => {res.status(200).send("ok!");
});export default router; // 导出后,这个Router就成为了一个中间件

然后修改 app.ts 类:

import express from "express";
import route1 from "./route1"; // 导入route1中间件const app = express();
const port = 3000;app.use("/route1", route1); // 在route1路径下使用route1中间件app.listen(port, () => {console.log(`Example app listening on port ${port}`);
});

中间件

中间件是模块化的路由处理函数,我们刚才已经看到了路由级中间件的用法(通过 express.Router 创建中间件,然后通过 app.use 导入中间件),此外广义上刚才讲过的所有路由处理函数也都可以视为是最普通的中间件。在创建和使用中间件时,还有以下注意事项:

用 app.use 加载的中间件无论什么 HTTP 请求方法都会调用,如想限定一种 HTTP 请求方法,需使用 app.METHOD。同时,如果没有给出路径参数,则默认任何路径的请求都会调用中间件。

中间件是按顺序加载和执行的,如果前面已经出现了匹配的路由,那么后面的路由即使匹配,也不会调用相应的中间件。next 函数默认将控制权移交给当前路由的下一个处理函数,如想将控制权移交给下一个路由,需调用 next(‘route’)。如果将非"route"的参数传递给了 next 函数,则 Express 会认为该中间件出错,从而越过其他的正常处理函数直接执行异常处理函数。

app.get("/user/:id",(req, res, next) => {// if the user ID is 0, skip to the next routeif (req.params.id === "0") next("route");// otherwise pass the control to the next middleware function in this stackelse next();},(req, res, next) => {// send a regular responseres.send("regular");}
);// handler for the /user/:id path, which sends a special response
app.get("/user/:id", (req, res, next) => {res.send("special");
});

异常处理

对于同步编程的中间件,当抛出一个异常时且没有自建异常处理函数时,Express 会自己来处理这个异常。Express 会根据错误的err.status (或err.statusCode)来设定状态码、错误信息等。我们也可以自己创建一个异常处理函数,如果为 Express 中间件传入四个参数,通常命名为(err, req, res, next),则 Express 会把这一中间件当作异常处理函数。

app.get("/", (req, res) => {throw new Error("error occurs!"); // 异常由Express自建异常处理函数来处理
});
app.use((err, req, res, next) => {res.status(500).send("error occurs!"); // 或者可以自己编写异常处理函数
});

对于异步编程的中间件,要想进行异常处理,需要调用 next 函数并向其中传递一个值。如果中间件返回的是一个 Promise,则会自动调用 next(value)。下面的异步函数返回 Promise,如果中间抛出了异常或者 reject,则 Express 会默认调用 next 函数并传入异常信息。

app.get("/user/:id", async (req, res, next) => {var user = await getUserById(req.params.id);res.send(user);
});

更多时候,我们在中间件内部来处理异常,而不用将异常传递给 Express:

app.get("/", async (req, res) => {try{...return res.status(200).send("ok");} catch(err) {...return res.status(500).send("Internal Server Error");}
})

常用中间件

Express 提供了一些内建中间件供用户选择,比如常用的 express.json 会以 json 格式来解析请求体,express.urlencoded 会以 urlencoded 格式来解析请求体。还有一些第三方的中间件可以通过包管理器来安装,如 cookie-parser。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/312180.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下使用Wireshark抓包教程

在实际开发中&#xff0c;涉及网络传输的环节是非常多的。在这些过程中&#xff0c;我们经常有查看被传输的数据信息的需求&#xff0c;因此&#xff0c;抓包工具应运而生。Wireshark便是一款非常有名的抓包及分析软件&#xff0c;具有强大的协议解析能力。本文将介绍如何在Lin…

BED 文件格式 chip-seq m6a数据可视化会用到

General usage — bedtools 2.31.0 documentationhttps://bedtools.readthedocs.io/en/latest/content/general-usage.html BED格式&#xff08;Browser Extensible Data format&#xff09;是一种在生物信息学中广泛使用的文本文件格式&#xff0c;用于描述基因组上的特征和…

同步与互斥(三)

一、递归锁 /* 创建一个递归锁&#xff0c;返回它的句柄。 * 此函数内部会分配互斥量结构体 * 返回值: 返回句柄&#xff0c;非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );/* 释放 */ BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t…

OpenEular23.09(欧拉)操作系统为企业搭建独立的K8S集群环境,详细流程+截图

一.环境&#xff1b; win10&#xff0c;vmware16 pro&#xff0c;openeular23.09 集群模式&#xff1a;一主二从 主机硬件配置 主机名IP角色CPU内存硬盘k8s-master01192.168.91.100master4C4G40Gk8s-worker02192.168.91.101worker(node)4C4G40Gk8s-worker03192.168.91.102wor…

Resnet BatchNormalization 迁移学习

时间&#xff1a;2015 网络中的亮点&#xff1a; 超深的网络结构&#xff08;突破1000层&#xff09;提出residual模块使用Batch Normalization加速训练&#xff08;丢弃dropout&#xff09; 层数越深效果越好&#xff1f; 是什么样的原因导致更深的网络导致的训练效果更差呢…

计算机组成原理知识总结

目录 第一章、计算机系统概述知识框架&#xff1a;1.冯诺依曼机和存储程序的概念&#xff1f;2.计算机的工作过程&#xff1f;3.在计算机系统结构中&#xff0c;什么是编译&#xff1f;什么是解释&#xff1f;4.描述一下指令执行过程&#xff1f;1) 取指令&#xff1a; PC 一&g…

【Redis技术专区】「原理分析」探讨Redis 6.0为何需要启用多线程?

探讨Redis 6.0为何需要启用多线程 背景介绍开启多线程多线程的CPU核心配置IO多线程模式单线程处理方式多线程处理方式 为什么要开启多线程&#xff1f;充分利用多核CPU提高网络I/O效率响应现代应用需求 多线程实现启用多线程 最后总结 背景介绍 在Redis 6.0版本中&#xff0c;…

vue+ts element-plu是页码器根据屏幕宽度变化,解决刷新后初始化值问题

实现思路&#xff1a;组件挂载后执行初始化操作&#xff0c;初始化添加事件监听器&#xff0c;当浏览器窗口大小发生变化时会调用这个函数handleResize <el-pagination v-model:current-page"currentPage" background :total"total" layout"prev,…

【Java】ThreadLocal原理与使用场景

ThreadLocal原理&#xff1a; 字段&#xff1a; //ThreadLocal对象的哈希码 private final int threadLocalHashCode nextHashCode();//生成ThreadLocal对象的哈希码时&#xff0c;需要用到该对象&#xff0c;从0开始 private static AtomicInteger nextHashCode new Atomic…

数据结构和算法-B树的插入和删除

文章目录 B树的插入小结B树的删除小结 B树的插入 首先将根节点的关键字个数填满&#xff0c;填满后再分开成树 分开的规则 此时插入90&#xff0c;从根节点依次查找&#xff0c;然后插入到终端节点的关键字中 插入同上&#xff0c;注意此时在终端节点插入要符合终端节点的大…

STM32+Codesys工业软件PLC解决方案

工业控制系统在现代制造和自动化领域扮演着关键角色, 基于IEC 61131-3 标准的控制器编程开发软件平台CODESYS&#xff0c;适用于多种行业的控制系统的开发,使用户方便快捷地对自动化工程进行编程和配置&#xff0c;完成项目开发、软件测试和应用调试。 本次STM32联合合作伙伴C…

Linux基础知识学习2

tree命令的使用 可以看到dir2目录下的这些文件&#xff0c;要想显示dir2的具体结构&#xff0c;可用tree命令 mv命令 它可以实现两个功能 1.将文件移动到另一个目录中 2.对某一个文件进行重命名 1.将文件移动到另一个目录中 这里将dir1中的2.txt移动到他的子目录dir3中 执行…