programing

봄철 LocalDateTime RequestParam 사용방법"String을 LocalDateTime으로 변환하지 못했습니다"라고 표시됩니다.

madecode 2023. 3. 4. 15:21
반응형

봄철 LocalDateTime RequestParam 사용방법"String을 LocalDateTime으로 변환하지 못했습니다"라고 표시됩니다.

Spring Boot도 포함되어 .jackson-datatype-jsr310Maven 함 ma :

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.7.3</version>
</dependency>

Java 8 Date/Time 타입의 Request Param을 사용하려고 하면

@GetMapping("/test")
public Page<User> get(
    @RequestParam(value = "start", required = false)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start) {
//...
}

다음 URL을 사용하여 테스트합니다.

/test?start=2016-10-8T00:00

다음의 에러가 표시됩니다.

{
  "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파라미터에도 적용됩니다.

@RequestParam 뒤에 = 기호 뒤에 는 = 기호로 됩니다.String그것이 바로 출연자 예외를 던지는 이유입니다.

여기에는 몇 가지 방법이 있습니다.

  1. 직접 날짜를 구문 분석하여 값을 문자열로 가져옵니다.
@GetMapping("/test")
public Page<User> get(@RequestParam(value="start", required = false) String start){

    //Create a DateTimeFormatter with your required format:
    DateTimeFormatter dateTimeFormat = 
            new DateTimeFormatter(DateTimeFormatter.BASIC_ISO_DATE);

    //Next parse the date from the @RequestParam, specifying the TO type as 
a TemporalQuery:
   LocalDateTime date = dateTimeFormat.parse(start, LocalDateTime::from);

    //Do the rest of your code...
}
  1. Spring의 기능을 활용하여 날짜 형식을 자동으로 해석하고 예상합니다.
@GetMapping("/test")
public void processDateTime(@RequestParam("start") 
                            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) 
                            LocalDateTime date) {
        // The rest of your code (Spring already parsed the date).
}

모든 작업을 올바르게 수행했습니다.:). 다음은 정확히 무엇을 하고 있는지를 보여주는 예입니다.Request Param에 주석을 달기만 하면 됩니다.@DateTimeFormat 할 는 없습니다.특별히 할 필요는 없다.GenericConversionService또는 컨트롤러에서 수동으로 변환합니다. 블로그 투고는 그것에 대해 쓰고 있다.

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

포맷에 문제가 있었나 봐요.내 설정에서는 모든 것이 잘 작동한다.

여기서 해결 방법을 찾았어요.

스프링/스프링 부트는 BODY 파라미터의 날짜/날짜 형식만 지원합니다.

다음 컨피규레이션클래스는 QUERY STRING(요구 파라미터)에 date/date-time 지원을 추가합니다.

// Since Spring Framwork 5.0 & Java 8+
@Configuration
public class DateTimeFormatConfiguration implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setUseIsoFormat(true);
        registrar.registerFormatters(registry);
    }
}

다음 각 항목을 선택합니다.

// 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이 경우 주석을 사용할 수 없습니다.

public class ReportRequest {
    private LocalDate from;
    private LocalDate to;

    public LocalDate getFrom() {
        return from;
    }

    public void setFrom(LocalDate from) {
        this.from = from;
    }

    public LocalDate getTo() {
        return to;
    }

    public void setTo(LocalDate to) {
        this.to = to;
    }
}

// ...

@GetMapping("/api/report")
public void getReport(ReportRequest request) {
// ...

, 이 할 수 있습니다.@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start

SpringBoot 2.X.X 이후

종속성 버전을 사용하는 경우2.0.0.RELEASE이상은 더 할 필요가 .jackson-datatype-jsr310.spring-boot-starter-web~를 참조해 주세요.

이는 Spring Boot 문제 #9297로 해결되었으며 답변은 여전히 유효하며 관련이 있습니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
@RequestMapping(value = "datetime", method = RequestMethod.POST)
public void foo(
        @RequestParam("dateTime") 
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime ldt) {

    // IMPLEMENTATION
}

같은 문제에 부딪혀, 여기서 해결책을 찾았습니다(주석 사용 안 함).

...적어도 컨텍스트에서 문자열을 [LocalDateTime]컨버터에 적절하게 등록해야 합니다.그러면 Spring은 String을 입력으로 제공하고 [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);
    }
}

빈을 등록하면 됩니다.

<bean class="com.mycompany.mypackage.StringToLocalDateTimeConverter"/>

주석 포함

Conversion Service에 추가합니다.

@Component
public class SomeAmazingConversionService extends GenericConversionService {

    public SomeAmazingConversionService() {
        addConverter(new StringToLocalDateTimeConverter());
    }

}

마지막으로 Conversion Service에서 @Autowire:

@Autowired
private SomeAmazingConversionService someAmazingConversionService;

스프링(및 포맷)을 사용한 변환에 대한 자세한 내용은 이 사이트를 참조하십시오.광고도 많이 게재되어 있습니다만, 확실히 도움이 되는 사이트이며, 토픽의 소개가 잘 되어 있는 것을 알 수 있었습니다.

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

DTO 클래스:

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

}

위의 답변은 도움이 되지 않았지만, 다음 중 하나를 우연히 찾았습니다.https://blog.codecentric.de/en/2017/08/parsing-of-localdate-query-parameters-in-spring-boot/에서 승리한 스니펫은 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
    public Formatter<LocalDate> localDateFormatter() {
        return new Formatter<>() {
            @Override
            public LocalDate parse(String text, Locale locale) {
                return LocalDate.parse(text, DateTimeFormatter.ISO_DATE);
            }

            @Override
            public String print(LocalDate object, Locale locale) {
                return DateTimeFormatter.ISO_DATE.format(object);
            }
        };
    }


    @Bean
    public Formatter<LocalDateTime> localDateTimeFormatter() {
        return new Formatter<>() {
            @Override
            public LocalDateTime parse(String text, Locale locale) {
                return LocalDateTime.parse(text, DateTimeFormatter.ISO_DATE_TIME);
            }

            @Override
            public String print(LocalDateTime object, Locale locale) {
                return DateTimeFormatter.ISO_DATE_TIME.format(object);
            }
        };
    }

파라미터 컨버터를 사용하는 다른 일반적인 솔루션은 다음과 같습니다.

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

은 datetime으로 할 수 .application properties를 들어 를를: :

spring.mvc.format.date=yyy-MM-dd

spring.mvc.format.date-time=yy-MM-dd HH:mm:ss

spring.mvc.format 입니다.시간=HH:mm:ss

mavern: org.springframework를 체크합니다.부트: spring-boot-autoconfigure: 2.5.3

글로벌 설정의 경우:

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

}

이와 관련하여 비슷한 문제가 있었습니다.

WebRequestDataBinder를 사용하여 요청 파라미터를 모델에 동적으로 매핑하고 있습니다.

Object domainObject = ModelManager.getEntity(entityName).newInstance();
WebRequestDataBinder binder = new WebRequestDataBinder(domainObject);
binder.bind(request);

이 코드 조각은 기본 요소에는 작동하지만 LocalDateTime 유형 특성에는 작동하지 않습니다.

문제를 해결하기 위해 binder.bind를 호출하기 전에 bind()를 호출하기 전에 커스텀에디터를 등록했습니다

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

                    @Override
                    public String getAsText() {
                        return DateTimeFormatter.ISO_DATE_TIME.format((LocalDateTime) getValue());
                    }

                }
            );

이것으로 문제가 해결되었다.

동작은 스웨거 오픈과 함께 작업하는 동안 발생했습니다.API 끝점...

오픈API 정의는 비슷해 보였습니다.

paths:
  /endpoint:
    get:
      summary: short summary
      operationId: endpointFunction
      parameters:
        - name: timestamp
          in: query
          description: 'Given timestamp'
          required: false
          schema:
            type: string
            format: date-time
            example: "2023-01-05T13:11:40.020747+01:00"
      responses:
        200:
          description: 'Ok'
        404:
          description: 'Not Ok'
          content: {}
        500:
          description: 'Failure'
          content: { }

Maven을 사용하여 정의를 컴파일하면 다음과 같습니다.

public interface EndpointApiDelegate {

    default Optional<NativeWebRequest> getRequest() {
        return Optional.empty();
    }

    /**
     * GET /endpoint : short summary
     *
     * @param timestamp Given timestamp (optional)
     * @return Ok (status code 200)
     *         or Not ok (status code 404)
     *         or Interner Fehler (status code 500)
     * @see EndpointApi#endpointFunction
     */
    default ResponseEntity<Void> endpointFunction(OffsetDateTime timestamp) {
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }

}

이제 직접 데이터를 전송하려고 합니다.

  • 브라우저의 URL
  • http 파일(.http)
  • 집배원 등
### Sample FAILING call in .http-file
GET http://localhost:{{port}}/endpoint?timestamp=22023-01-05T13:11:40.020747+01:00

지정된 예(2023-01-05T13:11:40.020747+01:00)를 사용하면 실패합니다.

"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 ... .. .

추리

쿼리 매개 변수는 다른 문자 집합으로 구문 분석됩니다(올바른 단어인지 확실하지 않지만 동작에 적합합니다).따라서 '+' 문자를 읽을 수 없습니다.

결의안

### Sample WORKING call in .http-file
GET http://localhost:{{port}}/endpoint?timestamp=2023-01-03T11%3A29%3A47.526612%2B01%3A00

주의

만약 그 해결책이 비현실적으로 느껴진다면...

.....그렇다면: 당신이 옳아요!!!

날짜 시간 전달(이 경우)은 잘못된 엔드포인트 설계 결정입니다.

Swagger OpenApi는 OpenApi 정의에서 int(최소 및 최대)와 문자열(regex-pattern)을 직접 검사합니다.

여전히 날짜 시간은 최소/최대 값이 상대적인 '매일 경과 시간'에 따라 달라질 수 있으므로 특별한 함정을 제공합니다.따라서 자동화된 최소/최대 값은 아직 구현되지 않은 것으로 예상됩니다.

더 좋은 해결책이 있을까요?네! :)

대신 POST 요청을 사용하여 URL-RequestParameter 대신 RequestBody에서 APPLICATION JSON을 정의합니다.

components:
  schemas:
    TimestampEntity:
      type: object
      properties:
        timestamp:
          description: 'timestamp'
          type: string
          format: date-time
          example: "2023-01-05T13:11:40.020747+01:00"
paths:
  /endpoint:
    post:
      summary: short summary
      operationId: endpointFunction
      # parameters:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimestampEntity'
      responses:
        200:
          description: 'Ok'
        404:
          description: 'Not Ok'
          content: {}
        500:
          description: 'Failure'
          content: { }

그 취지는 다음과 같습니다.

    default Optional<NativeWebRequest> getRequest() {
        return Optional.empty();
    }

    /**
     * POST /endpoint : short summary
     *
     * @param timestampEntity
 (optional)
     * @return Ok (status code 200)
     *         or Not Ok (status code 404)
     *         or Failure (status code 500)
     * @see EndpointApi#endpointFunction
     */
    default ResponseEntity<Void> endpointFunction(TimestampEntity timestampEntity) {
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }

생성된 Timestamp.class 사용:


public class TimestampEntity   {
  @JsonProperty("timestamp")
  private OffsetDateTime timestamp;

  public TimestampEntity timestamp(OffsetDateTime timestamp) {
    this.timestamp = timestamp;
    return this;
  }

... .. .

이제 값(2023-01-05T13:11:40.020747+01:00)이 적절하게 해석됩니다.

이것으로 URL charset-scuffle에 얽히는 것은 끝입니다;-)

저 같은 경우에는

'@설정

//@EnableWebMvc - 이 코멘트를 해제하면 LocalDateTime은 배열이 됩니다.

퍼블릭 클래스 WebConfig가 WebMvcConfigr {...}을(를) 구현합니다.}`

언급URL : https://stackoverflow.com/questions/40274353/how-to-use-localdatetime-requestparam-in-spring-i-get-failed-to-convert-string

반응형