SpringBoot 2.6.2 及表单数据

SpringBoot 2.6.2 and form data

我正在试验如下所示的控制器端点:

@PostMapping("login")
fun login(
    @RequestParam username: String,
    @RequestParam password: String): ResponseEntity<LoginResponse> {
    // ...
}

请求是从 HTML 表单发送的,如下所示:

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

这与 spring 引导版本 2.6.1 完美配合。但是升级到2.6.2版本,加上spring云网关后,突然就不能用了。 日志将如下所示:

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

我尝试了各种方法,例如:

@PostMapping(value = ["login"], consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
fun login(
        @RequestParam  paramMap: MultiValueMap<String,String>
      ): ResponseEntity<LoginResponse> {
    //...
}

但这也失败了,并显示以下日志:

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

我猜 415 UNSUPPORTED_MEDIA_TYPE 的错误消息只是误导,它以某种方式无法映射表单数据。我该怎么做才能让 API 再次接受表单数据?

尝试类似的东西:

@PostMapping(value = ["login"], consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
    fun login(@RequestParam paramMap: Map<String,String>): ResponseEntity<LoginCodeResponse> {
   // ...
}

实际上调用了 get,但 paramMap 始终为空。

实际有效的是以下内容:

@RestController
@RequestMapping("api")
class HelloWorldController(){

    @GetMapping("hello")
    fun helloName(@RequestParam name: String): String {
        return "Hello $name!"
    }
}

因此对于正常的获取请求 @RequestParam 按预期工作。

更新

我似乎归结为以下几点。使用 spring-boot-starter-webflux 似乎表单数据的 @RequestParam 不起作用。这好像是a known issue.

implementation("org.springframework.boot:spring-boot-starter-webflux")

使用 spring-boot-starter-web@RequestParam 用于表单数据工作。

implementation("org.springframework.boot:spring-boot-starter-web")

但此启动器与 spring 云不兼容。同时使用 spring-boot-starter-web 和设置 spring.main.web-application-type=reactive 使 spring 云网关以 spring 开始-boot-starter-web 但对于表单数据仍然 @RequestParam 不起作用。

为了通过 POST 使用 webflux 获取数据,我使用很好地提供的 awaitFormData 方法执行了以下操作:

@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)!!

这只是解决方案要点的草图。如果你想使用它,你还应该添加一些适当的错误处理来检查参数是否存在,否则你只会有一个错误 500,这并没有说明很多。