0

There is an incoming post request like this:

curl --location --request POST 'http://example.com/some/path' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'prop1=prop1Value' \
--data-urlencode 'prop2[prop2Inner1]=prop2Inner1Value' \
--data-urlencode 'prop2[prop2Inner2][inner3]=inner3Value' \
--data-urlencode 'prop2[prop2Inner2][inner4]=inner4Value'

In an php script i would get the post request data in an associative array like this automatically:

[
    'prop1' => 'prop1Value',
    'prop2' => [
        'prop2Inner1' => 'prop2Inner1Value',
        'prop2Inner2' => [
            'inner3' => 'inner3Value',
            'inner4' => 'inner4Value',
        ]
    ]
]

Now im trying to handle this request in java. I can not change the incoming post request format. I thought that i can make a couple of Classes like this

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class MyRequest {
    private String prop1,
    private FirstInnerDto prop2;
}

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class FirstInnerDto {
    private String prop2Inner1;
    private SecondInnerDto prop2Inner2;
}

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class SecondInnerDto {
    private String inner3;
    private String inner4;
}

And than in the controller do something like this, and get an object of class 'MyRequest' filled with request data:

@RequestMapping("/some/path")
public ResponseEntity<SomeResponseDto> myHandler(@ModelAttribute MyRequest request) {
    // Do anything here with 'request' object

    return ResponseEntity.status(HttpStatus.OK.value()).body(new SomeResponseDto());
}

but i'm getting this error:

"Invalid property 'prop2[prop2Inner1]' of 
bean class [com.testapp.java.myapp.Model.Dto.Request.MyRequest]: 
Property referenced in indexed property path 'prop2[prop2Inner1]' is neither an array 
nor a List nor a Map; 
returned value was [FirstInnerDto{prop2Inner1='null', prop2Inner2='null'}]"

Can anyone tell me what is the right way to handle such requests in spring boot?

3 Answers3

0

As you have mentioned that the incoming request format cannot be changed, so you need to do some minor change at your end.

As the incoming request is coming as an array, you need to capture it as an array request only like @RequestBody MyRequest[] request as well as other attributes which i have updated in the corresponding classes as shown below-

Code:

@RequestMapping("/some/path")
public ResponseEntity<SomeResponseDto> myHandler(@RequestBody 
                                                MyRequest[] request) {
    // Do anything here with 'request' object
return ResponseEntity.status(HttpStatus.OK.value()).body(new SomeResponseDto());
}

Change in Pojo's

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class MyRequest {
    private String prop1,
    private FirstInnerDto[] prop2;
}

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class FirstInnerDto {
    private String prop2Inner1;
    private SecondInnerDto[] prop2Inner2;
}

@NonArgumentConstructor
@AllArgumentsConstructor
@Setter
@Getter
class SecondInnerDto {
    private String inner3;
    private String inner4;
}
GD07
  • 1,117
  • 1
  • 7
  • 9
  • Doing so got this error: "No primary or single unique constructor found for class [Lcom.testapp.java.myapp.Model.Dto.Request.MyRequest;". Changed to "List" in controller and pojos ("MyRequest[]" -> "List" ) and got "No primary or single unique constructor found for interface java.util.List". Changed then to "ArrayList" and got no error, but "request" object is empty in controller – Александр Родригес Aug 19 '23 at 17:14
  • @АлександрРодригес Can you try it with the array approach without using the lombok annotations. – GD07 Aug 19 '23 at 17:22
  • tried the array approach without lombok and without any constructors, setters and getters for each pojo - got error: No primary or single unique constructor found for class [Lcom.testapp.java.transferwallet.Model.Dto.Request.MyRequest;. Added constructors (empty and for all params to each pojo) - the same error. – Александр Родригес Aug 19 '23 at 18:12
  • okay, i have updated the code with ```@RequestBody``` instead of ```@ModelAttribute```, can you try it with this as it was working fine in my local with ```@RequestBody``` – GD07 Aug 19 '23 at 18:17
  • Nope: Content-Type 'application/x-www-form-urlencoded;charset=UTF-8' is not supported. Isn't @RequestBody just for application/json? – Александр Родригес Aug 19 '23 at 18:34
  • No ```@RequestBody``` supports various media types such as JSON, XML, form data, plain text, and others. – GD07 Aug 19 '23 at 19:25
0

can you change your content type from default application/json to application/x-www-form-urlencoded?

https://www.baeldung.com/spring-url-encoded-form-data#:~:text=FormHttpMessageConverter%20Basics,it%20with%20the%20method%20parameter.

0

So, changing pojos and controller as GD07 said (no lombok annotations; add @RequestBody) and adding consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE} to @RequestMapping annotation (as Girish Chandra Mishra) suggested, ended with such error:

Unsupported Media Type. Content-Type 'application/x-www-form-urlencoded;charset=UTF-8

Debugging the error found out that the message converter handling the request is FormHttpMessageConverter, and it can work only with MultiValueMap. So had to change controller code to this:

@RequestMapping(
    path="/some/path",
    consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}
)
public ResponseEntity<String> myHandler(@RequestBody MultiValueMap req) {
    // Do anything here with 'req' object

    return ResponseEntity.status(HttpStatus.OK.value()).body(req.toString());
}

Now it works without errors. But i still need to map this MultiValueMap req to my DTO's. So it seems that the only wright way is to write my own converter, as outlined in this answer: https://stackoverflow.com/a/51160620

Any other suggestions are welcome.