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