【问题标题】:What is the best way to return different types of ResponseEntity in Spring MVC or Spring-Boot在 Spring MVC 或 Spring-Boot 中返回不同类型的 ResponseEntity 的最佳方法是什么
【发布时间】:2016-11-02 05:32:45
【问题描述】:

我使用 Spring MVC 4(或 Spring-Boot)编写了简单的 rest 应用程序。在控制器内我返回ResponseEntity。但在某些情况下,我想给出成功 JSON,如果有验证错误,我想给出错误 JSON。目前成功和错误响应完全不同,所以我为错误和成功创建了 2 个类。如果内部逻辑没问题,我想在控制器内返回 ResponseEntity<Success> 。否则我想返回ResponseEntity<Error>。有什么办法吗。

SuccessError 是我用来表示成功和错误响应的 2 个类。

【问题讨论】:

  • 一种解决方案是使用ResponseEntity<Object>。另一种是对 Success 和 Error 类型使用 BaseClass 或 Interface。
  • 你不能只在你的方法定义中返回ResponseEntity 吗? ` public ResponseEntity myControllerMethod(...) 并返回 ResponseEntity<Success>ResponseEntity<Error>
  • ResponseEntity<Either<Error, Success>> 会很好,如果你能设法在java中实现数据结构

标签: java json spring-mvc spring-boot


【解决方案1】:

使用自定义异常类可以返回不同的 HTTP 状态码和 dto 对象。

@PostMapping("/save")
public ResponseEntity<UserDto> saveUser(@RequestBody UserDto userDto) {
    if(userDto.getId() != null) {
        throw new UserNotFoundException("A new user cannot already have an ID");
    }
    return ResponseEntity.ok(userService.saveUser(userDto));
}

异常类

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "user not found")
public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {

        super(message);
    }
}

【讨论】:

    【解决方案2】:

    Spring 2 引入了ResponseStatusException,使用这个你可以返回字符串,不同的HTTP状态码,DTO同时

    @PostMapping("/save")
    public ResponseEntity<UserDto> saveUser(@RequestBody UserDto userDto) {
        if(userDto.getId() != null) {
            throw new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE,"A new user cannot already have an ID");
        }
        return ResponseEntity.ok(userService.saveUser(userDto));
    }
    

    【讨论】:

      【解决方案3】:

      对于特殊情况,我建议您在应用程序中采用RFC-7807 Problem Details for HTTP APIs 标准。

      Zalando's Problems for Spring 提供了与 Spring Boot 的良好集成,您可以轻松地将其与现有的基于 Spring Boot 的应用程序集成。就像JHipster 所做的那样。

      在您的应用程序中采用 RFC-7087 后,只需在您的控制器方法中抛出异常,您就会得到详细而标准的错误响应,例如:

         {
          "type": "https://example.com/probs/validation-error",
          "title": "Request parameter is malformed.",
          "status": 400
          "detail": "Validation error, value of xxx should be a positive number.",
          "instance": "/account/12345/msgs/abc",
         }
      

      【讨论】:

        【解决方案4】:

        我建议使用 Spring 的 @ControllerAdvice 来处理错误。阅读this guide 以获得很好的介绍,从名为“Spring Boot 错误处理”的部分开始。如需深入讨论,请参阅 2018 年 4 月更新的 Spring.io 博客中的 an article

        关于其工作原理的简要总结:

        • 您的控制器方法应该只返回ResponseEntity&lt;Success&gt;。它不负责返回错误或异常响应。
        • 您将实现一个处理所有控制器异常的类。这个类将被注释为@ControllerAdvice
        • 此控制器建议类将包含用@ExceptionHandler 注释的方法
        • 每个异常处理程序方法都将被配置为处理一种或多种异常类型。您可以在这些方法中指定错误的响应类型
        • 对于您的示例,您将(在控制器建议类中)声明验证错误的异常处理程序方法。返回类型为ResponseEntity&lt;Error&gt;

        使用这种方法,您只需在一处为 API 中的所有端点实现控制器异常处理。它还使您的 API 可以轻松地在所有端点上拥有统一的异常响应结构。这为您的客户简化了异常处理。

        【讨论】:

        • 这是标准,应该是 IMO 接受的答案。
        • 不建议通过 Java 中的异常处理预期的应用程序流这一事实如何?例如,getCustomerForIBAN 返回一个通过 REST api GET /api/customer/{iban} 公开的 Optional,它返回 200 ok 还是 404 not found?你会建议然后抛出一个异常并以同样的方式处理吗?
        • 如果你想避免在Java中使用异常,你可以设计你的控制器方法来返回一个ResponseEntityResponseEntity 允许您控制返回的 HTTP 状态码,它是一个泛型类型,因此您可以返回任何对象结构。下面是如何使用它的解释:baeldung.com/spring-response-entity
        • 这真的是处理“验证错误”的标准吗?验证错误是服务层中的受控流程(大概)。为什么我们要让异常冒泡到控制器级别而不进行处理?我了解意外异常(即 5xx 错误代码),但不了解验证异常(4xx)。我错过了什么吗?
        • 这是处理应用程序其他层允许向上传播或显式抛出的错误的标准。 RE:验证错误,服务层仍然可以捕获和处理。当我在第一句话中说“处理验证错误”时,我的限制太大了。我删除了“验证”以表明这是针对一般错误。感谢您指出这一点。
        【解决方案5】:

        注意:如果您从 Spring Boot 1 升级到 Spring Boot 2,则会有一个 ResponseStatusException,其中包含一个 Http 错误代码和描述。

        因此,您可以按照预期的方式有效地使用泛型。

        唯一对我来说有点挑战的情况是状态 204 的响应类型(好的,没有正文)。我倾向于将这些方法标记为ResponseEntity&lt;?&gt;,因为ResponseEntity&lt;Void&gt; 的预测性较差。

        【讨论】:

          【解决方案6】:

          您可以返回通用通配符&lt;?&gt; 以在同一请求映射方法上返回SuccessError

          public ResponseEntity<?> method() {
              boolean b = // some logic
              if (b)
                  return new ResponseEntity<Success>(HttpStatus.OK);
              else
                  return new ResponseEntity<Error>(HttpStatus.CONFLICT); //appropriate error code
          }
          

          @Mark Norman 的回答是正确的方法

          【讨论】:

          • 只有代码质量工具(如 Sonar Lint)用 通用通配符类型不应该在返回参数中使用(squid:S1452)跨度>
          • 这是一个hack如何规避Java泛型(不完美)机制的方法。 @MarkNorman 的回答是标准,应该被接受。
          • 我刚刚遇到了同样的问题,我正在研究创建可以与 VAVR Either 一起使用的东西,这样我就可以拥有 public ResponseEntity&lt;Either&lt;Error, Success&gt;&gt; method() 或更好的 public Either&lt;ResponseEntity&lt;Error&gt;, ResponseEntity&lt;Success&gt;&gt; method()。我认为做到这一点的方法是创建一个HttpMessageConverter,它知道如何处理它,并且只需将 Either 转换为左侧/右侧并让正常处理发生..这样我就可以在没有有效状态的情况下发出信号使用异常......对此有什么想法吗?
          【解决方案7】:

          我不确定,但我认为您可以使用 @ResponseEntity@ResponseBody 并发送 2 个不同的一个是成功,第二个是错误消息,例如:

          @RequestMapping(value ="/book2", produces =MediaType.APPLICATION_JSON_VALUE )
          @ResponseBody
          Book bookInfo2() {
              Book book = new Book();
              book.setBookName("Ramcharitmanas");
              book.setWriter("TulasiDas");
              return book;
          }
          
          @RequestMapping(value ="/book3", produces =MediaType.APPLICATION_JSON_VALUE )
          public ResponseEntity<Book> bookInfo3() {
              Book book = new Book();
              book.setBookName("Ramayan");
              book.setWriter("Valmiki");
              return ResponseEntity.accepted().body(book);
          }
          

          更多详情请参考: http://www.concretepage.com/spring-4/spring-4-mvc-jsonp-example-with-rest-responsebody-responseentity

          【讨论】:

            【解决方案8】:

            您可以将地图与您的对象或字符串一起使用,如下所示:

            @RequestMapping(value = "/path", 
                    method = RequestMethod.GET, 
                    produces = MediaType.APPLICATION_JSON_VALUE)
                @ResponseBody
                public ResponseEntity<Map<String,String>> getData(){
            
                Map<String,String> response = new HashMap<String, String>();
            
                boolean isValid = // some logic
                if (isValid){
                    response.put("ok", "success saving data");
                    return ResponseEntity.accepted().body(response);
                }
                else{
                    response.put("error", "an error expected on processing file");
                    return ResponseEntity.badRequest().body(response);
                }
            
            }
            

            【讨论】:

              【解决方案9】:

              你也可以这样实现,在同一个请求映射方法上返回成功和错误,使用对象类(java中每个类的父类):-

              public ResponseEntity< Object> method() {                                                                                                                                                                                                                                                                                                                                                                                  
                  boolean b = //  logic  here   
                    if (b)  
                      return new ResponseEntity< Object>(HttpStatus.OK);      
                  else      
                      return new ResponseEntity< Object>(HttpStatus.CONFLICT); //appropriate error code   
              }
              

              【讨论】:

                【解决方案10】:

                这是我会做的一种方式:

                public ResponseEntity < ? extends BaseResponse > message(@PathVariable String player) { //REST Endpoint.
                
                 try {
                  Integer.parseInt(player);
                  return new ResponseEntity < ErrorResponse > (new ErrorResponse("111", "player is not found"), HttpStatus.BAD_REQUEST);
                 } catch (Exception e) {
                
                
                 }
                 Message msg = new Message(player, "Hello " + player);
                 return new ResponseEntity < Message > (msg, HttpStatus.OK);
                
                }
                
                @RequestMapping(value = "/getAll/{player}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
                public ResponseEntity < List < ? extends BaseResponse >> messageAll(@PathVariable String player) { //REST Endpoint.
                
                 try {
                  Integer.parseInt(player);
                  List < ErrorResponse > errs = new ArrayList < ErrorResponse > ();
                  errs.add(new ErrorResponse("111", "player is not found"));
                  return new ResponseEntity < List < ? extends BaseResponse >> (errs, HttpStatus.BAD_REQUEST);
                 } catch (Exception e) {
                
                
                 }
                 Message msg = new Message(player, "Hello " + player);
                 List < Message > msgList = new ArrayList < Message > ();
                 msgList.add(msg);
                 return new ResponseEntity < List < ? extends BaseResponse >> (msgList, HttpStatus.OK);
                
                }
                

                【讨论】:

                • 添加一些解释,说明此答案如何帮助 OP 解决当前问题
                【解决方案11】:

                不使用泛型也可以返回ResponseEntity,如下所示,

                public ResponseEntity method() {
                    boolean isValid = // some logic
                    if (isValid){
                        return new ResponseEntity(new Success(), HttpStatus.OK);
                    }
                    else{
                        return new ResponseEntity(new Error(), HttpStatus.BAD_REQUEST);
                    }
                }
                

                【讨论】:

                • 不指定泛型类型会导致GsonHttpMessageConverter 抱怨并抛出错误。但只需修改为ResponseEntity&lt;?&gt; 即可。
                • 是的.. 无论如何,当使用 Jackson 时,没有泛型也可以使用。
                • 它会被大多数代码检查工具标记。因此,您必须为此特定响应类型添加异常或禁止它们。 - 不是一个大粉丝。
                【解决方案12】:

                我曾经使用过这样的课程。 statusCodemessage 中设置的错误消息出现错误时设置。数据在适当的时候存储在地图或列表中。

                /**
                * 
                */
                package com.test.presentation.response;
                
                import java.util.Collection;
                import java.util.Map;
                
                /**
                 * A simple POJO to send JSON response to ajax requests. This POJO enables  us to
                 * send messages and error codes with the actual objects in the application.
                 * 
                 * 
                 */
                @SuppressWarnings("rawtypes")
                public class GenericResponse {
                
                /**
                 * An array that contains the actual objects
                 */
                private Collection rows;
                
                /**
                 * An Map that contains the actual objects
                 */
                private Map mapData;
                
                /**
                 * A String containing error code. Set to 1 if there is an error
                 */
                private int statusCode = 0;
                
                /**
                 * A String containing error message.
                 */
                private String message;
                
                /**
                 * An array that contains the actual objects
                 * 
                 * @return the rows
                 */
                public Collection getRows() {
                    return rows;
                }
                
                /**
                 * An array that contains the actual objects
                 * 
                 * @param rows
                 *            the rows to set
                 */
                public void setRows(Collection rows) {
                    this.rows = rows;
                }
                
                /**
                 * An Map that contains the actual objects
                 * 
                 * @return the mapData
                 */
                public Map getMapData() {
                    return mapData;
                }
                
                /**
                 * An Map that contains the actual objects
                 * 
                 * @param mapData
                 *            the mapData to set
                 */
                public void setMapData(Map mapData) {
                    this.mapData = mapData;
                }
                
                /**
                 * A String containing error code.
                 * 
                 * @return the errorCode
                 */
                public int getStatusCode() {
                    return statusCode;
                }
                
                /**
                 * A String containing error code.
                 * 
                 * @param errorCode
                 *            the errorCode to set
                 */
                public void setStatusCode(int errorCode) {
                    this.statusCode = errorCode;
                }
                
                /**
                 * A String containing error message.
                 * 
                 * @return the errorMessage
                 */
                public String getMessage() {
                    return message;
                }
                
                /**
                 * A String containing error message.
                 * 
                 * @param errorMessage
                 *            the errorMessage to set
                 */
                public void setMessage(String errorMessage) {
                    this.message = errorMessage;
                }
                

                }

                希望这会有所帮助。

                【讨论】:

                  猜你喜欢
                  • 2022-06-14
                  • 1970-01-01
                  • 2020-05-16
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-03-13
                  • 1970-01-01
                  • 2020-05-23
                  • 2022-01-08
                  相关资源
                  最近更新 更多