You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NSwag Studio生成的TypeScript客户端中绑定自定义错误模型

配置NSwag Studio让TypeScript客户端绑定自定义错误模型

我正在开发一个项目,使用NSwag Studio从API的Swagger文档生成TypeScript客户端。我的API使用自定义错误模型(Error Response),需要确保TypeScript客户端在异常发生时能正确绑定该模型,而不是使用默认错误处理逻辑。我希望在API的Swagger中定义好自定义错误模型,让NSwag Studio生成的客户端能识别它,并修改生成的代码,将API错误响应映射到我自定义的错误类。请指导如何正确配置NSwag Studio来实现这个需求。

我的NSwag当前设置截图如下:
NSwag设置截图1
NSwag设置截图2
NSwag设置截图3


解决步骤

一、先确保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接口

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:59:50