Spring MVC : GET @RequestParam과 같은 복잡한 객체


192

테이블의 객체를 나열하는 페이지가 있고 테이블을 필터링하기 위해 양식을 넣어야한다고 가정합니다. 필터는 다음과 같은 URL에 Ajax GET으로 전송됩니다. http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z

그리고 내 컨트롤러에 많은 매개 변수가있는 대신 :

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "prop1", required = false) String prop1,
    @RequestParam(value = "prop2", required = false) String prop2,
    @RequestParam(value = "prop3", required = false) String prop3) { ... }

그리고 내가 MyObject를 다음과 같이 가정합니다.

public class MyObject {
    private String prop1;
    private String prop2;
    private String prop3;

    //Getters and setters
    ...
}

나는 다음과 같은 것을하고 싶다 :

@RequestMapping(value = "/action")
public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    @RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }

가능합니까? 어떻게해야합니까?


1
MyObject가 모델이 될 '@RequestMapping'과 결합 된 '@ModelAttribute'를 사용해보십시오.
michal

@michal +1. : 여기 그렇게하는 방법을 보여주는 자습서의 부부입니다 봄 3 MVC : 스프링 3.0 MVC에서 양식을 처리 , 무엇이고 어떻게 사용@ModelAttribute , 스프링 MVC 양식 예 처리는 . Google ' Spring MVC 양식 처리 ' 만하면 수많은 자습서 / 예제를 얻을 수 있습니다. 그러나 폼 핸들링의 현대적인 방법을 사용하십시오, 즉 봄 V2.5 +
informatik01

답변:


249

@RequestParam주석을 제거하면 Spring이 요청 매개 변수를 클래스 인스턴스에 깔끔하게 바인딩합니다.

public @ResponseBody List<MyObject> myAction(
    @RequestParam(value = "page", required = false) int page,
    MyObject myObject)

8
비주, 이것을 부르는 방법의 예를 들어 줄 수 있습니까? Rest API에 대한 기본 http GET 호출을 작성 중이며 멋진 형태는 없습니다.
bschandramohan

33
Spring은 기본적으로 MyObject가 자동으로 바인딩하기 위해 getters / setter를 필요로합니다. 그렇지 않으면 myObject를 바인딩하지 않습니다.
aka_sh 2014

20
MyObject에서 필드를 선택 / 선택 사항으로 설정하는 방법은 무엇입니까? 이에 대한 적절한 문서를 찾는 방법을 잘 모르겠습니다.
worldsayshi

6
@ Biju, MyObject@RequestParam과 비슷한 방법으로 기본값을 제어하고 필요한 경우가 있습니까?
Alexey

7
@BijuKunjummen MyObjectSnake 케이스에서 쿼리 매개 변수를 수락하고의 낙타 케이스 멤버에 매핑하도록 업데이트하려면 어떻게해야합니까 MyObject? 예를 들어, 는 ?의 멤버 ?book_id=4와 매핑되어야 bookId합니다 MyObject.
Vivek Vardhan

55

나는 저에게서 짧은 예를 추가 할 것입니다.

DTO 수업 :

public class SearchDTO {
    private Long id[];

    public Long[] getId() {
        return id;
    }

    public void setId(Long[] id) {
        this.id = id;
    }
    // reflection toString from apache commons
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

컨트롤러 클래스 내부의 요청 매핑 :

@RequestMapping(value="/handle", method=RequestMethod.GET)
@ResponseBody
public String handleRequest(SearchDTO search) {
    LOG.info("criteria: {}", search);
    return "OK";
}

질문:

http://localhost:8080/app/handle?id=353,234

결과:

[http-apr-8080-exec-7] INFO  c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]

나는 그것이 도움이되기를 바랍니다 :)

업데이트 / 코 틀린

현재 Kotlin과 많은 일을하고 있기 때문에 누군가가 비슷한 DTO를 정의하려면 Kotlin의 클래스는 다음과 같은 형식이어야합니다.

class SearchDTO {
    var id: Array<Long>? = arrayOf()

    override fun toString(): String {
        // to string implementation
    }
}

data클래스와 함께 :

data class SearchDTO(var id: Array<Long> = arrayOf())

Spring (부팅에서 테스트 됨)은 응답에서 언급 된 요청에 대해 다음 오류를 반환합니다.

" 'java.lang.String []'유형의 값을 'java.lang.Long []'유형으로 변환하지 못했습니다. 중첩 된 예외는 java.lang.NumberFormatException입니다. 입력 문자열 : \"353,234 \ ""

데이터 클래스는 다음 요청 매개 변수 양식에 대해서만 작동합니다.

http://localhost:8080/handle?id=353&id=234

이것을 명심하십시오!


1
"필수"를 dto 필드로 설정할 수 있습니까?
Normal

4
Spring MVC 유효성 검사기를 사용해보십시오. 예 : codejava.net/frameworks/spring/…
Przemek Nowak

주석이 필요하지 않은 것이 매우 궁금합니다! 불필요하지만 이것에 대한 명확한 주석이 있습니까?
제임스 왓킨스

8

나는 비슷한 문제가 있습니다. 실제로 내가 생각했던대로 문제는 더 깊다. 기본으로 $.post사용 하는 jquery 를 사용 Content-Type:application/x-www-form-urlencoded; charset=UTF-8하고 있습니다. 불행히도 나는 내 시스템을 기반으로했으며 복잡한 객체가 필요할 때 @RequestParam그것을 달성 할 수 없었습니다.

제 경우에는 다음과 같이 사용자 환경 설정을 보내려고합니다.

 $.post("/updatePreferences",  
    {id: 'pr', preferences: p}, 
    function (response) {
 ...

클라이언트 측에서 서버로 전송 된 실제 원시 데이터는 다음과 같습니다.

...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...

파싱;

id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en

서버 측은;

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") UserPreferences preferences) {

    ...
        return someService.call(preferences);
    ...
}

내가 노력 @ModelAttribute, 모든 가능성에 추가 세터 / 게터, 생성자 UserPreferences가 5 개 매개 변수로하여 전송 데이터를 인식하지만 실제로 매핑 방법은이 매개 변수가로하지만 기회를. 나는 Biju의 솔루션을 시도했지만 스프링은 기본 생성자로 UserPreferences 객체를 만들고 데이터를 채우지 않습니다.

클라이언트 측에서 환경 설정의 JSon 문자열을 보내 서버 측의 문자열 인 것처럼 처리하여 문제를 해결했습니다.

고객:

 $.post("/updatePreferences",  
    {id: 'pr', preferences: JSON.stringify(p)}, 
    function (response) {
 ...

섬기는 사람:

@RequestMapping(value = "/updatePreferences")
public
@ResponseBody
Object updatePreferences(@RequestParam("id") String id, @RequestParam("preferences") String preferencesJSon) {


        String ret = null;
        ObjectMapper mapper = new ObjectMapper();
        try {
            UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
            return someService.call(userPreferences);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

간단히 말해 REST 메서드 내에서 수동으로 변환을 수행했습니다. 내 의견으로는 스프링이 전송 된 데이터를 인식하지 못하는 이유는 내용 유형입니다.


1
방금 같은 문제가 발생하여 같은 방식으로 해결했습니다. 그건 그렇고, 오늘 더 나은 해결책을 찾았습니까?
Shay Elkayam

1
나는 정확히 같은 문제가 있습니다. 나는 다음을 사용하여 더 @RequestMapping(method = POST, path = "/settings/{projectId}") public void settings(@PathVariable String projectId, @RequestBody ProjectSettings settings)
나은

4

필수 항목을 설정하는 방법에 대한 질문이 각 게시물 아래에 표시되므로 필 요에 따라 필드를 설정하는 방법에 대한 작은 예를 작성했습니다.

public class ExampleDTO {
    @NotNull
    private String mandatoryParam;

    private String optionalParam;

    @DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
    @NotNull
    private LocalDate testDate;

    public String getMandatoryParam() {
        return mandatoryParam;
    }
    public void setMandatoryParam(String mandatoryParam) {
        this.mandatoryParam = mandatoryParam;
    }
    public String getOptionalParam() {
        return optionalParam;
    }
    public void setOptionalParam(String optionalParam) {
        this.optionalParam = optionalParam;
    }
    public LocalDate getTestDate() {
        return testDate;
    }
    public void setTestDate(LocalDate testDate) {
        this.testDate = testDate;
    }
}

@RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (@Valid ExampleDTO e){
    System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
    return "Does this work?";
}

0

참조 답변을하는 동안 @ModelAttribute, @RequestParam, @PathParam및 좋아하는이 유효, 나는 우연히 작은 잡았다가있다. 결과 메소드 매개 변수는 Spring이 DTO를 감싸는 프록시입니다. 따라서 고유 한 사용자 지정 유형이 필요한 컨텍스트에서 사용하려고하면 예기치 않은 결과가 발생할 수 있습니다.

다음은 작동하지 않습니다.

@GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(@ModelAttribute CustomDto dto) {
    return ResponseEntity.ok(dto);
}

필자의 경우 Jackson 바인딩에서 사용하려고하면 com.fasterxml.jackson.databind.exc.InvalidDefinitionException.

dto에서 새 객체를 만들어야합니다.



0

허용 된 답변은 매력처럼 작동하지만 객체에 객체 목록이 있으면 예상대로 작동하지 않으므로 파기 후 내 해결책이 있습니다.

이 스레드 조언에 따라 다음 은 내가 한 일입니다.

  • 프론트 엔드 : 제출을 위해 base64로 인코딩하는 것보다 객체를 문자열 화합니다.
  • 백엔드 : base64 문자열을 디코딩 한 다음 문자열 json을 원하는 객체로 변환하십시오.

postman으로 API를 디버깅하는 데 가장 좋지는 않지만 예상대로 작동합니다.

원본 객체 : {페이지 : 1, 크기 : 5, 필터 : [{필드 : "id", 값 : 1, 비교 : "EQ"}

인코딩 된 오브젝트 : eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19

@GetMapping
fun list(@RequestParam search: String?): ResponseEntity<ListDTO> {
    val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
    ...
}

private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
    if (search.isNullOrEmpty()) return SearchFilterDTO()
    return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}

그리고 여기 SearchFilterDTO와 FilterDTO

class SearchFilterDTO(
    var currentPage: Int = 1,
    var pageSize: Int = 10,
    var sort: Sort? = null,
    var column: String? = null,
    var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
    var paged: Boolean = true
)

class FilterDTO(
    var field: String,
    var value: Any,
    var comparison: Comparison
)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.