【问题标题】:How do I return an error from a Controller in Loopback 4?如何从 Loopback 4 中的控制器返回错误?
【发布时间】:2019-08-13 23:35:54
【问题描述】:

我有一个控制器方法

// ... inside a controller class

@get('/error', {})
async error() {
  throw new Error("This is the error text");
}

我从这个错误前端得到的响应是:

{ “错误”: { “状态码”:500, “消息”:“内部服务器错误” } }

我希望错误是:

{ “错误”: { “状态码”:500, "message": "这是错误文本" } }

如何在 Loopback 4 中从控制器返回错误?

【问题讨论】:

    标签: error-handling loopbackjs loopback4


    【解决方案1】:

    来自 LoopBack 团队的问候 ?

    在您的控制器或存储库中,您应该完全按照您的问题所示抛出错误。

    现在,当 LoopBack 捕获错误时,它会调用 reject 操作来处理它。 reject 的内置实现通过 console.error 记录一条消息,并返回带有 4xx/5xx 错误代码和描述错误的响应正文的 HTTP 响应。

    默认情况下,LoopBack 会隐藏 HTTP 响应中的实际错误消息。这是一种安全措施,可防止服务器泄露潜在的敏感数据(无法打开的文件路径、无法访问的后端服务的 IP 地址)。

    在底层,我们使用strong-error-handler 将错误对象转换为 HTTP 响应。该模块提供两种模式:

    • 生产模式(默认):5xx 错误不包含任何附加信息,4xx 错误包含部分信息。
    • 调试模式 (debug: true):所有错误详细信息都包含在响应中,包括完整的堆栈跟踪。

    调试模式可以通过添加下面一行到你的应用构造函数来启用:

    this.bind(RestBindings.ERROR_WRITER_OPTIONS).to({debug: true});
    

    在我们的文档中了解更多信息:Sequence >> Handling errors

    或者,您可以实现自己的错误处理程序并将其绑定为序列操作reject。请参阅我们的文档中的 Customizing sequence actions

    export class MyRejectProvider implements Provider<Reject> {
      constructor(
        @inject(RestBindings.SequenceActions.LOG_ERROR)
        protected logError: LogError,
        @inject(RestBindings.ERROR_WRITER_OPTIONS, {optional: true})
        protected errorWriterOptions?: ErrorWriterOptions,
      ) {}
    
      value(): Reject {
        return (context, error) => this.action(context, error);
      }
    
      action({request, response}: HandlerContext, error: Error) {
        const err = <HttpError>error;
    
        const statusCode = err.statusCode || err.status || 500;
        const body = // convert err to plain data object
    
        res.statusCode = statusCode;
        res.setHeader('Content-Type', 'application/json; charset=utf-8');
        res.end(JSON.stringify(body), 'utf-8');
    
        this.logError(error, statusCode, request);
      }
    }
    

    【讨论】:

    • 这些是一些非常有趣的细节,但我仍然不明白这与从控制器或存储库中返回错误有何关系。我无法访问响应回调或拒绝回调,并且抛出不起作用。
    • @SephReed 我更新了我的回复,希望现在更清楚。
    • 您好!但是如果我想返回一个更清晰的错误消息,告诉用户发生了什么?
    • @EmilioNumazaki 请参阅“convert err to plain data object”这一行 - 您可以在此处将 Error 对象转换为用户友好的 HTTP 响应正文。
    • 此解决方案不适用于 Loopback 的最新版本,其中默认序列不是动作序列,而是它的中间件序列。
    【解决方案2】:

    如果您只想显示错误消息,您只需扩展 Error 对象并将其抛出如下。 (无论如何,Loopback 文档都没有提到这一点)

    避免使用 5xx 错误并使用 4xx 错误向用户展示一些重要的东西是最佳实践,因此 Loopback4 是这样实现的。

    class NotFound extends Error {
      statusCode: number
    
      constructor(message: string) {
        super(message)
        this.statusCode = 404
      }
    }
    

    ...

    if (!await this.userRepository.exists(id)) {
      throw new NotFound('user not found')
    }
    

    【讨论】:

    • 太棒了!这是公开异常消息的最干净的方法!谢谢!
    【解决方案3】:

    对于我的情况,我在我的sequence.ts 文件中找到了一个catch。在 catch 内部,它检查错误的状态码是否为 4xx,如果没有,则返回一个匿名 500。

    这是我正在寻找执行逻辑的代码:

    // sequence.ts
    ...
    } catch (err) {
      console.log(err);
      let code: string = (err.code || 500).toString();
      if (code.length && code[0] === '4') {
        response.status(Number(code) || 500);
        return this.send(response, {
          error: {
            message: err.message,
            name: err.name || 'UnknownError',
            statusCode: code
          }
        });
      }
      return this.reject(context, err);
    }
    ...
    

    这是你告诉它做什么的方法:

    // ... inside a controller class
    
    @get('/error', {})
    async error() {
      throw {
        code: 400,
        message: "This is the error text",
        name: "IntentionalError"
      }
    }
    

    【讨论】:

      【解决方案4】:

      要抛出自定义验证错误,我使用此方法:

      private static createError(msg: string, name?: string): HttpErrors.HttpError {
          const error = new HttpErrors['422'](msg);
          error.name = name ?? this.name;
          return error;
      }
      

      这里的捕获错误示例是针对 defaultSequence,覆盖了句柄方法。 但是现在的应用模板使用的是 MiddlewareSequence。

      所以这里是例子,如何修改中间件序列中的响应,你可以使用这个例子:

      import { Middleware, MiddlewareContext } from '@loopback/rest';
      
      export const ErrorMiddleware: Middleware = async (middlewareCtx: MiddlewareContext, next) => {
          // const {response} = middlewareCtx;
          try {
              // Proceed with next middleware
              return await next();
          } catch (err) {
              // Catch errors from downstream middleware
              // How to catch specific error and how to send custom error response:
              if (HttpErrors.isHttpError(err) || (err as HttpErrors.HttpError).statusCode) {
                  const code: string = (err.statusCode || 500).toString();
                  if (code.length && code[0] === '4') {
                      response.status(Number(code) || 500);
                      return response.send({
                          error: {
                              message: err.message,
                              name: err.name || 'UnknownError',
                              statusCode: code
                          }
                      });
                  }
              }
              throw err;
          }
      };
      

      并在application.ts中注册中间件

      this.middleware(ErrorMiddleware);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-17
        • 2016-05-21
        • 1970-01-01
        • 2015-03-23
        • 2020-08-13
        • 1970-01-01
        • 2018-09-19
        • 2020-11-12
        相关资源
        最近更新 更多