繁体   English   中英

在 Spring MVC 或 Spring-Boot 中返回不同类型的 ResponseEntity 的最佳方法是什么

[英]What is the best way to return different types of ResponseEntity in Spring MVC or Spring-Boot

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

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

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

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

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

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

您可以返回通用通配符<?>以在同一请求映射方法上返回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 答案是正确的方法

我不确定,但是,我认为您可以使用@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

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

@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);
    }

}

您也可以像这样实现以在相同的请求映射方法上返回成功和错误,使用对象类(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   
}

可以在不使用泛型的情况下返回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);
    }
}

这是我会做的一种方式:

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);

}

春天2使用这个你可以在同一时间返回字符串,不同的HTTP状态代码,DTO介绍ResponseStatusException。

@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));
}

我曾经使用过这样的类。 当存在与在消息中的错误信息组的错误的的StatusCode被设置。 数据在适当的时候存储在 Map 或 List 中。

/**
* 
*/
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;
}

}

希望这会有所帮助。

注意:如果您从 spring boot 1 升级到 spring boot 2,则会出现ResponseStatusException ,其中包含 Http 错误代码和描述。

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

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

使用自定义异常类,您可以返回不同的 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);
    }
}

对于特殊情况,我建议您在您的应用程序中采用RFC-7807 问题详细信息的 HTTP API标准。

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

在您的应用程序中采用 RFC-7087 后,只需在您的控制器方法中抛出 Exception,您将获得详细且标准的错误响应,例如:

   {
    "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",
   }

要跟进@MarkNorman的答案,我想说您必须定义来自您的service的异常与您的controller (HTTP 错误代码)之间的映射。

  • 成功响应映射到200状态(OK)
  • 使用400状态映射的验证异常 (BAD_REQUEST)
  • 未找到映射为404状态的记录 (NOT_FOUND)
  • 映射到500状态的其他异常 (INTERNAL_SERVER_ERROR)

例如,您的代码应如下所示:

@GetMapping("/persons/{id}")
public ResponseEntity<Success> findPersonById(@PathVariable("id") Long id) {
    try {
        var person = service.findById(id);
        var message = new Message(HttpStatus.OK, getCurrentDateTime(), person);
        return message;
    } catch(ServiceException exception) {
        throw new NotFoundException("An error occurs while finding a person", exception);
    }
}

所有抛出的异常都应该在ControllerAdvice中重定向

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<Error> handleNotFoundException(NotFoundException exception) {
    var error = new Error(HttpStatus.NOT_FOUND,
                getCurrentDateTime(),
                exception.getMessage());

    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

主要要了解的是,你的RestControllers只了解 HTTP 协议和 HTTP 响应代码。

您可以使用 @ExceptionHandler Controller Advice 返回自定义返回对象以防出错。 请参阅下面的示例代码,它在验证错误的情况下返回 400 和自定义响应。

@ControllerAdvice
public class RestExceptionHandler {
    @ExceptionHandler(value = InputValidationError.class)
    public ResponseEntity<ValidationErrorResponse> handleException(InputValidationError ex){
        return ResponseEntity.badRequest().body(ex.validationErrorResponse);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM