jackson Sping Boot 控制器处理无效JSON

hof1towb  于 7个月前  发布在  其他
关注(0)|答案(2)|浏览(85)

我有一个使用JSON的Sping 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需要no-args构造函数来进行反序列化。
当在调试器中检查时,Ship实体有一个所有者集,但没有名称集,所以它不是整个实体都为空-只有一个字段。
我尝试了我的Ship实体,没有默认的no args构造函数,所以你甚至不能将带有null字段的实体传递给服务,但是即使是有效的JSON也会失败。
如何以及在哪里处理无效JSON的异常?

wn9m85ua

wn9m85ua1#

需要添加一些注解和检查来验证请求体json。

1.在pom.xml中为以下注解添加依赖-

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

2. @对请求体有效

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

3. @ Nottune,带必选属性

public class Ship implements Serializable {

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

4. equals方法前增加null检查

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

输出的json无效:
输入

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

**输出:**您将得到一个400 Bad请求状态,长错误堆栈跟踪包括如下内容

"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"
    }
]

**注意:一些额外的代码,用于使用我们自定义的exceptionsoftware处理MethodArgumentNotValidException**异常,以便验证将在正确的格式响应中,而不是第二次错误stacktrace,并控制状态码,如下图所示:

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

json无效的错误输出:

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

yiytaume2#

与其将问题视为一个命名错误的字段,不如将其视为两个问题:缺少属性(name)和额外属性(name_wrong)。
Sping Boot 默认配置为忽略任何额外的属性。除非你告诉控制器name属性是必需的(例如,通过在name字段上使用@NotNullbean验证注解),它将很乐意接受此属性的空值。

缺少必填属性

由于您试图在nullname字段上调用equals,因此发生了NullPointerException

ship.getName().equals("Queen Mary")
^^^^^^^^^^^^^^

有几种不同的解决方法。首先,您可以将bean验证添加到必填字段:

public class Ship implements Serializable {
  @NotNull
  @JsonProperty("Name")
  private String name;

  @JsonProperty("Owner")
  private String owner;

  // ...
}

或者,您可以将equals方法更改为null-safe:

Boolean checkShip(Ship ship) {
    if (Objects.equals(ship.getName(), "Queen Mary"))) {
         // do something
    }
}

额外属性

有几种方法可以让Spring拒绝额外的name_wrong属性。
如果你希望应用程序总是拒绝未知属性(除非特定的bean选择退出),你可以全局配置它:

application.yml

spring.jackson.deserialization.fail-on-unknown-properties: false

相关问题