
上一篇文章我们总结了网页开发的进阶 5 种 http/https 传输数据的方式:
url param query form urlencoded form data json 这 5 种方式覆盖了开发中绝大多数场景,掌握好这些就能轻松应对各种 http/https 数据通信的全栈需求。
如果你想成为一名全栈工程师,实现那么不能满足于会写这几种方式的种接前端代码,后端代码也得会写。进阶
所以,全栈这篇文章我们来实现下前后端代码,实现把整个链路打通,种接真正掌握它们。进阶
前端使用 axios 发送请求,全栈后端使用 Nest.js 作为服务端框架。实现
准备工作
首先我们要把 Nest.js 服务端跑起来,种接并且支持 api 接口、进阶静态页面。全栈
Nest.js 创建一个 crud 服务是实现非常快的,只需要这么几步:
安装 @nest/cli,使用 nest new xxx 创建一个 Nest.js 的项目, 在根目录执行 nest g resource person 快速生成 person 模块的 crud 代码 npm run start 启动 Nest.js 服务 这样一个有 person 的 crud 接口的服务就跑起来了,是不是非常快。
服务跑起来以后是这样的

打印出了有哪些接口可以用,云南idc服务商可以在 postman 或者浏览器来测试下:

api 接口跑通了,再支持下静态资源的访问:
main.ts 是负责启动 Nest.js 的 ioc 容器的,在脚手架生成的代码的基础上,调用下 useStaticAssets 就可以支持静态资源的请求。
async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); app.useStaticAssets(join(__dirname, .., public), { prefix: /static}); await app.listen(3000); } bootstrap(); 我们指定 prefix 为 static,然后再静态文件目录 public 下添加一个 html:
<html> <body>hello</body> </html> 重启服务,然后浏览器访问下试试:

api 接口和静态资源的访问都支持了,接下来就分别实现下 5 种前后端 http 数据传输的方式吧。
url param
url param 是 url 中的参数,Nest.js 里通过 :参数名 的方式来声明,然后通过 @Param(参数名) 的装饰器取出来注入到 controller:
@Controller(api/person) export class PersonController { @Get(:id) urlParm(@Param(id) id: string) { return `received: id=${ id}`; } } 前端代码就是一个 get 方法,参数放在 url 里:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script> </head> <body> <script> async function urlParam() { const res = await axios.get(/api/person/1); console.log(res); } urlParam(); </script> </body> 启动服务,在浏览器访问下:

控制台打印了服务端返回的消息,证明服务端拿到了通过 url param 传递的数据。
通过 url 传递数据的方式除了 url param 还有 query:
query
query 是 url 中 ? 后的香港云服务器字符串,需要做 url encode。
在 Nest.js 里,通过 @Query 装饰器来取:
@Controller(api/person) export class PersonController { @Get(find) query(@Query(name) name: string, @Query(age) age: number) { return `received: name=${ name},age=${ age}`; } } 前端代码同样是通过 axios 发送一个 get 请求:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script> </head> <body> <script> async function query() { const res = await axios.get(/api/person/find, { params: { name: 光, age: 20 } }); console.log(res); } query(); </script> </body> </html> 参数通过 params 指定,axios 会做 url encode,不需要自己做。
然后测试下:

服务端成功接受了我们通过 query 传递的数据。
上面两种(url param、query)是通过 url 传递数据的方式,下面 3 种是通过 body 传递数据。
html urlencoded
html urlencoded 是通过 body 传输数据,其实是把 query 字符串放在了 body 里,所以需要做 url encode:

用 Nest.js 接收的话,使用 @Body 装饰器,Nest.js 会解析请求体,然后注入到 dto 中。
dto 是 data transfer object,就是用于封装传输的数据的对象:
export class CreatePersonDto { name: string; age: number; } import { CreatePersonDto } from ./dto/create-person.dto; @Controller(api/person) export class PersonController { @Post() body(@Body() createPersonDto: CreatePersonDto) { return `received: ${ JSON.stringify(createPersonDto)}` } } 前端代码使用 post 方式请求,指定 content type 为 application/x-www-form-urlencoded,用 qs 做下 url encode:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script> <script src="https://unpkg.com/qs@6.10.2/dist/qs.js"></script> </head> <body> <script> async function formUrlEncoded() { const res = await axios.post(/api/person, Qs.stringify({ name: 光, age: 20 }), { headers: { content-type: application/x-www-form-urlencoded } }); console.log(res); } formUrlEncoded(); </script> </body> </html> 测试下:

服务端成功的接收到了数据。亿华云
其实比起 form urlencoded,使用 json 来传输更常用一些:
json
json 需要指定 content-type 为 application/json,内容会以 JSON 的方式传输:

后端代码同样使用 @Body 来接收,不需要做啥变动。form urlencoded 和 json 都是从 body 取值,Nest.js 内部会根据 content type 做区分,使用不同的解析方式。
@Controller(api/person) export class PersonController { @Post() body(@Body() createPersonDto: CreatePersonDto) { return `received: ${ JSON.stringify(createPersonDto)}` } } 前端代码使用 axios 发送 post 请求,默认传输 json 就会指定 content type 为 application/json,不需要手动指定:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script> </head> <body> <script> async function json() { const res = await axios.post(/api/person, { name: 光, age: 20 }); console.log(res); } json(); </script> </body> </html> 测试下:

服务端成功接收到了通过 json 传递的数据。
json 和 form urlencoded 都不适合传递文件,想传输文件要用 form data:
form data
form data 是用 -------- 作为 boundary 分隔传输的内容的:

Nest.js 解析 form data 使用 FilesInterceptor 的拦截器,用 @UseInterceptors 装饰器启用,然后通过 @UploadedFiles 来取。非文件的内容,同样是通过 @Body 来取。
import { AnyFilesInterceptor } from @nestjs/platform-express; import { CreatePersonDto } from ./dto/create-person.dto; @Controller(api/person) export class PersonController { @Post(file) @UseInterceptors(AnyFilesInterceptor()) body2(@Body() createPersonDto: CreatePersonDto, @UploadedFiles() files: Array<Express.Multer.File>) { console.log(files); return `received: ${ JSON.stringify(createPersonDto)}` } } 前端代码使用 axios 发送 post 请求,指定 content type 为 multipart/form-data:
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script> </head> <body> <input id="fileInput" type="file" multiple/> <script> const fileInput = document.querySelector(#fileInput); async function formData() { const data = new FormData(); data.set(name,光); data.set(age, 20); data.set(file1, fileInput.files[0]); data.set(file2, fileInput.files[1]); const res = await axios.post(/api/person/file, data, { headers: { content-type: multipart/form-data } }); console.log(res); } fileInput.onchange = formData; </script> </body> </html> file input 指定 multiple 可以选择多个文件。
测试下:

服务端接收到了 name 和 age:

去服务器控制台看下:

可以看到,服务器成功的接收到了我们上传的文件。
全部代码上传到了 github:https://github.com/QuarkGluonPlasma/nestjs-exercize


总结
我们用 axios 发送请求,使用 Nest.js 起后端服务,实现了 5 种 http/https 的数据传输方式:
其中前两种是 url 中的:
url param:url 中的参数,Nest.js 中使用 @Param 来取
query:url 中 ? 后的字符串,Nest.js 中使用 @Query 来取
后三种是 body 中的:
form urlencoded:类似 query 字符串,只不过是放在 body 中。Nest.js 中使用 @Body 来取,axios 中需要指定 content type 为 application/x-www-form-urlencoded,并且对数据用 qs 做 url encode
json:json 格式的数据。Nest.js 中使用 @Body 来取,axios 中不需要单独指定 content type,axios 内部会处理。
form data:通过 ----- 作为 boundary 分隔的数据。主要用于传输文件,Nest.js 中要使用 FilesInterceptor 来处理,用 @UseInterceptors 来启用。其余部分用 @Body 来取。axios 中需要指定 content type 为 multipart/form-data,并且用 FormData 对象来封装传输的内容。
这 5 种 http/https 的传输数据的方式覆盖了绝大多数开发场景,如果你想进阶全栈,能够提供这 5 种接口是首先要做到的。