我有一个使用JSON的Spring Boot控制器:

    @PostMapping(
    value = "/shipdetails")
    public ResponseEntity acceptShip(@RequestBody Ship ship, HttpServletRequest request) {
        shipService.checkShip(ship);
        return new ResponseEntity<>(HttpStatus.OK);
    }

存在对应的发货实体:

public class Ship implements Serializable {

@JsonProperty("Name")
private String name;
@JsonProperty("Owner")
private String owner;

public Ship(String name, String owner) {
    this.name = name;
    this.owner = owner;    }

public Ship() {
}

// Getters and Setters removed for brevity

最后是这项服务:

@Service
public class ShipService {

Boolean checkShip(Ship ship) {
    if (ship.getName().equals("Queen Mary")) {
         // do something
    }
//edited for brevity

无效JSON示例:

{
    "name_wrong":"test name",
    "owner":"Lloyds Shipping"
}

目前,如果我发送无效的JSON,我在堆栈跟踪上得到的错误是(服务层):Cannot invoke "String.equals(Object)" because the return value of "com.ships.Ship.getName()" is null.

Jackson需要无参数的构造函数来进行反序列化.

在调试器中判断时,Ship实体有一个所有者集,但没有一个名称集,所以它不是整个实体是空的-只有一个字段.

我在没有缺省的无args构造函数的情况下try 了SHIP实体,因此您甚至不能将带有空字段的实体传递给服务,但是它失败了,甚至带有有效的JSON.

应该如何以及在哪里处理无效JSON的异常?

推荐答案

为了验证请求正文json,需要添加少量注释和判断.

1. Add dependency in pom.xml for the below annotations-

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>3.1.1</version>
</dependency>

2. @Valid with Request Body

    @PostMapping(value = "/shipdetails")
    public ResponseEntity acceptShip(@Valid @RequestBody Ship ship, 
                                     HttpServletRequest request)

3. @NotNull with the mandatory attributes

    public class Ship implements Serializable {

      @NotNull(message = "The name is mandatory")
      @JsonProperty("Name")
      private String name;
      @JsonProperty("Owner")
      private String owner;

4. Additional null check before equals method

Boolean checkShip(Ship ship) {
        if (StringUtils.isNotBlank(ship.getName()) 
            && ship.getName().equals("Queen Mary")) {
            // do something
        }

Output with Invalid json:

Input

    {
     "name_wrong":"test name",
     "owner":"Lloyds Shipping"
    }

Output:个 您将获得一个400 Bad Request状态,并且长错误堆栈跟踪包含如下内容

    "message": "Validation failed for object='ship'. Error count: 1",
"errors": [
    {
        "codes": [
            "NotNull.ship.name",
            "NotNull.name",
            "NotNull.java.lang.String",
            "NotNull"
        ],
        "arguments": [
            {
                "codes": [
                    "ship.name",
                    "name"
                ],
                "arguments": null,
                "defaultMessage": "name",
                "code": "name"
            }
        ],
        "defaultMessage": "The name is mandatory",
        "objectName": "ship",
        "field": "name",
        "rejectedValue": null,
        "bindingFailure": false,
        "code": "NotNull"
    }
]

Note: 使用我们的定制异常处理程序处理100异常的一些附加代码,以便验证将在正确格式化的响应中进行,而不是长时间的错误堆栈跟踪和控制状态代码,如下所示:

@ControllerAdvice
public class ValidationExceptionHandler {
    /**
     * For an invalid input, spring framework will throw an 
            MethodArgumentNotValidException exception
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> notValidInput(MethodArgumentNotValidException e) {
        Map<String,String> errorMap = e.getAllErrors()
                .stream()
                .collect(Collectors.toMap(x -> ((FieldError)x).getField(), 
                 b -> b.getDefaultMessage(),(p,q) -> p, LinkedHashMap::new));
        return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST);
    }
}

Formatted output for invalid json:

{
    "name": "The name is mandatory"
}

Java相关问答推荐

长音符

Java 21虚拟线程会解决转向react 式单线程框架的主要原因吗?

根据对象和值的参数将映射<;T、值&>转换为列表<;T&>

为什么我的ArrayList索引的索引总是返回-1?

存根基类的受保护方法

当我已经安装了其他版本的Java时,如何在Mac OSX 14.3.1上安装Java 6?

获取字符串中带空格的数字和Java中的字符

Chunk(Int)已弃用并标记为要删除

测试容器无法加载类路径初始化脚本

在Eclipse中数组的可空性

Java17支持哪个MapR版本?

Groovy/Java:匹配带引号的命令选项

如何读取3个CSV文件并在控制台中按顺序显示?(Java)

基于配置switch 的@Controller的条件摄取

为什么在下面的Java泛型方法中没有类型限制?

在Java泛型中使用通配符时,如何推断类型

IntelliJ IDEA中的JavaFX应用程序无法在资源中找到CSS文件

通过/失败的参数化junit测试方法执行数

为什么没有加载java.se模块?

在单例类上获取Java锁,了解原因