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