繁体   English   中英

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

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

我正在尝试使用 Spring 5 为我的请求参数设置自定义 bean。 理论上这很容易,但我想让字段名称与参数名称不同。

我可以使用普通的@RequestParam参数轻松地做到这一点,但我似乎无法让它与 bean 一起工作。

我发现这个问题之前被问过,答案似乎是“手动执行”,有各种不同的自动化选项,例如使用参数解析器。 但是Spring 5真的还是这样吗?

我的代码(顺便说一句,它是 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 {

似乎没有 Spring 提供的方法可以做到这一点。

Spring GitHub 页面上有一个未解决的问题: https://github.com/spring-projects/spring-framework/issues/25815

我在 StackOverflow 上找到了一些解决方案,例如https://stackoverflow.com/a/16520399/4161471 ,但它们看起来相当复杂,或者不可重复使用。 所以我开发了一个解决方法。

解决方法:解析为 JSON

我通过创建自定义注释和HandlerMethodArgumentResolver解决了这个问题。 参数解析器将使用 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)
    }
  }
}

请注意,将参数 map 值与webRequest.parameterMap.mapValues { (_, v) -> v.joinToString() }一起加入是一个快速修复。 它可能不正确,只适用于具有原始参数的类 - 请提出改进建议!

用法示例

所以如果我有一个 DTO...

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

我已经使用@JsonProperty来覆盖属性名称,这意味着“名称”的请求参数将为n

现在在我的@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"
  }
}

所以如果我 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