【问题标题】:Creating a custom error in golang for http responses在 golang 中为 http 响应创建自定义错误
【发布时间】:2021-07-10 13:37:49
【问题描述】:

我想为我目前正在处理的身份验证服务创建自定义错误。 由于我必须为每个 http 响应创建一个错误,而且我对 golang 还很陌生,所以我遇到了困难。 下面的代码是我想在 go 中实现的 javascript 代码的副本。

export abstract class CustomError extends Error {
abstract statusCode: number;

constructor(message: string) {
    super(message);

    Object.setPrototypeOf(this, CustomError.prototype);
}

abstract serializeErrors(): { message: string; field?: string }[];
}

基于这样的自定义错误创建扩展类

import { CustomError } from "./custom-error";

export class NotFoundError extends CustomError {
statusCode = 404;

constructor() {
    super("Route not found");

    Object.setPrototypeOf(this, NotFoundError.prototype);
}

serializeErrors() {
    return [{ message: "Not Found" }];
 }
}

这样我就可以从主文件中被抛出或记录,即 const existingUser = await User.findOne({ email });

    if (existingUser) {
        throw new BadRequestError("Email is already in use");
    }

所以我想用简单的语言创建一个 CustomErrors 的对象/模型,它可以帮助创建更多样化的 Erros,例如 BadRequestError()

所以,我需要有关创建这个的帮助。 这是我的第一个问题

【问题讨论】:

    标签: node.js go error-handling backend mux


    【解决方案1】:

    在 Go 中,您只需实现 error 接口即可创建自定义错误类型。

    error 接口是:

    type error interface {
        Error() string
    }
    

    [如果你完全不熟悉 Go,我建议从 Tour of Go on interfaces 开始]

    例如:

    type SyntaxError struct {
        msg    string // description of error
        Offset int64  // error occurred after reading Offset bytes
    }
    
    func (e *SyntaxError) Error() string { return e.msg }
    

    this official Go blog post for more details


    这是针对错误类型的;如果您专门查找 HTTP 错误,则通过将错误状态连同您需要的错误消息写入 http.ResponseWriter 来完成服务器中的错误,您可以为此使用帮助程序 http.Error。示例:

    func myHandler(w http.ResponseWriter, req *http.Request) {
      if (somethingIsWrong) {
        http.Error(w, "the error message", http.StatusBadRequest)
      }
    }
    

    http.Error 的最后一个参数是 HTTP 状态;查看net/http stdlib 包文档以了解您在那里的选项。

    现在,要将两者联系起来,http.Error 通常会使用自定义错误的Error() 方法作为消息,并且状态实际上是特定于应用程序的。

    【讨论】:

      【解决方案2】:

      如果您想要纯文本的 http 错误响应,http.Error 应该是您的选择。但是,如果您的身份验证服务需要特定格式(JSON/XML)的错误响应,那么您需要创建可以序列化并写入响应的自定义 http 错误。

      要为 JSON 格式创建自定义 http 错误响应(用于 XML 格式修改序列化),首先您需要创建一些类型 -

      type ErrFields map[string]string  // Error field-value pair type
      
      type ResponseError struct {
          Msg    string `json:"message"` // Error message
          Status int    `json:"status"`  // Http status code
          Data   ErrFields               // For extra error fields e.g. reason, details, etc.
      }
      
      type ErrList []ResponseError       // Multiple http errors type
      

      ResponseError 类型的方法-

      // AddErrField adds a new field to the response error with given key and value
      func (err *ResponseError) AddErrField(key, value string) {
          if err.Data == nil {
              err.Data = make(ErrFields)
          }
          err.Data[key] = value
      }
      
      // RemoveErrField removes existing field matching given key from response error
      func (err *ResponseError) RemoveErrField(key string) {
          delete(err.Data, key)
      }
      
      // MarshalJSON marshals the response error into json 
      func (err *ResponseError) MarshalJSON() ([]byte, error) {
          // Determine json field name for error message
          errType := reflect.TypeOf(*err)
          msgField, ok := errType.FieldByName("Msg")
          msgJsonName := "message"
          if ok {
              msgJsonTag := msgField.Tag.Get("json")
              if msgJsonTag != "" {
                  msgJsonName = msgJsonTag
              }
          }
          // Determine json field name for error status code
          statusField, ok := errType.FieldByName("Status")
          statusJsonName := "status"
          if ok {
              statusJsonTag := statusField.Tag.Get("json")
              if statusJsonTag != "" {
                  statusJsonName = statusJsonTag
              }
          }
          fieldMap := make(map[string]string)
          fieldMap[msgJsonName] = err.Msg
          fieldMap[statusJsonName] = fmt.Sprintf("%d", err.Status)
          for key, value := range err.Data {
              fieldMap[key] = value
          }
          return json.Marshal(fieldMap)
      }
      
      // SerializeJSON converts response error into serialized json string
      func (resErr *ResponseError) SerializeJSON() (string, error) {
          value, err := json.Marshal(resErr)
          if err != nil {
              return "", err
          }
          return string(value), nil
      }
      

      ErrList 类型的方法-

      // SerializeJSON converts error list into serialized json string
      func (errList ErrList) SerializeJSON() (string, error) {
          value, err := json.Marshal(errList)
          if err != nil {
              return "", err
          }
          return string(value), nil
      }
      

      现在您可以通过创建不同的 ResponseError 类型值来创建自定义 http 错误响应 -

      // Error returns a general response error
      func Error(msg string, status int) ResponseError {
          return ResponseError{msg, status, nil}
      }
      
      // Errors returns a error list containing given response errors
      func Errors(errors ...ResponseError) ErrList {
          return errors
      }
      
      // Specific HTTP error responses
      
      func ErrorNotFound() ResponseError {
          return Error("not found", http.StatusNotFound)
      }
      
      func ErrorBadRequest() ResponseError {
          return Error("bad request", http.StatusBadRequest)
      }
      
      func ErrorInternalServerError() ResponseError {
          return Error("internal server error", http.StatusInternalServerError)
      }
      
      func ErrorForbidden() ResponseError {
          return Error("forbidden", http.StatusForbidden)
      }
      

      您可以向ResponseError 值添加/删除自定义字段 -

      notFoundErr := ErrorNotFound()
      notFoundErr.AddErrField("reason", "given 'id' does not exist")
      notFoundErr.RemoveErrField("reason")
      

      由于 Go 中没有 throw 的概念,所以只能从函数返回响应错误 -

      func Foo() (resErr ResponseError, ok bool) {
          ...
          if existingUser {
             resErr = ErrorBadRequest()
             resErr.AddErrField("reason", "Email is already in use")
             return resErr, true
          }
          ...
          return ResponseError{}, false
      }
      

      将响应错误序列化为 JSON -

      resErr, ok := Foo()
      if !ok {
          json, err := resErr.SerializeJSON()
          if err != nil {
              // Handle serialization error
          }
      }
      
      

      请参阅 Go 操场示例 here

      【讨论】:

        猜你喜欢
        • 2022-09-30
        • 2014-12-01
        • 2013-06-08
        • 2018-01-29
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 2020-01-14
        • 1970-01-01
        相关资源
        最近更新 更多