Spring REST API 为 pojo 注册日期转换器

Spring REST API register date converter for pojo

Spring rest 默认从路径变量和 url 参数提供构建 pojo 功能。

就我而言,我有 pojo:

public class MyCriteria {
  private String from;
  private String till;
  private Long communityNumber;
  private String communityName;
}

在我的控制器中使用。 Url 是 http://localhost:8080/community/{communityNumber}/app

的请求结果
curl "http://localhost:8080/community/1/app?from=2018-11-14&till=2019-05-13&communityName=myCOm"

是:

{
  'from':'2018-11-14';
  'till':'2019-05-12';
  'communityNumber':'1';
  'communityName':'myCOm'
}

看起来效果不错。按目的使用所需类型的 pojo 数据要好得多。所以我想要 LocalDate 类型的 fromtill 字段。使用 spring 我希望这个解决方案几乎 开箱即用 。但是由于生命周期的原因,任何 spring 或 jackson 日期转换器都无法解决我的问题。

Spring 在注入日期之前验证 pojo 字段的类型,我得到类型不匹配异常。我认为一般原因是 spring 使用了特殊的构建器,它试图按名称查找所需的参数,它忽略了要在 pojo 内部为字段应用的注释。

问题是:

对于spring构建pojo是否有任何优雅的解决方案,默认情况下某些字段将从String格式转换为LocalDate格式?

P.S.

强制条件是:

public class MyCriteria {
  private LocalDate from;
  private LocalDate till;
  private Long communityNumber;
  private String communityName;
}

P.S.2.

import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import org.vl.example.rest.dtofromoaramsandpath.web.dto.MyCriteria;

import java.time.LocalDate;

@RestController
@RequestMapping("verify/criteria/mapping")
@Slf4j
public class MyController {

    @GetMapping("community/{communityNumber}/dto")
    public MyCriteria loadDataByDto(MyCriteria criteria) {
        log.info("received criteria: {}", criteria);
        return criteria;
    }

    @GetMapping("community/{communityNumber}/default/params")
    public String loadDataByDefaultParameters(@PathVariable("communityNumber") String communityNumber,
                                            @RequestParam(value = "from", required = false) String from,
                                            @RequestParam(value = "till", required = false) String till,
                                            @RequestParam(value = "communityName", required = false) String communityName) {
        log.info("received data without converting:\n\tcommunityNumber => {}\n\tfrom => {}\n\ttill => {}\n\tcommunityName => {}",
                communityNumber, from, till, communityName);
        return new StringBuilder("{")
                .append("\n\tfrom:").append(from).append(";")
                .append("\n\tfrom:").append(from).append(";")
                .append("\n\ttill:").append(till).append(";")
                .append("\n\tcommunityName:").append(communityName)
                .append("\n}\n").toString();
    }

    @GetMapping("community/{communityNumber}/converted/params")
    public String loadUsingConvertedParameters(@PathVariable("communityNumber") String communityNumber,
                                             @RequestParam(value = "from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from,
                                             @RequestParam("till") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate till,
                                             @RequestParam(value = "communityName", required = false) String communityName) {
        log.info("received data with LocalDate converting:\n\tcommunityNumber => {}\n\tfrom => {}\n\ttill => {}\n\tcommunityName => {}",
                communityNumber, from, till, communityName);
        return new StringBuilder("{")
                .append("\n\tfrom:").append(from).append(";")
                .append("\n\tfrom:").append(from).append(";")
                .append("\n\ttill:").append(till).append(";")
                .append("\n\tcommunityName:").append(communityName)
                .append("\n}\n").toString();
    }
}

这可能对您有所帮助。我有类似的情况,我使用这种方法将数据转换为特定需求。

public class MyCriteria {
  public MyCriteria(LocalDate from, LocalDate till, Long communityNumber, String communityName){
   // assignement of variables
}
  private LocalDate from;
  private LocalDate till;
  private Long communityNumber;
  private String communityName;
}

所以每当您从 JSON 创建对象时,它都会根据要求创建它。

当我实现这个的时候,我使用了 "Jackson" 的 ObjectMapper class 来做这件事。希望你也一样。

在 pojo class 中使用 java.sql.Date .like private Date from。我希望它适用于 JSON 转换。当您从 UI 接收日期 JSON 字段时,请始终使用 Java.sql.Date 进行 Jackson 日期转换。

为此,您需要添加 jsr310 依赖项。

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.10")

我希望这对你有用。

问题的答案是使用 init binder 在指定条件内注册类型映射。需要 PropertyEditorSupport 实现特定目的。

短代码示例为:

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE));
            }
        });
    }

完整的代码示例可以从 github:

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.vl.example.rest.dtofromoaramsandpath.web.dto.MyCriteriaLd;

import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@RestController
@RequestMapping("verify/criteria/mapping")
@Slf4j
public class MyControllerLd {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE));
            }
        });
    }

    @GetMapping("community/{communityNumber}/dtold")
    public MyCriteriaLd loadDataByDto(MyCriteriaLd criteria) {
        log.info("received criteria: {}", criteria);
        return criteria;
    }
}

所以这个案例的模型可以是下一个:

import lombok.Data;

import java.time.LocalDate;

@Data
public class MyCriteriaLd {
    private LocalDate from;
    private LocalDate till;
    private Long communityNumber;
    private String communityName;
}