如何在NSwag Studio生成的TypeScript客户端中绑定自定义错误模型
配置NSwag Studio让TypeScript客户端绑定自定义错误模型
我正在开发一个项目,使用NSwag Studio从API的Swagger文档生成TypeScript客户端。我的API使用自定义错误模型(Error Response),需要确保TypeScript客户端在异常发生时能正确绑定该模型,而不是使用默认错误处理逻辑。我希望在API的Swagger中定义好自定义错误模型,让NSwag Studio生成的客户端能识别它,并修改生成的代码,将API错误响应映射到我自定义的错误类。请指导如何正确配置NSwag Studio来实现这个需求。
我的NSwag当前设置截图如下:
解决步骤
一、先确保Swagger文档正确定义自定义错误模型
先给API的Swagger文档做配置:所有可能返回错误的接口,都要在responses里为4xx、5xx状态码明确指定错误响应的Schema(对应你定义的IErrorResult模型)。只有Swagger里清晰定义了错误模型,NSwag才能识别并生成对应代码。
二、NSwag Studio核心配置
打开NSwag Studio,按以下步骤调整设置:
1. TypeScript客户端生成设置
切换到TypeScript Client标签页:
- 在Error handling区域:
- 勾选「Generate exception classes」
- 把「Exception type」改成你自定义的错误类名(比如
CustomErrorResultException) - 勾选「Use transform options」,在「Exception transformer」里配置响应到自定义错误类的映射逻辑(或者后续通过模板修改更灵活)
- 在Code generator settings里:
- 确认「Model generator settings」已经包含你的
IErrorResult模型,NSwag会自动从Swagger生成对应的TypeScript接口
- 确认「Model generator settings」已经包含你的
2. 用自定义模板修改异常生成逻辑
如果默认配置满足不了需求,直接改NSwag的Handlebars模板:
- 点击NSwag Studio里的「Edit Templates」按钮
- 找到ExceptionTemplate(对应生成
SwaggerException类的逻辑) - 把默认的
SwaggerException替换成你的自定义错误类结构,或者修改throwException函数,让它抛出你的CustomErrorResultException而非默认类。示例模板片段:
class {{exceptionType}} extends Error implements {{errorInterfaceName}} { public messages: string[]; public source?: string; public exception?: string; public errorId?: string; public supportMessage?: string; public statusCode: string; constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) { super(message); this.messages = result?.messages || []; this.source = result?.source; this.exception = result?.exception; this.errorId = result?.errorId; this.supportMessage = result?.supportMessage; this.statusCode = result?.statusCode?.toString() || status.toString(); if (!Array.isArray(this.messages)) { this.messages = [this.messages]; } } protected is{{exceptionType}} = true; static is{{exceptionType}}(obj: any): obj is {{exceptionType}} { return obj.is{{exceptionType}} === true; } } function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any { throw new {{exceptionType}}(message, status, response, headers, result); }
3. 确保生成代码能引用自定义错误接口
在Code generator settings的TypeScript settings里,设置「Import types from」为你的IErrorResult接口文件路径(比如../../api/IErrorResult),保证生成的代码能正确导入接口。
三、业务代码里的使用示例
生成客户端代码后,就可以直接在业务逻辑里用自定义错误类:
async function login(params: { username: string; password: string }): Promise<[number, any]> { const request = new TokenRequest(); request.email = params.username; request.password = params.password; try { const response = await tokensClient.tokens_GetToken(request); return [200, response]; } catch (error: unknown) { if (error instanceof CustomErrorResultException) { // 直接用自定义错误的属性 return [parseInt(error.statusCode), { messages: error.Messages, errorId: error.ErrorId }]; } else { // 兜底处理其他错误类型 const customError = CustomErrorResultException.fromApiResponse({ messages: [error instanceof Error ? error.message : '未知错误'], exception: 'UnexpectedError', statusCode: '500', }); return [500, customError]; } } }
四、验证生成结果
重新生成代码后,检查这几点:
- 生成的
CustomErrorResultException是否正确实现了IErrorResult接口 throwException函数是否抛出你的自定义错误类- API调用捕获异常时,能不能直接拿到自定义错误模型的数据
自定义错误类代码(CustomErrorExtension.ts)
// CustomErrorExtension.ts import { ApiException } from '../CustomErrorExtension'; import { IErrorResult } from '../../api/IErrorResult'; export class CustomErrorResultException extends ApiException implements IErrorResult { public Messages: string[]; public Source?: string; public Exception?: string; public ErrorId?: string; public SupportMessage?: string; public StatusCode: string; constructor(message: string, statusCode: string, details?: any, errorResult?: IErrorResult) { super(message, statusCode, details); // 映射ApiException属性到ErrorResult属性 this.Messages = errorResult?.messages ?? []; this.Source = errorResult?.source; this.Exception = errorResult?.exception; this.ErrorId = errorResult?.errorId; this.SupportMessage = errorResult?.supportMessage; this.StatusCode = errorResult?.statusCode ?? statusCode; // 确保Messages始终是数组 if (!Array.isArray(this.Messages)) { this.Messages = [this.Messages]; } } static fromApiResponse(apiResponse: any): CustomErrorResultException { const errorResult: IErrorResult = { messages: [], exception: '发生了意外错误', statusCode: '500', }; if (apiResponse && typeof apiResponse === 'object') { errorResult.messages = apiResponse.messages || []; errorResult.exception = apiResponse.exception || '发生了意外错误'; errorResult.statusCode = apiResponse.statusCode?.toString() || '500'; } return new CustomErrorResultException( apiResponse?.message || '发生了未知错误', apiResponse?.status || 500, undefined, errorResult ); } } // 扩展SwaggerException使其包含ErrorResult属性 class ExtendedSwaggerException extends Error implements IErrorResult { messages!: string[]; source?: string | undefined; exception?: string | undefined; errorId?: string | undefined; supportMessage?: string | undefined; email?: string | undefined; statusCode?: string | undefined; constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) { super(message); // 映射SwaggerException属性到ErrorResult属性 this.message = result.messages || []; this.source = result.source; this.exception = result.exception; this.errorId = result.errorId; this.supportMessage = result.supportMessage; this.statusCode = result.statusCode?.toString() || '500'; // 确保Messages始终是数组 if (!Array.isArray(this.message)) { this.messages = [this.message]; } } } // 用ExtendedSwaggerException替换原始的SwaggerException export class SwaggerException extends ExtendedSwaggerException {} async function login(params: { username: string; password: string }): Promise<[number, any]> { const request = new TokenRequest(); request.email = params.username; request.password = params.password; try { const response = await tokensClient.tokens_GetToken(request); return [200, response]; } catch (error: unknown) { debugger; if (error instanceof CustomErrorResultException) { // 直接返回自定义错误 return [ error.status, error.errorResult ]; } else if (error instanceof SwaggerException) { // 转换SwaggerException为CustomErrorResultException const customError = CustomErrorResultException.fromApiResponse({ messages: error.message.split('\n'), exception: error.name, statusCode: error.statusCode, }); // 返回自定义错误 return [ customError.status, customError.errorResult ]; } else if (error instanceof ApiException) { // 转换ApiException为CustomErrorResultException const customApiError = CustomErrorResultException.fromApiResponse({ messages: error.message.split('\n'), exception: error.name, statusCode: error.status.toString(), }); // 返回自定义错误 return [ customApiError.status, customApiError.errorResult ]; } else { // 处理其他错误类型 const errorMessage = error instanceof Error ? error.message : String(error); const customError = CustomErrorResultException.fromApiResponse({ messages: [errorMessage], exception: '发生了意外错误', statusCode: '500', }); return [ customError.status, customError.errorResult ]; } } }
NSwag Studio自动生成的代码
class SwaggerException extends Error { override message: string; status: number; response: string; headers: { [key: string]: any; }; result: any; constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) { super(); this.message = message; this.status = status; this.response = response; this.headers = headers; this.result = result; } protected isSwaggerException = true; static isSwaggerException(obj: any): obj is SwaggerException { return obj.isSwaggerException === true; } } function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any { throw new SwaggerException(message, status, response, headers, result); }
内容的提问来源于stack exchange,提问作者Sunil Kumar
相关产品推荐
相关产品推荐

