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