[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller":1890,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller":1916},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":1886,"stem":1887,"id":1888,"extension":1889},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller","控制器与路由","控制器处理 HTTP 路由与入参。见 基础、管道。","技能小册",[12,13],"nestjs","controller","2020-07-18",false,{"type":17,"value":18,"toc":1878},"minimark",[19,22,37,40,45,55,60,262,284,288,291,405,408,450,454,471,522,534,569,588,592,595,736,742,767,771,911,915,919,926,997,1001,1016,1060,1064,1071,1136,1140,1146,1340,1347,1353,1499,1515,1615,1619,1669,1672,1813,1816,1820,1823,1874],[20,21,8],"h1",{"id":8},[23,24,25,26,31,32,36],"p",{},"控制器处理 HTTP 路由与入参。见 ",[27,28,30],"a",{"href":29},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fbase","基础","、",[27,33,35],{"href":34},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fpipes","管道","。",[23,38,39],{},"控制器负责处理传入的请求并向客户端返回响应。通过装饰器将类和方法与特定路由绑定，NestJS 提供了丰富且直观的 API 来定义路由、获取请求参数和构造响应。",[41,42,44],"h3",{"id":43},"一路由定义","一、路由定义",[46,47,49,50,54],"h4",{"id":48},"_1-controller-与路由前缀","1. ",[51,52,53],"code",{},"@Controller()"," 与路由前缀",[23,56,57,59],{},[51,58,53],{}," 装饰器用于定义控制器类，可接受一个路径前缀，从而将一组相关路由聚合在一个路径下。",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { Controller, Get } from \"@nestjs\u002Fcommon\";\n\n@Controller(\"users\")\nexport class UsersController {\n  @Get()\n  findAll() {\n    return \"This returns all users\";\n  }\n\n  @Get(\"profile\")\n  getProfile() {\n    return \"User profile\";\n  }\n}\n","ts","",[51,68,69,112,119,142,159,171,183,198,204,209,227,237,251,256],{"__ignoreMap":66},[70,71,74,78,82,86,89,92,95,98,102,106,109],"span",{"class":72,"line":73},"line",1,[70,75,77],{"class":76},"sVHd0","import",[70,79,81],{"class":80},"sP7_E"," {",[70,83,85],{"class":84},"su5hD"," Controller",[70,87,88],{"class":80},",",[70,90,91],{"class":84}," Get",[70,93,94],{"class":80}," }",[70,96,97],{"class":76}," from",[70,99,101],{"class":100},"sjJ54"," \"",[70,103,105],{"class":104},"s_sjI","@nestjs\u002Fcommon",[70,107,108],{"class":100},"\"",[70,110,111],{"class":80},";\n",[70,113,115],{"class":72,"line":114},2,[70,116,118],{"emptyLinePlaceholder":117},true,"\n",[70,120,122,125,129,132,134,137,139],{"class":72,"line":121},3,[70,123,124],{"class":80},"@",[70,126,128],{"class":127},"sGLFI","Controller",[70,130,131],{"class":84},"(",[70,133,108],{"class":100},[70,135,136],{"class":104},"users",[70,138,108],{"class":100},[70,140,141],{"class":84},")\n",[70,143,145,148,152,156],{"class":72,"line":144},4,[70,146,147],{"class":76},"export",[70,149,151],{"class":150},"sbsja"," class",[70,153,155],{"class":154},"sbgvK"," UsersController",[70,157,158],{"class":80}," {\n",[70,160,162,165,168],{"class":72,"line":161},5,[70,163,164],{"class":80},"  @",[70,166,167],{"class":127},"Get",[70,169,170],{"class":84},"()\n",[70,172,174,178,181],{"class":72,"line":173},6,[70,175,177],{"class":176},"sVXei","  findAll",[70,179,180],{"class":80},"()",[70,182,158],{"class":80},[70,184,186,189,191,194,196],{"class":72,"line":185},7,[70,187,188],{"class":76},"    return",[70,190,101],{"class":100},[70,192,193],{"class":104},"This returns all users",[70,195,108],{"class":100},[70,197,111],{"class":80},[70,199,201],{"class":72,"line":200},8,[70,202,203],{"class":80},"  }\n",[70,205,207],{"class":72,"line":206},9,[70,208,118],{"emptyLinePlaceholder":117},[70,210,212,214,216,218,220,223,225],{"class":72,"line":211},10,[70,213,164],{"class":80},[70,215,167],{"class":127},[70,217,131],{"class":84},[70,219,108],{"class":100},[70,221,222],{"class":104},"profile",[70,224,108],{"class":100},[70,226,141],{"class":84},[70,228,230,233,235],{"class":72,"line":229},11,[70,231,232],{"class":176},"  getProfile",[70,234,180],{"class":80},[70,236,158],{"class":80},[70,238,240,242,244,247,249],{"class":72,"line":239},12,[70,241,188],{"class":76},[70,243,101],{"class":100},[70,245,246],{"class":104},"User profile",[70,248,108],{"class":100},[70,250,111],{"class":80},[70,252,254],{"class":72,"line":253},13,[70,255,203],{"class":80},[70,257,259],{"class":72,"line":258},14,[70,260,261],{"class":80},"}\n",[263,264,265,276],"ul",{},[266,267,268,269,272,273,36],"li",{},"请求 ",[51,270,271],{},"GET \u002Fusers"," 会调用 ",[51,274,275],{},"findAll()",[266,277,268,278,272,281,36],{},[51,279,280],{},"GET \u002Fusers\u002Fprofile",[51,282,283],{},"getProfile()",[46,285,287],{"id":286},"_2-http-方法装饰器","2. HTTP 方法装饰器",[23,289,290],{},"NestJS 为每个标准 HTTP 方法提供了对应的装饰器：",[292,293,294,310],"table",{},[295,296,297],"thead",{},[298,299,300,304,307],"tr",{},[301,302,303],"th",{},"装饰器",[301,305,306],{},"HTTP 方法",[301,308,309],{},"描述",[311,312,313,327,340,353,366,379,392],"tbody",{},[298,314,315,321,324],{},[316,317,318],"td",{},[51,319,320],{},"@Get()",[316,322,323],{},"GET",[316,325,326],{},"获取资源",[298,328,329,334,337],{},[316,330,331],{},[51,332,333],{},"@Post()",[316,335,336],{},"POST",[316,338,339],{},"创建资源",[298,341,342,347,350],{},[316,343,344],{},[51,345,346],{},"@Put()",[316,348,349],{},"PUT",[316,351,352],{},"全量更新资源",[298,354,355,360,363],{},[316,356,357],{},[51,358,359],{},"@Patch()",[316,361,362],{},"PATCH",[316,364,365],{},"部分更新资源",[298,367,368,373,376],{},[316,369,370],{},[51,371,372],{},"@Delete()",[316,374,375],{},"DELETE",[316,377,378],{},"删除资源",[298,380,381,386,389],{},[316,382,383],{},[51,384,385],{},"@Options()",[316,387,388],{},"OPTIONS",[316,390,391],{},"预检请求",[298,393,394,399,402],{},[316,395,396],{},[51,397,398],{},"@Head()",[316,400,401],{},"HEAD",[316,403,404],{},"仅请求响应头",[23,406,407],{},"每个方法装饰器可以接受一个字符串参数作为路由路径（相对控制器前缀）。",[61,409,411],{"className":63,"code":410,"language":65,"meta":66,"style":66},"@Post('create')\ncreateUser() { ... }\n",[51,412,413,432],{"__ignoreMap":66},[70,414,415,417,420,422,425,428,430],{"class":72,"line":73},[70,416,124],{"class":80},[70,418,419],{"class":127},"Post",[70,421,131],{"class":84},[70,423,424],{"class":100},"'",[70,426,427],{"class":104},"create",[70,429,424],{"class":100},[70,431,141],{"class":84},[70,433,434,437,440,443,447],{"class":72,"line":114},[70,435,436],{"class":127},"createUser",[70,438,439],{"class":84},"() ",[70,441,442],{"class":80},"{",[70,444,446],{"class":445},"smGrS"," ...",[70,448,449],{"class":80}," }\n",[46,451,453],{"id":452},"_3-路由参数通配符与-restful-风格","3. 路由参数通配符与 Restful 风格",[263,455,456],{},[266,457,458,462,463,466,467,470],{},[459,460,461],"strong",{},"动态参数","（路由参数）：使用冒号 ",[51,464,465],{},":"," 声明，后接参数名，通过 ",[51,468,469],{},"@Param()"," 获取。",[61,472,474],{"className":63,"code":473,"language":65,"meta":66,"style":66},"@Get(':id')\nfindOne(@Param('id') id: string) { ... }\n",[51,475,476,493],{"__ignoreMap":66},[70,477,478,480,482,484,486,489,491],{"class":72,"line":73},[70,479,124],{"class":80},[70,481,167],{"class":127},[70,483,131],{"class":84},[70,485,424],{"class":100},[70,487,488],{"class":104},":id",[70,490,424],{"class":100},[70,492,141],{"class":84},[70,494,495,498,501,504,506,508,511,513,516,518,520],{"class":72,"line":114},[70,496,497],{"class":127},"findOne",[70,499,500],{"class":84},"(@",[70,502,503],{"class":127},"Param",[70,505,131],{"class":84},[70,507,424],{"class":100},[70,509,510],{"class":104},"id",[70,512,424],{"class":100},[70,514,515],{"class":84},") id: string) ",[70,517,442],{"class":80},[70,519,446],{"class":445},[70,521,449],{"class":80},[263,523,524],{},[266,525,526,529,530,533],{},[459,527,528],{},"通配符","：可以使用 ",[51,531,532],{},"*"," 匹配任意路径（仅支持末尾通配符）。",[61,535,537],{"className":63,"code":536,"language":65,"meta":66,"style":66},"@Get('files\u002F*')\nhandleWildcard() { ... }\n",[51,538,539,556],{"__ignoreMap":66},[70,540,541,543,545,547,549,552,554],{"class":72,"line":73},[70,542,124],{"class":80},[70,544,167],{"class":127},[70,546,131],{"class":84},[70,548,424],{"class":100},[70,550,551],{"class":104},"files\u002F*",[70,553,424],{"class":100},[70,555,141],{"class":84},[70,557,558,561,563,565,567],{"class":72,"line":114},[70,559,560],{"class":127},"handleWildcard",[70,562,439],{"class":84},[70,564,442],{"class":80},[70,566,446],{"class":445},[70,568,449],{"class":80},[263,570,571],{},[266,572,573,576,577,31,580,31,582,31,585,36],{},[459,574,575],{},"Restful 风格","：NestJS 天然支持设计符合 REST 规范的路由，通常配合 ",[51,578,579],{},"@Get(':id')",[51,581,333],{},[51,583,584],{},"@Put(':id')",[51,586,587],{},"@Delete(':id')",[41,589,591],{"id":590},"二请求对象处理","二、请求对象处理",[23,593,594],{},"NestJS 提供了多种装饰器来获取请求中的信息，大部分基于 Express 或 Fastify 的原生请求对象，但通过装饰器简化了访问方式。",[292,596,597,608],{},[295,598,599],{},[298,600,601,603,605],{},[301,602,303],{},[301,604,309],{},[301,606,607],{},"示例",[311,609,610,629,647,662,677,691,706,721],{},[298,611,612,621,624],{},[316,613,614,617,618],{},[51,615,616],{},"@Req()"," \u002F ",[51,619,620],{},"@Request()",[316,622,623],{},"获取原生请求对象",[316,625,626],{},[51,627,628],{},"@Req() request: Request",[298,630,631,639,642],{},[316,632,633,617,636],{},[51,634,635],{},"@Res()",[51,637,638],{},"@Response()",[316,640,641],{},"获取原生响应对象",[316,643,644],{},[51,645,646],{},"@Res() response: Response",[298,648,649,654,657],{},[316,650,651],{},[51,652,653],{},"@Body()",[316,655,656],{},"获取请求体，可选参数名",[316,658,659],{},[51,660,661],{},"@Body() body: CreateUserDto",[298,663,664,669,672],{},[316,665,666],{},[51,667,668],{},"@Query()",[316,670,671],{},"获取查询参数（Query String）",[316,673,674],{},[51,675,676],{},"@Query('page') page: string",[298,678,679,683,686],{},[316,680,681],{},[51,682,469],{},[316,684,685],{},"获取路由参数",[316,687,688],{},[51,689,690],{},"@Param('id') id: string",[298,692,693,698,701],{},[316,694,695],{},[51,696,697],{},"@Headers()",[316,699,700],{},"获取请求头",[316,702,703],{},[51,704,705],{},"@Headers('authorization') auth: string",[298,707,708,713,716],{},[316,709,710],{},[51,711,712],{},"@Ip()",[316,714,715],{},"获取客户端 IP 地址",[316,717,718],{},[51,719,720],{},"@Ip() clientIp: string",[298,722,723,728,731],{},[316,724,725],{},[51,726,727],{},"@Session()",[316,729,730],{},"获取会话对象（需配置 session 中间件）",[316,732,733],{},[51,734,735],{},"@Session() session: Record\u003Cstring, any>",[23,737,738,741],{},[459,739,740],{},"注意","：",[263,743,744,754],{},[266,745,746,747,749,750,753],{},"若使用 ",[51,748,635],{}," 并手动调用 ",[51,751,752],{},"res.send()","，则框架不会再自动处理返回值。此时需要自己发送完整响应。",[266,755,756,757,760,761,763,764,766],{},"为了同时获取原生请求和部分装饰器（如 ",[51,758,759],{},"@Body","），可以注入 ",[51,762,616],{}," 而不使用 ",[51,765,635],{},"，让框架继续自动序列化返回值。",[46,768,770],{"id":769},"示例综合使用","示例：综合使用",[61,772,774],{"className":63,"code":773,"language":65,"meta":66,"style":66},"@Post()\ncreate(\n  @Body() createUserDto: CreateUserDto,\n  @Query('token') token: string,\n  @Headers('user-agent') userAgent: string,\n) {\n  console.log(token, userAgent);\n  return this.usersService.create(createUserDto);\n}\n",[51,775,776,784,791,804,825,846,854,880,907],{"__ignoreMap":66},[70,777,778,780,782],{"class":72,"line":73},[70,779,124],{"class":80},[70,781,419],{"class":127},[70,783,170],{"class":84},[70,785,786,788],{"class":72,"line":114},[70,787,427],{"class":127},[70,789,790],{"class":84},"(\n",[70,792,793,795,798,801],{"class":72,"line":121},[70,794,164],{"class":84},[70,796,797],{"class":127},"Body",[70,799,800],{"class":84},"() createUserDto: CreateUserDto",[70,802,803],{"class":80},",\n",[70,805,806,808,811,813,815,818,820,823],{"class":72,"line":144},[70,807,164],{"class":84},[70,809,810],{"class":127},"Query",[70,812,131],{"class":84},[70,814,424],{"class":100},[70,816,817],{"class":104},"token",[70,819,424],{"class":100},[70,821,822],{"class":84},") token: string",[70,824,803],{"class":80},[70,826,827,829,832,834,836,839,841,844],{"class":72,"line":161},[70,828,164],{"class":84},[70,830,831],{"class":127},"Headers",[70,833,131],{"class":84},[70,835,424],{"class":100},[70,837,838],{"class":104},"user-agent",[70,840,424],{"class":100},[70,842,843],{"class":84},") userAgent: string",[70,845,803],{"class":80},[70,847,848,851],{"class":72,"line":173},[70,849,850],{"class":84},") ",[70,852,853],{"class":80},"{\n",[70,855,856,859,862,865,868,870,872,875,878],{"class":72,"line":185},[70,857,858],{"class":84},"  console",[70,860,861],{"class":80},".",[70,863,864],{"class":127},"log",[70,866,131],{"class":867},"skxfh",[70,869,817],{"class":84},[70,871,88],{"class":80},[70,873,874],{"class":84}," userAgent",[70,876,877],{"class":867},")",[70,879,111],{"class":80},[70,881,882,885,889,891,894,896,898,900,903,905],{"class":72,"line":200},[70,883,884],{"class":76},"  return",[70,886,888],{"class":887},"s39Yj"," this",[70,890,861],{"class":80},[70,892,893],{"class":84},"usersService",[70,895,861],{"class":80},[70,897,427],{"class":127},[70,899,131],{"class":867},[70,901,902],{"class":84},"createUserDto",[70,904,877],{"class":867},[70,906,111],{"class":80},[70,908,909],{"class":72,"line":206},[70,910,261],{"class":80},[41,912,914],{"id":913},"三响应处理","三、响应处理",[46,916,918],{"id":917},"_1-自动序列化","1. 自动序列化",[23,920,921,922,925],{},"控制器方法返回的值如果是对象或数组，NestJS 会自动将之 JSON 序列化并设置 ",[51,923,924],{},"Content-Type: application\u002Fjson","。返回字符串或数字则会原样输出（作为纯文本）。",[61,927,929],{"className":63,"code":928,"language":65,"meta":66,"style":66},"@Get()\nfindAll() {\n  return [{ id: 1, name: 'John' }]; \u002F\u002F 自动转为 JSON\n}\n",[51,930,931,939,948,993],{"__ignoreMap":66},[70,932,933,935,937],{"class":72,"line":73},[70,934,124],{"class":80},[70,936,167],{"class":127},[70,938,170],{"class":84},[70,940,941,944,946],{"class":72,"line":114},[70,942,943],{"class":127},"findAll",[70,945,439],{"class":84},[70,947,853],{"class":80},[70,949,950,952,955,957,960,962,966,968,971,973,976,979,981,983,986,989],{"class":72,"line":121},[70,951,884],{"class":76},[70,953,954],{"class":867}," [",[70,956,442],{"class":80},[70,958,959],{"class":867}," id",[70,961,465],{"class":80},[70,963,965],{"class":964},"srdBf"," 1",[70,967,88],{"class":80},[70,969,970],{"class":867}," name",[70,972,465],{"class":80},[70,974,975],{"class":100}," '",[70,977,978],{"class":104},"John",[70,980,424],{"class":100},[70,982,94],{"class":80},[70,984,985],{"class":867},"]",[70,987,988],{"class":80},";",[70,990,992],{"class":991},"sutJx"," \u002F\u002F 自动转为 JSON\n",[70,994,995],{"class":72,"line":144},[70,996,261],{"class":80},[46,998,1000],{"id":999},"_2-状态码修饰","2. 状态码修饰",[23,1002,1003,1004,1007,1008,1011,1012,1015],{},"默认状态下，POST 请求返回 ",[51,1005,1006],{},"201 Created","，其他请求返回 ",[51,1009,1010],{},"200 OK","。可以通过 ",[51,1013,1014],{},"@HttpCode()"," 修改状态码。",[61,1017,1019],{"className":63,"code":1018,"language":65,"meta":66,"style":66},"@Post()\n@HttpCode(204)\ncreate() {\n  \u002F\u002F 返回 204 No Content\n}\n",[51,1020,1021,1029,1043,1051,1056],{"__ignoreMap":66},[70,1022,1023,1025,1027],{"class":72,"line":73},[70,1024,124],{"class":80},[70,1026,419],{"class":127},[70,1028,170],{"class":84},[70,1030,1031,1033,1036,1038,1041],{"class":72,"line":114},[70,1032,124],{"class":80},[70,1034,1035],{"class":127},"HttpCode",[70,1037,131],{"class":84},[70,1039,1040],{"class":964},"204",[70,1042,141],{"class":84},[70,1044,1045,1047,1049],{"class":72,"line":121},[70,1046,427],{"class":127},[70,1048,439],{"class":84},[70,1050,853],{"class":80},[70,1052,1053],{"class":72,"line":144},[70,1054,1055],{"class":991},"  \u002F\u002F 返回 204 No Content\n",[70,1057,1058],{"class":72,"line":161},[70,1059,261],{"class":80},[46,1061,1063],{"id":1062},"_3-自定义响应头","3. 自定义响应头",[23,1065,1066,1067,1070],{},"使用 ",[51,1068,1069],{},"@Header()"," 添加自定义响应头（可同时使用多个）。",[61,1072,1074],{"className":63,"code":1073,"language":65,"meta":66,"style":66},"@Get()\n@Header('Cache-Control', 'no-cache')\nfindAll() {\n  return 'data';\n}\n",[51,1075,1076,1084,1111,1119,1132],{"__ignoreMap":66},[70,1077,1078,1080,1082],{"class":72,"line":73},[70,1079,124],{"class":80},[70,1081,167],{"class":127},[70,1083,170],{"class":84},[70,1085,1086,1088,1091,1093,1095,1098,1100,1102,1104,1107,1109],{"class":72,"line":114},[70,1087,124],{"class":80},[70,1089,1090],{"class":127},"Header",[70,1092,131],{"class":84},[70,1094,424],{"class":100},[70,1096,1097],{"class":104},"Cache-Control",[70,1099,424],{"class":100},[70,1101,88],{"class":80},[70,1103,975],{"class":100},[70,1105,1106],{"class":104},"no-cache",[70,1108,424],{"class":100},[70,1110,141],{"class":84},[70,1112,1113,1115,1117],{"class":72,"line":121},[70,1114,943],{"class":127},[70,1116,439],{"class":84},[70,1118,853],{"class":80},[70,1120,1121,1123,1125,1128,1130],{"class":72,"line":144},[70,1122,884],{"class":76},[70,1124,975],{"class":100},[70,1126,1127],{"class":104},"data",[70,1129,424],{"class":100},[70,1131,111],{"class":80},[70,1133,1134],{"class":72,"line":161},[70,1135,261],{"class":80},[46,1137,1139],{"id":1138},"_4-重定向","4. 重定向",[23,1141,1142,1145],{},[51,1143,1144],{},"@Redirect()"," 装饰器可以指定重定向的 URL 和状态码（默认 302）。也可以通过函数返回值动态重定向。",[61,1147,1149],{"className":63,"code":1148,"language":65,"meta":66,"style":66},"@Get('docs')\n@Redirect('https:\u002F\u002Fdocs.example.com', 301)\ngetDocs() {}\n\n\u002F\u002F 动态重定向\n@Get('legacy')\nfindOld(@Query('version') version) {\n  if (version === '2') {\n    return { url: 'https:\u002F\u002Fnew-site.com\u002Fv2', statusCode: 301 };\n  }\n  return { url: 'https:\u002F\u002Fnew-site.com', statusCode: 302 };\n}\n",[51,1150,1151,1168,1191,1201,1205,1210,1227,1250,1274,1304,1308,1336],{"__ignoreMap":66},[70,1152,1153,1155,1157,1159,1161,1164,1166],{"class":72,"line":73},[70,1154,124],{"class":80},[70,1156,167],{"class":127},[70,1158,131],{"class":84},[70,1160,424],{"class":100},[70,1162,1163],{"class":104},"docs",[70,1165,424],{"class":100},[70,1167,141],{"class":84},[70,1169,1170,1172,1175,1177,1179,1182,1184,1186,1189],{"class":72,"line":114},[70,1171,124],{"class":80},[70,1173,1174],{"class":127},"Redirect",[70,1176,131],{"class":84},[70,1178,424],{"class":100},[70,1180,1181],{"class":104},"https:\u002F\u002Fdocs.example.com",[70,1183,424],{"class":100},[70,1185,88],{"class":80},[70,1187,1188],{"class":964}," 301",[70,1190,141],{"class":84},[70,1192,1193,1196,1198],{"class":72,"line":121},[70,1194,1195],{"class":127},"getDocs",[70,1197,439],{"class":84},[70,1199,1200],{"class":80},"{}\n",[70,1202,1203],{"class":72,"line":144},[70,1204,118],{"emptyLinePlaceholder":117},[70,1206,1207],{"class":72,"line":161},[70,1208,1209],{"class":991},"\u002F\u002F 动态重定向\n",[70,1211,1212,1214,1216,1218,1220,1223,1225],{"class":72,"line":173},[70,1213,124],{"class":80},[70,1215,167],{"class":127},[70,1217,131],{"class":84},[70,1219,424],{"class":100},[70,1221,1222],{"class":104},"legacy",[70,1224,424],{"class":100},[70,1226,141],{"class":84},[70,1228,1229,1232,1234,1236,1238,1240,1243,1245,1248],{"class":72,"line":185},[70,1230,1231],{"class":127},"findOld",[70,1233,500],{"class":84},[70,1235,810],{"class":127},[70,1237,131],{"class":84},[70,1239,424],{"class":100},[70,1241,1242],{"class":104},"version",[70,1244,424],{"class":100},[70,1246,1247],{"class":84},") version) ",[70,1249,853],{"class":80},[70,1251,1252,1255,1258,1260,1263,1265,1268,1270,1272],{"class":72,"line":200},[70,1253,1254],{"class":76},"  if",[70,1256,1257],{"class":867}," (",[70,1259,1242],{"class":84},[70,1261,1262],{"class":445}," ===",[70,1264,975],{"class":100},[70,1266,1267],{"class":104},"2",[70,1269,424],{"class":100},[70,1271,850],{"class":867},[70,1273,853],{"class":80},[70,1275,1276,1278,1280,1283,1285,1287,1290,1292,1294,1297,1299,1301],{"class":72,"line":206},[70,1277,188],{"class":76},[70,1279,81],{"class":80},[70,1281,1282],{"class":867}," url",[70,1284,465],{"class":80},[70,1286,975],{"class":100},[70,1288,1289],{"class":104},"https:\u002F\u002Fnew-site.com\u002Fv2",[70,1291,424],{"class":100},[70,1293,88],{"class":80},[70,1295,1296],{"class":867}," statusCode",[70,1298,465],{"class":80},[70,1300,1188],{"class":964},[70,1302,1303],{"class":80}," };\n",[70,1305,1306],{"class":72,"line":211},[70,1307,203],{"class":80},[70,1309,1310,1312,1314,1316,1318,1320,1323,1325,1327,1329,1331,1334],{"class":72,"line":229},[70,1311,884],{"class":76},[70,1313,81],{"class":80},[70,1315,1282],{"class":867},[70,1317,465],{"class":80},[70,1319,975],{"class":100},[70,1321,1322],{"class":104},"https:\u002F\u002Fnew-site.com",[70,1324,424],{"class":100},[70,1326,88],{"class":80},[70,1328,1296],{"class":867},[70,1330,465],{"class":80},[70,1332,1333],{"class":964}," 302",[70,1335,1303],{"class":80},[70,1337,1338],{"class":72,"line":239},[70,1339,261],{"class":80},[46,1341,1343,1344,1346],{"id":1342},"_5-完全控制响应流使用-res","5. 完全控制响应流（使用 ",[51,1345,635],{},"）",[23,1348,1349,1350,1352],{},"当需要完全控制响应（例如文件下载、流式响应、设置复杂 cookie 等）时，可以注入 ",[51,1351,635],{}," 对象。",[61,1354,1356],{"className":63,"code":1355,"language":65,"meta":66,"style":66},"import { Response } from 'express';\n\n@Get('file')\ndownloadFile(@Res() res: Response) {\n  const file = createReadStream('file.pdf');\n  res.setHeader('Content-Type', 'application\u002Fpdf');\n  file.pipe(res);\n}\n",[51,1357,1358,1380,1384,1401,1416,1444,1476,1495],{"__ignoreMap":66},[70,1359,1360,1362,1364,1367,1369,1371,1373,1376,1378],{"class":72,"line":73},[70,1361,77],{"class":76},[70,1363,81],{"class":80},[70,1365,1366],{"class":84}," Response",[70,1368,94],{"class":80},[70,1370,97],{"class":76},[70,1372,975],{"class":100},[70,1374,1375],{"class":104},"express",[70,1377,424],{"class":100},[70,1379,111],{"class":80},[70,1381,1382],{"class":72,"line":114},[70,1383,118],{"emptyLinePlaceholder":117},[70,1385,1386,1388,1390,1392,1394,1397,1399],{"class":72,"line":121},[70,1387,124],{"class":80},[70,1389,167],{"class":127},[70,1391,131],{"class":84},[70,1393,424],{"class":100},[70,1395,1396],{"class":104},"file",[70,1398,424],{"class":100},[70,1400,141],{"class":84},[70,1402,1403,1406,1408,1411,1414],{"class":72,"line":144},[70,1404,1405],{"class":127},"downloadFile",[70,1407,500],{"class":84},[70,1409,1410],{"class":127},"Res",[70,1412,1413],{"class":84},"() res: Response) ",[70,1415,853],{"class":80},[70,1417,1418,1421,1425,1428,1431,1433,1435,1438,1440,1442],{"class":72,"line":161},[70,1419,1420],{"class":150},"  const",[70,1422,1424],{"class":1423},"s_hVV"," file",[70,1426,1427],{"class":445}," =",[70,1429,1430],{"class":127}," createReadStream",[70,1432,131],{"class":867},[70,1434,424],{"class":100},[70,1436,1437],{"class":104},"file.pdf",[70,1439,424],{"class":100},[70,1441,877],{"class":867},[70,1443,111],{"class":80},[70,1445,1446,1449,1451,1454,1456,1458,1461,1463,1465,1467,1470,1472,1474],{"class":72,"line":173},[70,1447,1448],{"class":84},"  res",[70,1450,861],{"class":80},[70,1452,1453],{"class":127},"setHeader",[70,1455,131],{"class":867},[70,1457,424],{"class":100},[70,1459,1460],{"class":104},"Content-Type",[70,1462,424],{"class":100},[70,1464,88],{"class":80},[70,1466,975],{"class":100},[70,1468,1469],{"class":104},"application\u002Fpdf",[70,1471,424],{"class":100},[70,1473,877],{"class":867},[70,1475,111],{"class":80},[70,1477,1478,1481,1483,1486,1488,1491,1493],{"class":72,"line":185},[70,1479,1480],{"class":84},"  file",[70,1482,861],{"class":80},[70,1484,1485],{"class":127},"pipe",[70,1487,131],{"class":867},[70,1489,1490],{"class":84},"res",[70,1492,877],{"class":867},[70,1494,111],{"class":80},[70,1496,1497],{"class":72,"line":200},[70,1498,261],{"class":80},[23,1500,1501,1503,1504,1506,1507,1510,1511,1514],{},[459,1502,740],{},"：一旦使用 ",[51,1505,635],{}," 并手动发送响应，NestJS 将中断该请求的处理链（不会执行拦截器中的 ",[51,1508,1509],{},"map"," 等操作）。如果仍希望让框架处理响应（例如只想要修改部分响应头），可以注入 ",[51,1512,1513],{},"@Res({ passthrough: true })","。在 Nest 9+ 中支持该选项。",[61,1516,1518],{"className":63,"code":1517,"language":65,"meta":66,"style":66},"@Get()\nfind(@Res({ passthrough: true }) res: Response) {\n  res.setHeader('X-Custom', 'Value');\n  return { data: 'hello' }; \u002F\u002F 仍然由框架自动序列化\n}\n",[51,1519,1520,1528,1557,1587,1611],{"__ignoreMap":66},[70,1521,1522,1524,1526],{"class":72,"line":73},[70,1523,124],{"class":80},[70,1525,167],{"class":127},[70,1527,170],{"class":84},[70,1529,1530,1533,1535,1537,1539,1541,1544,1546,1550,1552,1555],{"class":72,"line":114},[70,1531,1532],{"class":127},"find",[70,1534,500],{"class":84},[70,1536,1410],{"class":127},[70,1538,131],{"class":84},[70,1540,442],{"class":80},[70,1542,1543],{"class":867}," passthrough",[70,1545,465],{"class":80},[70,1547,1549],{"class":1548},"syTEX"," true",[70,1551,94],{"class":80},[70,1553,1554],{"class":84},") res: Response) ",[70,1556,853],{"class":80},[70,1558,1559,1561,1563,1565,1567,1569,1572,1574,1576,1578,1581,1583,1585],{"class":72,"line":121},[70,1560,1448],{"class":84},[70,1562,861],{"class":80},[70,1564,1453],{"class":127},[70,1566,131],{"class":867},[70,1568,424],{"class":100},[70,1570,1571],{"class":104},"X-Custom",[70,1573,424],{"class":100},[70,1575,88],{"class":80},[70,1577,975],{"class":100},[70,1579,1580],{"class":104},"Value",[70,1582,424],{"class":100},[70,1584,877],{"class":867},[70,1586,111],{"class":80},[70,1588,1589,1591,1593,1596,1598,1600,1603,1605,1608],{"class":72,"line":144},[70,1590,884],{"class":76},[70,1592,81],{"class":80},[70,1594,1595],{"class":867}," data",[70,1597,465],{"class":80},[70,1599,975],{"class":100},[70,1601,1602],{"class":104},"hello",[70,1604,424],{"class":100},[70,1606,1607],{"class":80}," };",[70,1609,1610],{"class":991}," \u002F\u002F 仍然由框架自动序列化\n",[70,1612,1613],{"class":72,"line":161},[70,1614,261],{"class":80},[41,1616,1618],{"id":1617},"四最佳实践","四、最佳实践",[263,1620,1621,1634,1646,1659],{},[266,1622,1623,1629,1630,1633],{},[459,1624,1625,1626,1628],{},"避免混合使用 ",[51,1627,635],{}," 和自动返回","：除非使用 ",[51,1631,1632],{},"{ passthrough: true }","，否则选择其中一种方式。",[266,1635,1636,1641,1642,1645],{},[459,1637,1638,1639],{},"使用 DTO 配合 ",[51,1640,653],{},"：通过 ",[51,1643,1644],{},"class-validator"," 进行数据验证。",[266,1647,1648,1651,1652,31,1654,31,1656,1658],{},[459,1649,1650],{},"路由参数装饰器顺序","：不影响功能，但通常按 ",[51,1653,469],{},[51,1655,668],{},[51,1657,653],{}," 排列。",[266,1660,1661,1664,1665,1668],{},[459,1662,1663],{},"全局前缀","：可以在 ",[51,1666,1667],{},"app.setGlobalPrefix('api')"," 设置全局路由前缀，与控制器前缀叠加。",[41,1670,1671],{"id":1671},"总结表",[292,1673,1674,1684],{},[295,1675,1676],{},[298,1677,1678,1681],{},[301,1679,1680],{},"场景",[301,1682,1683],{},"使用的装饰器\u002F工具",[311,1685,1686,1696,1709,1718,1728,1737,1746,1755,1763,1772,1781,1790,1803],{},[298,1687,1688,1691],{},[316,1689,1690],{},"定义路径前缀",[316,1692,1693],{},[51,1694,1695],{},"@Controller('prefix')",[298,1697,1698,1701],{},[316,1699,1700],{},"定义方法及子路径",[316,1702,1703,31,1706,1708],{},[51,1704,1705],{},"@Get('subpath')",[51,1707,333],{}," 等",[298,1710,1711,1714],{},[316,1712,1713],{},"获取请求体",[316,1715,1716],{},[51,1717,653],{},[298,1719,1720,1723],{},[316,1721,1722],{},"获取查询参数",[316,1724,1725],{},[51,1726,1727],{},"@Query('name')",[298,1729,1730,1732],{},[316,1731,685],{},[316,1733,1734],{},[51,1735,1736],{},"@Param('id')",[298,1738,1739,1741],{},[316,1740,700],{},[316,1742,1743],{},[51,1744,1745],{},"@Headers('key')",[298,1747,1748,1751],{},[316,1749,1750],{},"获取客户端 IP",[316,1752,1753],{},[51,1754,712],{},[298,1756,1757,1759],{},[316,1758,623],{},[316,1760,1761],{},[51,1762,616],{},[298,1764,1765,1768],{},[316,1766,1767],{},"获取原生响应对象（完全控制）",[316,1769,1770],{},[51,1771,635],{},[298,1773,1774,1777],{},[316,1775,1776],{},"修改状态码",[316,1778,1779],{},[51,1780,1014],{},[298,1782,1783,1786],{},[316,1784,1785],{},"添加响应头",[316,1787,1788],{},[51,1789,1069],{},[298,1791,1792,1795],{},[316,1793,1794],{},"重定向",[316,1796,1797,1799,1800],{},[51,1798,1144],{}," 或返回 ",[51,1801,1802],{},"{ url, statusCode }",[298,1804,1805,1808],{},[316,1806,1807],{},"流式\u002F文件响应",[316,1809,1810,1812],{},[51,1811,635],{}," 手动 pipe",[23,1814,1815],{},"通过合理使用这些装饰器，可以快速构建规范的 RESTful API，同时保持代码简洁和类型安全。",[1817,1818,1819],"h2",{"id":1819},"参考文献",[23,1821,1822],{},"以下链接在编写时均可正常访问：",[292,1824,1825,1835],{},[295,1826,1827],{},[298,1828,1829,1832],{},[301,1830,1831],{},"资料",[301,1833,1834],{},"说明",[311,1836,1837,1850,1862],{},[298,1838,1839,1847],{},[316,1840,1841],{},[27,1842,1846],{"href":1843,"rel":1844},"https:\u002F\u002Fdocs.nestjs.com\u002F",[1845],"nofollow","NestJS 文档",[316,1848,1849],{},"官方",[298,1851,1852,1859],{},[316,1853,1854],{},[27,1855,1858],{"href":1856,"rel":1857},"https:\u002F\u002Fdocs.nestjs.com\u002Fcontrollers",[1845],"Controllers",[316,1860,1861],{},"本章主题",[298,1863,1864,1871],{},[316,1865,1866],{},[27,1867,1870],{"href":1868,"rel":1869},"https:\u002F\u002Fdocs.nestjs.com\u002Ffaq\u002Frequest-lifecycle",[1845],"Request lifecycle",[316,1872,1873],{},"执行顺序",[1875,1876,1877],"style",{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":66,"searchDepth":114,"depth":114,"links":1879},[1880,1881,1882,1883,1884,1885],{"id":43,"depth":121,"text":44},{"id":590,"depth":121,"text":591},{"id":913,"depth":121,"text":914},{"id":1617,"depth":121,"text":1618},{"id":1671,"depth":121,"text":1671},{"id":1819,"depth":114,"text":1819},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fcontroller","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fcontroller.md","md",{"items":1891},[1892,1896,1900,1904,1908,1912],{"path":1893,"title":1894,"description":1895,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth","认证与授权","认证（Authentication）：确认「你是谁」（如 JWT、Session）。 - 授权（Authorization）：确认「你能做什么」（如 RBAC、策略检查）。",{"path":1897,"title":1898,"description":1899,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcache","缓存","@nestjs\u002Fcache-manager 统一缓存 API，存储实现可插拔。",{"path":1901,"title":1902,"description":1903,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fservice","提供者与服务","Service 是最常见的 Provider，封装业务逻辑。见 module。",{"path":1905,"title":1906,"description":1907,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fguards","守卫 (Guards) 与授权","守卫决定是否放行请求，常用于认证与授权。见 auth。",{"path":1909,"title":1910,"description":1911,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig","配置管理（Config 模块）","@nestjs\u002Fconfig 加载 .env 并提供 ConfigService。见 工程化 env。",{"path":1913,"title":1914,"description":1915,"category":10,"score":173},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdatabase","数据库集成（以 TypeORM 为例）","Nest 通过 @nestjs\u002Ftypeorm 等包集成 ORM；生产环境用 migration，慎用 synchronize。亦可选用 Prisma、MikroORM 等（见 官方 Database）。",{"series":1917,"title":1918,"items":1919,"index":144,"prev":1978,"next":1979},"booklet\u002Fend\u002Fnestjs\u002Fnew","new",[1920,1922,1925,1927,1929,1930,1934,1938,1940,1944,1948,1950,1954,1958,1962,1966,1968,1972,1974],{"path":1893,"title":1894,"date":1921,"pinned":15},"2025-12-23",{"path":29,"title":1923,"date":1924,"pinned":15},"NestJS 基础","2021-03-09",{"path":1897,"title":1898,"date":1926,"pinned":15},"2025-07-04",{"path":1909,"title":1910,"date":1928,"pinned":15},"2023-08-26",{"path":7,"title":8,"date":14,"pinned":15},{"path":1931,"title":1932,"date":1933,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron","定时任务","2023-04-12",{"path":1935,"title":1936,"date":1937,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcustom-decorators","自定义装饰器","2021-09-19",{"path":1913,"title":1914,"date":1939,"pinned":15},"2023-05-26",{"path":1941,"title":1942,"date":1943,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdeploy","部署与运维","2023-01-24",{"path":1945,"title":1946,"date":1947,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Ferror","异常处理","2020-01-28",{"path":1905,"title":1906,"date":1949,"pinned":15},"2023-09-09",{"path":1951,"title":1952,"date":1953,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fintercept","拦截器 (Interceptors)","2021-04-26",{"path":1955,"title":1956,"date":1957,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Flogger","日志记录","2022-10-10",{"path":1959,"title":1960,"date":1961,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmiddleware","中间件 (Middleware)","2021-11-26",{"path":1963,"title":1964,"date":1965,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmodule","模块系统与依赖注入","2022-06-15",{"path":34,"title":1967,"date":14,"pinned":15},"管道 (Pipes) 与数据验证",{"path":1969,"title":1970,"date":1971,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fqueue","队列（Bull）","2021-07-01",{"path":1901,"title":1902,"date":1973,"pinned":15},"2024-12-02",{"path":1975,"title":1976,"date":1977,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fwebsocket","WebSocket（网关）","2022-12-14",{"path":1909,"title":1910,"date":1928,"pinned":15},{"path":1931,"title":1932,"date":1933,"pinned":15}]