简体   繁体   English

Spring 5 中带有请求参数bean的自定义参数名称

[英]Custom parameter names with a bean for request parameters in Spring 5

I'm trying to use Spring 5 to have a custom bean for my request parameters.我正在尝试使用 Spring 5 为我的请求参数设置自定义 bean。 In theory this is easy, but I want to have the field names be different from the parameter names.理论上这很容易,但我想让字段名称与参数名称不同。

I can do that trivially with normal @RequestParam parameters, but I can't seem to get it to work with a bean.我可以使用普通的@RequestParam参数轻松地做到这一点,但我似乎无法让它与 bean 一起工作。

I've found this question asked before, and the answer seems to be "Do it manually", with various different options for automating that, eg using Argument Resolvers.我发现这个问题之前被问过,答案似乎是“手动执行”,有各种不同的自动化选项,例如使用参数解析器。 But is this really still the case in Spring 5?但是Spring 5真的还是这样吗?

My code (It's Kotlin btw, but that shouldn't matter) is like this:我的代码(顺便说一句,它是 Kotlin,但这不重要)是这样的:

data class AuthorizationCodeParams(
    @RequestParam("client_id") val clientIdValue: String?,
    @RequestParam("redirect_uri") val redirectUriValue: String?,
    @RequestParam("scope") val scopes: String?,
    @RequestParam("state") val state: String?
)

fun startAuthorizationCode(params: AuthorizationCodeParams): ModelAndView {

It seems there's no Spring-provided way of doing this.似乎没有 Spring 提供的方法可以做到这一点。

There's an open issue on the Spring GitHub page: https://github.com/spring-projects/spring-framework/issues/25815 Spring GitHub 页面上有一个未解决的问题: https://github.com/spring-projects/spring-framework/issues/25815

I found some solutions on StackOverflow, for example https://stackoverflow.com/a/16520399/4161471 , but they seemed quite complex, or not re-usable.我在 StackOverflow 上找到了一些解决方案,例如https://stackoverflow.com/a/16520399/4161471 ,但它们看起来相当复杂,或者不可重复使用。 So I developed a workaround.所以我开发了一个解决方法。

Workaround: Parse as JSON解决方法:解析为 JSON

I resolved this by creating a custom annotation and a HandlerMethodArgumentResolver .我通过创建自定义注释和HandlerMethodArgumentResolver解决了这个问题。 The argument resolver will translate the request parameters using Jackson. (Using Jackson is not required, but it was nice and generic and safe.)参数解析器将使用 Jackson 转换请求参数。(不需要使用 Jackson,但它很好、通用且安全。)

import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.core.MethodParameter
import org.springframework.stereotype.Component
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.method.support.ModelAndViewContainer

/**
 * Parses all request parameters and maps them to a JSON-serializable class.
 *
 * This is useful, because it allows multiple `@RequestParam`s to be
 * encapsulated in a single object, the values will be type-safe, and
 * the parameter names can be set using (for example, with `@JsonProperty`).
 */
@Target(AnnotationTarget.VALUE_PARAMETER)
@MustBeDocumented
annotation class JsonRequestParam {

  @Component
  class ArgumentResolver(
    private val objectMapper: ObjectMapper
  ) : HandlerMethodArgumentResolver {
  
    // only resolve parameters that are annotated with JsonRequestParam
    override fun supportsParameter(parameter: MethodParameter): Boolean {
      return parameter.hasParameterAnnotation(JsonRequestParam::class.java)
    }
  
    override fun resolveArgument(
      parameter: MethodParameter,
      mavContainer: ModelAndViewContainer?,
      webRequest: NativeWebRequest,
      binderFactory: WebDataBinderFactory?
    ): Any? {
      // `webRequest.parameterMap` is a `Map<String, String[]>`, so join
      // the values to a string
      val params: Map<String, String> = webRequest.parameterMap.mapValues { (_, v) -> v.joinToString() }
      return objectMapper.convertValue(params, parameter.parameterType)
    }
  }
}

Note that joining the parameter map values together with webRequest.parameterMap.mapValues { (_, v) -> v.joinToString() } is a quick fix.请注意,将参数 map 值与webRequest.parameterMap.mapValues { (_, v) -> v.joinToString() }一起加入是一个快速修复。 It's probably not correct and will only work for classes with primitive parameters - please suggest improvements!它可能不正确,只适用于具有原始参数的类 - 请提出改进建议!

Example usage用法示例

So if I have a DTO...所以如果我有一个 DTO...

data class MyDataObject(
  @JsonProperty("n")
  val name: String,
  val age: Int,
)

I've used @JsonProperty to override the property name, which means the request parameter for 'name' will be n .我已经使用@JsonProperty来覆盖属性名称,这意味着“名称”的请求参数将为n

Now in my @Controller I can use the DTO in a @GetMapping method, and annotate it with the @JsonRequestParam I created.现在在我的@Controller中,我可以在@GetMapping方法中使用 DTO,并使用我创建的@JsonRequestParam对其进行注释。

import my.project.JsonRequestParam
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api")
class MyDataController {

  @GetMapping(value = ["/data"])
  fun getData(
    @JsonRequestParam
    request: MyDataObject
  ): ResponseEntity<String> {
    return "get data $request"
  }
}

So if I GET /api/data?n=MyName&age=22 , then I'll get a response:所以如果我 GET /api/data?n=MyName&age=22 ,那么我会得到一个回应:

get data MyDataObject(name = "MyName", age = 22)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM