【问题标题】:How to handle invalid parameters in REST service?如何处理 REST 服务中的无效参数?
【发布时间】:2015-01-21 14:39:49
【问题描述】:

我使用spring @RestController 提供REST 网络服务。

一般无效的参数内容应该如何处理?我尝试抛出一个自定义异常,但这会导致客户端出现HTTP 500 错误,从而暴露堆栈跟踪。

这可能不是正确的方法。但是应该如何只返回简单的错误消息呢? (webservice不会被用户手动访问。只是由连接到rest控制器的其他服务)。

【问题讨论】:

  • 如何使用 javax.validation 和 Hibernate Validator 实现?它应该工作得很好。有关示例,请参阅此博客文章:captechconsulting.com/blog/jens-alm/…
  • 问题不在于如何验证参数。问题是如何创建一个简单但有用的错误消息,而不是将堆栈跟踪扔回客户端。
  • 好吧,如果 javax.validation 抛出异常,那么您有很多信息可以通过简单的 JSON 响应呈现给用户。 Spring 中的 ExceptionMappers 应该足以创建您需要的内容 (journaldev.com/2651/…)。

标签: java spring web-services rest


【解决方案1】:

我正在使用 jersey,这是一个简单的示例,它将使用 hibernate bean 验证框架来验证您的 bean。这是一项正在进行的工作,但您应该可以非常简单地看到它是如何工作的。

@Path("customers")
public class CustomerResource {
    @PUT
    public Response createCustomer(Customer customer) {
        BeanValidator.validate(customer);
        final String rialtoId = customerProvider.createCustomer(customer);

        return Response.ok(rialtoId).build();
    }
}

这是我创建的处理 bean 验证的通用类。

public class BeanValidator {

    /**
     * Used to validate an order request and all the attached objects that
     * support validation.
     * 
     * @param request
     * @throws ConstraintViolationException
     */
    public static <T> void validate(T request) throws ConstraintViolationException {
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            Validator validator = factory.getValidator();
            Set<ConstraintViolation<T>> constraintViolations = validator.validate(request);
            if (constraintViolations.size() > 0) {
                    throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(constraintViolations));
            }
    }
}


@XmlRootElement
public class Customer {

    @NotNull(message = "spCustomerID1 is a required field")
    @Size(max = 60, message = "spCustomerID1 has a max length of 60 characters")
    private String spCustomerID1;

    @Size(max = 60, message = "spCustomerID2 has a max length of 60 characters")
    private String spCustomerID2;

    @Size(max = 60, message = "spCustomerID3 has a max length of 60 characters")
    private String spCustomerID3;

    @NotNull(message = "customerName is a required field")
    @Size(max = 60)
    private String customerName;

    @Valid
    @NotNull(message = "customerAddress is a required field")
    private PostalAddress customerAddress;

    @Valid
    @NotNull(message = "customerContact is a required field")
    private ContactInfo customerContact;

    @Valid
    @NotNull(message = "technicalContact is a required field")
    private ContactInfo technicalContact;
    ... / Getters and Setters
}

然后这里是一个简单的 ExceptionMapper,它将支持构造一个简单的响应以发送回客户端。请注意,它将响应类型设置为 400 BAD_REQUEST 而不是 500+ 服务器端错误。

public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {

    public Response toResponse(ConstraintViolationException exception) {

        final StringBuilder strBuilder = new StringBuilder();

        for (ConstraintViolation<?> cv : exception.getConstraintViolations()) {
            strBuilder.append(cv.getPropertyPath().toString() + " " + cv.getMessage());
        }

        RestResponse responseEntity = RestResponse.responseCode(ResponseCode.CONSTRAINT_VIOLATION).setResponseMessage(strBuilder.toString()).build();

        return Response.status(Response.Status.BAD_REQUEST).entity(responseEntity).build();
    }
}

此代码尚未经过测试,但它可能有助于了解如何进行验证。在我看来,这是一种非常直接的方式来进行休息服务验证,并允许您报告准确的变量路径以及每个字段的自定义错误消息。

【讨论】:

    【解决方案2】:

    您应该在应用程序的最外层验证您的参数,然后再将其传递到您的域中。此时您仍处于 HTTP 层,因此可以采取适当的措施,即返回 400 BAD REQUEST 状态。

    在此范围内,您可以完全控制如何将此信息传递给您的用户(或其他服务)。如果您只是记录纯文本,或者设计您自己的 Json/Xml 有效负载来描述错误,那么纯文本就可以了。

    【讨论】:

      【解决方案3】:

      如果我理解你的话,

      那么,通常我认为最好在每个 json 响应(或者即使您的响应是 XML)中有一个键,这表明进程的状态。这个字段可以称为状态。

      因此,您发回的每个响应都应该有这个 status 字段,它的值应该表明处理过程中发生了什么,以及调用者应该在响应中期待什么。

      这个值可以是一个数字,或者一个文本信息,一些类似常量的信息 您也可以添加另一个字段,message,其中包含一些状态代码的文本描述。

      现在你必须列出你的服务可能发回的可能的雕像。

      例如:

      status: 0000 
      message: success
      
      status: 0001 
      message: invalid_params
      
      status: 0002 
      message: invalid_param_value
      
      status: 0003 
      message: missing_param,
      :
      :
      etc
      

      所以您的 json 响应将始终包含这些字段。在应该返回的其他数据中。

      现在处理这些响应是客户的职责。

      JSON 示例:

      {
      "status":"0000",
      "message":"success",
      "images":[ ... ]
      }
      
      {
      "status":"0003",
      "message":"missing_param"
      }
      

      您注意到,在非0000 状态的情况下,不会发回其他数据。 只是告诉客户我们有“这个问题”。

      或者您可以通过在错误消息常量中添加: 来提供更多信息,以提供有关错误的更多信息:

      例如,

      {
      "status":"0003",
      "message":"missing_param:album_id"
      }
      

      告诉用户,缺少一个参数,是album_id

      现在你可以写出所有可能的status 回复,还有message 这将成为您的服务文档的一部分。

      【讨论】:

      • REST/HTTP 协议本身已经为您定义了一些。 200 表示成功,400 表示错误请求 - 这些应该被重用。在这些内部构建自己的消息很好,但不应该代替众所周知的代码使用
      • @MrWiggles 我 100% 了解 Http 协议响应代码,因此问题中的 OP 这个resp code 500 - 如果你看一下 - 他看起来不喜欢使用它们。
      • 很抱歉,您的回答或问题均未提及有关使用正确响应代码的任何内容,唯一提及的代码是 500
      • @MrWiggles 确切地说,500 意味着 - 作为一个概念 - 我们(OP 和我)都知道这种事情的存在。谢谢
      • 我并没有试图变得困难,我知道 OP 知道 500 响应代码,但他们知道无效参数必须返回 400 代码并不明显。我见过会返回 500 或 200 的“REST”系统,并希望您解码返回的有效负载以查看是否发生错误以及它是什么类,使用正确的代码可以避免这种情况
      【解决方案4】:

      如果你手动验证你的参数,你可能会抛出一个特定的异常。然后您可以将您的异常映射到特定的 HTTP 状态,例如 BAD REQUEST。

      您可以使用 Spring Controller Advice 将您的异常映射到响应状态:http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-12
        • 2023-02-05
        • 1970-01-01
        • 1970-01-01
        • 2014-03-03
        • 1970-01-01
        • 2016-05-04
        • 1970-01-01
        相关资源
        最近更新 更多