Issue
I am experimenting with a controller endpoint that looks like this:
@PostMapping("login")
fun login(
@RequestParam username: String,
@RequestParam password: String): ResponseEntity<LoginResponse> {
// ...
}
The request is send from a HTML form looking like this:
<form action="../api/login" method="POST">
<input id="username" type="text" placeholder="Enter Username" name="username" required=""><br>
<input id="password" type="password" placeholder="Enter Password" name="password" required=""><br>
<button type="submit">Login</button>
</form>
This works perfectly will with spring boot version 2.6.1. But after an upgrade to version 2.6.2 and adding spring cloud gateway it all of a sudden does not work any longer. The log would look like this:
2022-01-11 14:33:09,618 [reactor-http-nio-2] DEBUG o.s.web.method.HandlerMethod - [3d97dc1a-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:13027] Could not resolve parameter [0] in public org.springframework.http.ResponseEntity<com.example.models.LoginResponse> com.example.login(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String): 400 BAD_REQUEST "Required String parameter 'username' is not present"
2022-01-11 14:33:09,656 [reactor-http-nio-2] DEBUG org.springframework.web.HttpLogging - [3d97dc1a-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:13027] Resolved [ServerWebInputException: 400 BAD_REQUEST "Required String parameter 'username' is not present"] for HTTP POST /api/login
I tried various things like:
@PostMapping(value = ["login"], consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
fun login(
@RequestParam paramMap: MultiValueMap<String,String>
): ResponseEntity<LoginResponse> {
//...
}
But also this fails with the following log:
2022-01-11 14:10:11,589 [reactor-http-nio-2] DEBUG o.s.w.s.a.HttpWebHandlerAdapter - [656327b1-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:11772] HTTP POST "/api/login"
2022-01-11 14:10:11,601 [reactor-http-nio-2] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [656327b1-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:11772] Mapped to com.example.ApiController#login(MultiValueMap)
2022-01-11 14:10:12,945 [reactor-http-nio-2] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - Form data is accessed via ServerWebExchange.getFormData() in WebFlux.
2022-01-11 14:10:21,640 [reactor-http-nio-2] DEBUG o.s.web.method.HandlerMethod - [656327b1-1, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:11772] Could not resolve parameter [0] in public org.springframework.http.ResponseEntity<com.example.models.LoginResponse> com.example.ApiController.login(org.springframework.util.MultiValueMap<java.lang.String, java.lang.String>): 415 UNSUPPORTED_MEDIA_TYPE
I would guess the error message with 415 UNSUPPORTED_MEDIA_TYPE
is just misleading and it somehow fails to map the form-data. What can I do to get the API again accept form-data?
Trying something like:
@PostMapping(value = ["login"], consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
fun login(@RequestParam paramMap: Map<String,String>): ResponseEntity<LoginCodeResponse> {
// ...
}
Actually get's called but paramMap is always empty.
What actually works is the following:
@RestController
@RequestMapping("api")
class HelloWorldController(){
@GetMapping("hello")
fun helloName(@RequestParam name: String): String {
return "Hello $name!"
}
}
So for a normal get request @RequestParam
works as expected.
Update
I seems to boil down to the following. With spring-boot-starter-webflux it seems the @RequestParam
for form-data does not work. This seem to be a known issue.
implementation("org.springframework.boot:spring-boot-starter-webflux")
With spring-boot-starter-web it @RequestParam
for form-data works.
implementation("org.springframework.boot:spring-boot-starter-web")
But this starter is not compatible with spring cloud. Using both spring-boot-starter-web with setting spring.main.web-application-type=reactive
makes spring cloud gateway start with spring-boot-starter-web but still @RequestParam
for form-data not working.
Solution
To get from data with POST working with webflux I did the following using the nicely provided awaitFormData method:
@PostMapping("authorize", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
suspend fun login(exchange: ServerWebExchange): ResponseEntity<LoginResponse> {
val formData = exchange.awaitFormData()
val username = formData["username"]?.get(0)!!
val password = formData["password"]?.get(0)!!
That is just a sketch of the essentials of the solution. If you want to use this you should also add some proper error handling to check whether parameters are there otherwise you will just have an error 500 which does not tell a lot.
Answered By - Sjoerd222888
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.