/* * Copyright (c) 2025 SimcuTeam. All rights reserved. * 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。 */ package simapi.middlewares import soulsoft_web_http.* import soulsoft_extensions_logging.* import simapi_serialization.* import simapi.communications.* import simapi.exceptions.* import simapi.configurations.* import simapi.helpers.* /** * 异常处理中间件:全异常捕获,统一输出 HTTP 200 + JSON 响应。 * 对应 C# 的 SimApi.Middlewares.SimApiExceptionMiddleware。 */ public class SimApiExceptionMiddleware <: IMiddleware { private let _options: SimApiOptions private let _logger: ILogger public init(options: SimApiOptions, loggerFactory: ILoggerFactory) { _options = options _logger = loggerFactory.createLogger("SimApi.ExceptionMiddleware") } /** * 中间件入口(IMiddleware 风格,对齐 C# UseMiddleware 的 InvokeAsync)。 */ public func invoke(context: HttpContext, next: RequestDelegate): Unit { try { // 透传 Query-Id 请求头 if (let Some(queryId) <- context.request.headers.get("Query-Id")) { context.response.headers.add("Query-Id", queryId) } next(context) // 若响应未开始且状态码不在跳过列表,则视为业务错误 if (!context.response.hasStarted) { let code = Int64(context.response.statusCode) if (!_options.simApiExceptionOptions.skipStatusCodes.contains(code)) { throw SimApiException(code) } } } catch (ex: Exception) { let response = buildResponse(ex, context) if (!context.response.hasStarted) { context.response.statusCode = 200 context.response.contentType = "application/json; charset=utf-8" SimApiResponseWriter.write(context, responseJson(response)) } } } private func buildResponse(ex: Exception, context: HttpContext): SimApiBaseResponse { if (let simEx: SimApiException <- ex) { let message = if (simEx.message.isEmpty()) { SimApiBaseResponse.getDefaultMessage(simEx.code) } else { simEx.message } var response = SimApiBaseResponse(simEx.code, message) if (context.response.statusCode == 404) { response.message = "接口不存在" } return response } _logger.error(EventId(0), ex, ex.message) return SimApiBaseResponse(500) } private func responseJson(response: SimApiBaseResponse): String { JsonSerializer.Serialize(response) } }