Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Spring中使用LocalDateTime RequestParam?我得到;无法将字符串转换为LocalDateTime;_Spring_Spring Boot_Spring Mvc_Java Time_Jsr310 - Fatal编程技术网

如何在Spring中使用LocalDateTime RequestParam?我得到;无法将字符串转换为LocalDateTime;

如何在Spring中使用LocalDateTime RequestParam?我得到;无法将字符串转换为LocalDateTime;,spring,spring-boot,spring-mvc,java-time,jsr310,Spring,Spring Boot,Spring Mvc,Java Time,Jsr310,我使用Spring Boot,在Maven中包括了jackson-datatype-jsr310: <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.7.3</version> </depe

我使用Spring Boot,在Maven中包括了
jackson-datatype-jsr310

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.7.3</version>
</dependency>
我得到以下错误:

{
  "timestamp": 1477528408379,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
  "message": "Failed to convert value of type [java.lang.String] to required type [java.time.LocalDateTime]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime] for value '2016-10-8T00:00'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2016-10-8T00:00]",
  "path": "/test"
}

TL;DR-您可以使用
@RequestParam
将其捕获为字符串,或者您也可以让Spring通过参数上的
@DateTimeFormat
将字符串解析为java日期/时间类

@RequestParam
足以获取您在=符号后提供的日期,但是,它作为
字符串进入方法。这就是它抛出cast异常的原因

有几种方法可以实现这一点:

  • 自己解析日期,将值作为字符串抓取
  • @GetMapping(“/test”)
    公共页面获取(@RequestParam(value=“start”,required=false)字符串开始){
    //使用所需格式创建DateTimeFormatter:
    DateTimeFormatter dateTimeFormat=
    新的DateTimeFormatter(DateTimeFormatter.BASIC_ISO_DATE);
    //接下来从@RequestParam解析日期,将TO类型指定为
    临时查询:
    LocalDateTime日期=dateTimeFormat.parse(开始,LocalDateTime::from);
    //做你剩下的代码。。。
    }
    
  • 利用Spring自动解析和预期日期格式的能力:
  • @GetMapping(“/test”)
    public void processDateTime(@RequestParam(“start”)
    @DateTimeFormat(iso=DateTimeFormat.iso.DATE\u时间)
    LocalDateTime(日期){
    //剩下的代码(Spring已经解析了日期)。
    }
    
    我遇到了同样的问题并找到了解决方案(没有使用注释)

    …您必须至少向中的[LocalDateTime]转换器正确注册一个字符串 您的上下文,以便Spring可以使用它为 每次输入一个字符串并期望得到一个[LocalDateTime]时,您都会遇到这种情况。(一个大的 Spring已经实现并包含了许多转换器 在core.convert.support包中,但没有涉及[LocalDateTime] 转换)

    因此,在您的情况下,您可以这样做:

    public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
        public LocalDateTime convert(String source) {
            DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
            return LocalDateTime.parse(source, formatter);
        }
    }
    
    最后,您将在ConversionService中使用@Autowire:

    @Component
    public class SomeAmazingConversionService extends GenericConversionService {
    
        public SomeAmazingConversionService() {
            addConverter(new StringToLocalDateTimeConverter());
        }
    
    }
    
    @Autowired
    private SomeAmazingConversionService someAmazingConversionService;
    

    您可以在此阅读更多关于spring转换(和格式)的信息。事先警告一下,它有大量的广告,但我肯定发现它是一个有用的网站,是一个很好的主题介绍

    你做的一切都是正确的:)。这是一个能准确显示您正在做什么的示例。只需使用
    @DateTimeFormat
    注释RequestParam即可。控制器中不需要特殊的
    通用转换服务
    或手动转换。这篇博文写的就是这个

    @RestController
    @RequestMapping("/api/datetime/")
    final class DateTimeController {
    
        @RequestMapping(value = "datetime", method = RequestMethod.POST)
        public void processDateTime(@RequestParam("datetime") 
                                    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateAndTime) {
            //Do stuff
        }
    }
    

    我猜你对格式有问题。在我的设置中,一切都很好。

    就像我在评论中提到的那样,您也可以在签名方法中使用此解决方案:
    @RequestParam@DateTimeFormat(iso=DateTimeFormat.iso.DATE\u-TIME)LocalDateTime start
    我找到了解决方法

    Spring/Spring Boot仅支持BODY参数中的日期/时间格式

    以下配置类在查询字符串(请求参数)中添加了对日期/日期时间的支持:

    分别为:

    // Until Spring Framwork 4.+
    @Configuration
    public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
    
        @Override
        public void addFormatters(FormatterRegistry registry) {
            DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
            registrar.setUseIsoFormat(true);
            registrar.registerFormatters(registry);
        }
    }
    
    即使您将多个请求参数绑定到某个类(在本例中为
    @DateTimeFormat
    注释),它也可以工作:


    以下内容适用于Spring Boot 2.1.6:

    控制器

    @Slf4j
    @RestController
    public class RequestController {
    
        @GetMapping
        public String test(RequestParameter param) {
            log.info("Called services with parameter: " + param);
            LocalDateTime dateTime = param.getCreated().plus(10, ChronoUnit.YEARS);
            LocalDate date = param.getCreatedDate().plus(10, ChronoUnit.YEARS);
    
            String result = "DATE_TIME: " + dateTime + "<br /> DATE: " + date;
            return result;
        }
    
        @PostMapping
        public LocalDate post(@RequestBody PostBody body) {
            log.info("Posted body: " + body);
            return body.getDate().plus(10, ChronoUnit.YEARS);
        }
    }
    
    测试类:

    @Value
    public class RequestParameter {
        @DateTimeFormat(iso = DATE_TIME)
        LocalDateTime created;
    
        @DateTimeFormat(iso = DATE)
        LocalDate createdDate;
    }
    
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class PostBody {
        LocalDate date;
    }
    
    @RunWith(SpringRunner.class)
    @WebMvcTest(RequestController.class)
    public class RequestControllerTest {
    
        @Autowired MockMvc mvc;
        @Autowired ObjectMapper mapper;
    
        @Test
        public void testWsCall() throws Exception {
            String pDate        = "2019-05-01";
            String pDateTime    = pDate + "T23:10:01";
            String eDateTime = "2029-05-01T23:10:01"; 
    
            MvcResult result = mvc.perform(MockMvcRequestBuilders.get("")
                .param("created", pDateTime)
                .param("createdDate", pDate))
              .andExpect(status().isOk())
              .andReturn();
    
            String payload = result.getResponse().getContentAsString();
            assertThat(payload).contains(eDateTime);
        }
    
        @Test
        public void testMapper() throws Exception {
            String pDate        = "2019-05-01";
            String eDate        = "2029-05-01";
            String pDateTime    = pDate + "T23:10:01";
            String eDateTime    = eDate + "T23:10:01"; 
    
            MvcResult result = mvc.perform(MockMvcRequestBuilders.get("")
                .param("created", pDateTime)
                .param("createdDate", pDate)
            )
            .andExpect(status().isOk())
            .andReturn();
    
            String payload = result.getResponse().getContentAsString();
            assertThat(payload).contains(eDate).contains(eDateTime);
        }
    
    
        @Test
        public void testPost() throws Exception {
            LocalDate testDate = LocalDate.of(2015, Month.JANUARY, 1);
    
            PostBody body = PostBody.builder().date(testDate).build();
            String request = mapper.writeValueAsString(body);
    
            MvcResult result = mvc.perform(MockMvcRequestBuilders.post("")
                .content(request).contentType(APPLICATION_JSON_VALUE)
            )
            .andExpect(status().isOk())
            .andReturn();
    
            ObjectReader reader = mapper.reader().forType(LocalDate.class);
            LocalDate payload = reader.readValue(result.getResponse().getContentAsString());
            assertThat(payload).isEqualTo(testDate.plus(10, ChronoUnit.YEARS));
        }
    
    }
    

    上面的答案对我不起作用,但我在这里错了:获胜的片段是ControllerAdvice注释,它的优点是在所有控制器上应用此修复:

    @ControllerAdvice
    public class LocalDateTimeControllerAdvice
    {
    
        @InitBinder
        public void initBinder( WebDataBinder binder )
        {
            binder.registerCustomEditor( LocalDateTime.class, new PropertyEditorSupport()
            {
                @Override
                public void setAsText( String text ) throws IllegalArgumentException
                {
                    LocalDateTime.parse( text, DateTimeFormatter.ISO_DATE_TIME );
                }
            } );
        }
    }
    

    您可以添加到配置中,此解决方案可以使用可选参数和非可选参数

    @Bean
    公共格式化程序localDateFormatter(){
    返回新格式化程序(){
    @凌驾
    公共LocalDate解析(字符串文本、区域设置){
    返回LocalDate.parse(text,DateTimeFormatter.ISO_DATE);
    }
    @凌驾
    公共字符串打印(LocalDate对象、Locale){
    返回日期timeformatter.ISO_DATE.format(对象);
    }
    };
    }
    @豆子
    公共格式化程序localDateTimeFormatter(){
    返回新格式化程序(){
    @凌驾
    公共LocalDateTime解析(字符串文本、区域设置){
    返回LocalDateTime.parse(text,DateTimeFormatter.ISO_DATE_TIME);
    }
    @凌驾
    公共字符串打印(LocalDateTime对象、区域设置){
    返回日期timeformatter.ISO_DATE_TIME.format(对象);
    }
    };
    }
    
    对于全局配置:

    public class LocalDateTimePropertyEditor extends PropertyEditorSupport {
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            setValue(LocalDateTime.parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        }
    
    }
    
    然后

    @ControllerAdvice
    public class InitBinderHandler {
    
        @InitBinder
        public void initBinder(WebDataBinder binder) { 
            binder.registerCustomEditor(OffsetDateTime.class, new OffsetDateTimePropertyEditor());
        }
    
    }
    
    SpringBoot 2.X.X更新 如果使用依赖版本
    2.0.0.RELEASE
    或更高版本,则不再需要明确包含
    jackson-datatype-jsr310
    依赖项,该依赖项已通过
    spring boot starter web
    提供

    这已解决为弹簧启动问题,仍然有效且相关:

    @RequestMapping(value = "datetime", method = RequestMethod.POST)
    public void foo(@RequestParam("dateTime") 
                    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime ldt) {
        // IMPLEMENTATION
    }
    

    下面是另一个使用参数转换器的通用解决方案:

    import org.springframework.core.convert.converter.Converter;
    import org.springframework.stereotype.Component;
    import ru.diasoft.micro.msamiddleoffice.ftcaa.customerprofile.config.JacksonConfig;
    
    import java.time.DateTimeException;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeParseException;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Component
    public class LocalDateTimeConverter implements Converter<String, LocalDateTime>{
    
        private static final List<String> SUPPORTED_FORMATS = Arrays.asList("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "[another date time format ...]");
        private static final List<DateTimeFormatter> DATE_TIME_FORMATTERS = SUPPORTED_FORMATS
                .stream()
                .map(DateTimeFormatter::ofPattern)
                .collect(Collectors.toList());
    
        @Override
        public LocalDateTime convert(String s) {
    
            for (DateTimeFormatter dateTimeFormatter : DATE_TIME_FORMATTERS) {
                try {
                    return LocalDateTime.parse(s, dateTimeFormatter);
                } catch (DateTimeParseException ex) {
                    // deliberate empty block so that all parsers run
                }
            }
    
            throw new DateTimeException(String.format("unable to parse (%s) supported formats are %s",
                    s, String.join(", ", SUPPORTED_FORMATS)));
        }
    }
    
    import org.springframework.core.convert.converter.converter;
    导入org.springframework.stereotype.Component;
    导入ru.diasoft.micro.msamidleoffice.ftcaa.customerprofile.config.JacksonConfig;
    导入java.time.DateTimeException;
    导入java.time.LocalDateTime;
    导入java.time.format.DateTimeFormatter;
    导入java.time.format.DateTimeParseException;
    导入java.util.array;
    导入java.util.List;
    导入java.util.stream.collector;
    @组成部分
    公共类LocalDateTimeConverter实现转换器{
    支持的私有静态最终列表_格式=Arrays.asList(“yyyy-MM-dd'T'HH:MM:ss.SSS'Z'”,[另一个日期时间格式…]);
    私有静态最终列表日期\时间\格式化程序=支持的\格式
    
    @ControllerAdvice
    public class LocalDateTimeControllerAdvice
    {
    
        @InitBinder
        public void initBinder( WebDataBinder binder )
        {
            binder.registerCustomEditor( LocalDateTime.class, new PropertyEditorSupport()
            {
                @Override
                public void setAsText( String text ) throws IllegalArgumentException
                {
                    LocalDateTime.parse( text, DateTimeFormatter.ISO_DATE_TIME );
                }
            } );
        }
    }
    
    public class LocalDateTimePropertyEditor extends PropertyEditorSupport {
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            setValue(LocalDateTime.parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        }
    
    }
    
    @ControllerAdvice
    public class InitBinderHandler {
    
        @InitBinder
        public void initBinder(WebDataBinder binder) { 
            binder.registerCustomEditor(OffsetDateTime.class, new OffsetDateTimePropertyEditor());
        }
    
    }
    
    @RequestMapping(value = "datetime", method = RequestMethod.POST)
    public void foo(@RequestParam("dateTime") 
                    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime ldt) {
        // IMPLEMENTATION
    }
    
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.stereotype.Component;
    import ru.diasoft.micro.msamiddleoffice.ftcaa.customerprofile.config.JacksonConfig;
    
    import java.time.DateTimeException;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeParseException;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Component
    public class LocalDateTimeConverter implements Converter<String, LocalDateTime>{
    
        private static final List<String> SUPPORTED_FORMATS = Arrays.asList("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "[another date time format ...]");
        private static final List<DateTimeFormatter> DATE_TIME_FORMATTERS = SUPPORTED_FORMATS
                .stream()
                .map(DateTimeFormatter::ofPattern)
                .collect(Collectors.toList());
    
        @Override
        public LocalDateTime convert(String s) {
    
            for (DateTimeFormatter dateTimeFormatter : DATE_TIME_FORMATTERS) {
                try {
                    return LocalDateTime.parse(s, dateTimeFormatter);
                } catch (DateTimeParseException ex) {
                    // deliberate empty block so that all parsers run
                }
            }
    
            throw new DateTimeException(String.format("unable to parse (%s) supported formats are %s",
                    s, String.join(", ", SUPPORTED_FORMATS)));
        }
    }