Micronaut 簡(jiǎn)單請(qǐng)求綁定

2023-03-02 16:40 更新

上一節(jié)中的示例演示了 Micronaut 如何讓您從 URI 路徑變量綁定方法參數(shù)。本節(jié)介紹如何綁定請(qǐng)求其他部分的參數(shù)。

綁定注解

所有綁定注解都支持自定義被綁定的變量的名稱及其名稱成員。

下表總結(jié)了注釋及其用途,并提供了示例:

表 1. 參數(shù)綁定注解
注解 描述 示例

@Body

從請(qǐng)求正文綁定

@Body String body

@CookieValue

從 cookie 綁定參數(shù)

@CookieValue String myCookie

@Header

綁定來(lái)自 HTTP 標(biāo)頭的參數(shù)

@Header String requestId

@QueryValue

從請(qǐng)求查詢參數(shù)綁定

@QueryValue String myParam

@Part

從多部分請(qǐng)求的一部分綁定

@Part CompletedFileUpload file

@RequestAttribute

從請(qǐng)求的屬性綁定。屬性通常在過(guò)濾器中創(chuàng)建

@RequestAttribute String myAttribute

@PathVariable

從請(qǐng)求的路徑綁定

@PathVariable String id

@RequestBean

將任何 Bindable 值綁定到單個(gè) Bean 對(duì)象

@RequestBean MyBean bean

當(dāng)綁定注釋中未指定值時(shí),將使用方法參數(shù)名稱。換句話說(shuō),以下兩個(gè)方法是等效的,并且都從名為 myCookie 的 cookie 綁定:

 Java Groovy  Kotlin 
@Get("/cookieName")
public String cookieName(@CookieValue("myCookie") String myCookie) {
    // ...
}

@Get("/cookieInferred")
public String cookieInferred(@CookieValue String myCookie) {
    // ...
}
@Get("/cookieName")
String cookieName(@CookieValue("myCookie") String myCookie) {
    // ...
}

@Get("/cookieInferred")
String cookieInferred(@CookieValue String myCookie) {
    // ...
}
@Get("/cookieName")
fun cookieName(@CookieValue("myCookie") myCookie: String): String {
    // ...
}

@Get("/cookieInferred")
fun cookieInferred(@CookieValue myCookie: String): String {
    // ...
}

由于變量名稱中不允許使用連字符,因此可能需要在注釋中設(shè)置名稱。下面的定義是等價(jià)的:

 Java Groovy  Kotlin 
@Get("/headerName")
public String headerName(@Header("Content-Type") String contentType) {
    // ...
}

@Get("/headerInferred")
public String headerInferred(@Header String contentType) {
    // ...
}
@Get("/headerName")
String headerName(@Header("Content-Type") String contentType) {
    // ...
}

@Get("/headerInferred")
String headerInferred(@Header String contentType) {
    // ...
}
@Get("/headerName")
fun headerName(@Header("Content-Type") contentType: String): String {
    // ...
}

@Get("/headerInferred")
fun headerInferred(@Header contentType: String): String {
    // ...
}

流支持

Micronaut 還支持將正文綁定到 InputStream。如果該方法正在讀取流,則必須將方法執(zhí)行卸載到另一個(gè)線程池以避免阻塞事件循環(huán)。

使用 InputStream 執(zhí)行阻塞 I/O

 Java Groovy Kotlin 
@Post(value = "/read", processes = MediaType.TEXT_PLAIN)
@ExecuteOn(TaskExecutors.IO) // (1)
String read(@Body InputStream inputStream) throws IOException { // (2)
    return IOUtils.readText(new BufferedReader(new InputStreamReader(inputStream))); // (3)
}
@Post(value = "/read", processes = MediaType.TEXT_PLAIN)
@ExecuteOn(TaskExecutors.IO) // (1)
String read(@Body InputStream inputStream) throws IOException { // (2)
    IOUtils.readText(new BufferedReader(new InputStreamReader(inputStream))) // (3)
}
@Post(value = "/read", processes = [MediaType.TEXT_PLAIN])
@ExecuteOn(TaskExecutors.IO) // (1)
fun read(@Body inputStream: InputStream): String { // (2)
    return IOUtils.readText(BufferedReader(InputStreamReader(inputStream))) // (3)
}
  1. 控制器方法在IO線程池上執(zhí)行

  2. 正文作為輸入流傳遞給方法

  3. 流被讀取

從多個(gè)查詢值綁定

與其從請(qǐng)求的單個(gè)部分進(jìn)行綁定,不如將所有查詢值綁定到例如 POJO。這可以通過(guò)在 URI 模板中使用展開運(yùn)算符 (?pojo*) 來(lái)實(shí)現(xiàn)。例如:

綁定請(qǐng)求參數(shù)到POJO

 Java Groovy  Kotlin 
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.core.annotation.Nullable;

import javax.validation.Valid;

@Controller("/api")
public class BookmarkController {

    @Get("/bookmarks/list{?paginationCommand*}")
    public HttpStatus list(@Valid @Nullable PaginationCommand paginationCommand) {
        return HttpStatus.OK;
    }
}
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get

import javax.annotation.Nullable
import javax.validation.Valid

@Controller("/api")
class BookmarkController {

    @Get("/bookmarks/list{?paginationCommand*}")
    HttpStatus list(@Valid @Nullable PaginationCommand paginationCommand) {
        HttpStatus.OK
    }
}
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import javax.validation.Valid

@Controller("/api")
open class BookmarkController {

    @Get("/bookmarks/list{?paginationCommand*}")
    open fun list(@Valid paginationCommand: PaginationCommand): HttpStatus {
        return HttpStatus.OK
    }
}

從多個(gè)可綁定值綁定

除了只綁定查詢值,還可以將任何 Bindable 值綁定到 POJO(例如,將 HttpRequest、@PathVariable、@QueryValue 和 @Header 綁定到單個(gè) POJO)。這可以通過(guò) @RequestBean 注釋和自定義 Bean 類來(lái)實(shí)現(xiàn),該類具有帶有 Bindable 注釋的字段,或者可以按類型(例如 HttpRequest、BasicAuth、Authentication 等)綁定的字段。

例如:

將 Bindable 值綁定到 POJO

 Java Groovy  Kotlin 
@Controller("/api")
public class MovieTicketController {

    // You can also omit query parameters like:
    // @Get("/movie/ticket/{movieId}
    @Get("/movie/ticket/{movieId}{?minPrice,maxPrice}")
    public HttpStatus list(@Valid @RequestBean MovieTicketBean bean) {
        return HttpStatus.OK;
    }
}
@Controller("/api")
class MovieTicketController {

    // You can also omit query parameters like:
    // @Get("/movie/ticket/{movieId}
    @Get("/movie/ticket/{movieId}{?minPrice,maxPrice}")
    HttpStatus list(@Valid @RequestBean MovieTicketBean bean) {
        HttpStatus.OK
    }
}
@Controller("/api")
open class MovieTicketController {

    // You can also omit query parameters like:
    // @Get("/movie/ticket/{movieId}
    @Get("/movie/ticket/{movieId}{?minPrice,maxPrice}")
    open fun list(@Valid @RequestBean bean: MovieTicketBean): HttpStatus {
        return HttpStatus.OK
    }

}

它使用這個(gè) bean 類:

Bean定義

 Java Groovy  Kotlin 
@Introspected
public class MovieTicketBean {

    private HttpRequest<?> httpRequest;

    @PathVariable
    private String movieId;

    @Nullable
    @QueryValue
    @PositiveOrZero
    private Double minPrice;

    @Nullable
    @QueryValue
    @PositiveOrZero
    private Double maxPrice;

    public MovieTicketBean(HttpRequest<?> httpRequest,
                           String movieId,
                           Double minPrice,
                           Double maxPrice) {
        this.httpRequest = httpRequest;
        this.movieId = movieId;
        this.minPrice = minPrice;
        this.maxPrice = maxPrice;
    }

    public HttpRequest<?> getHttpRequest() {
        return httpRequest;
    }

    public String getMovieId() {
        return movieId;
    }

    @Nullable
    public Double getMaxPrice() {
        return maxPrice;
    }

    @Nullable
    public Double getMinPrice() {
        return minPrice;
    }
}
@Introspected
class MovieTicketBean {

    private HttpRequest<?> httpRequest

    @PathVariable
    String movieId

    @Nullable
    @QueryValue
    @PositiveOrZero
    Double minPrice

    @Nullable
    @QueryValue
    @PositiveOrZero
    Double maxPrice
}
@Introspected
data class MovieTicketBean(
    val httpRequest: HttpRequest<Any>,
    @field:PathVariable val movieId: String,
    @field:QueryValue @field:PositiveOrZero @field:Nullable val minPrice: Double,
    @field:QueryValue @field:PositiveOrZero @field:Nullable val maxPrice: Double
)

bean 類必須使用@Introspected 進(jìn)行自省。它可以是以下之一:

  1. 具有 setter 和 getter 的可變 Bean 類

  2. 具有 getter 和全參數(shù)構(gòu)造函數(shù)(或構(gòu)造函數(shù)或靜態(tài)方法上的 @Creator 注釋)的不可變 Bean 類。構(gòu)造函數(shù)的參數(shù)必須匹配字段名稱,這樣對(duì)象就可以在沒有反射的情況下被實(shí)例化。

由于 Java 不在字節(jié)碼中保留參數(shù)名稱,因此您必須使用 -parameters 編譯代碼才能使用來(lái)自另一個(gè) jar 的不可變 bean 類。另一種選擇是在您的源代碼中擴(kuò)展 Bean 類。

可綁定類型

通常,可以綁定任何可以通過(guò) ConversionService API 從 String 表示形式轉(zhuǎn)換為 Java 類型的類型。

這包括最常見的 Java 類型,但是可以通過(guò)創(chuàng)建類型為 TypeConverter 的 @Singleton bean 來(lái)注冊(cè)其他 TypeConverter 實(shí)例。

可空性的處理值得特別提及。例如考慮以下示例:

 Java Groovy  Kotlin 
@Get("/headerInferred")
public String headerInferred(@Header String contentType) {
    // ...
}
@Get("/headerInferred")
String headerInferred(@Header String contentType) {
    // ...
}
@Get("/headerInferred")
fun headerInferred(@Header contentType: String): String {
    // ...
}

在這種情況下,如果請(qǐng)求中不存在 HTTP 標(biāo)頭 Content-Type,則路由被視為無(wú)效,因?yàn)樗鼰o(wú)法滿足,并返回 HTTP 400 BAD REQUEST。

要使 Content-Type 標(biāo)頭可選,您可以改為編寫:

 Java Groovy  Kotlin 
@Get("/headerNullable")
public String headerNullable(@Nullable @Header String contentType) {
    // ...
}
@Get("/headerNullable")
String headerNullable(@Nullable @Header String contentType) {
    // ...
}
@Get("/headerNullable")
fun headerNullable(@Header contentType: String?): String? {
    // ...
}

如果請(qǐng)求中沒有標(biāo)頭,則傳遞一個(gè)空字符串。

也可以使用 java.util.Optional,但不鼓勵(lì)將其用于方法參數(shù)。

此外,任何符合 RFC-1123 的 DateTime 都可以綁定到參數(shù)?;蛘撸梢允褂?nbsp;Format 注釋自定義格式:

 Java Groovy  Kotlin 
@Get("/date")
public String date(@Header ZonedDateTime date) {
    // ...
}

@Get("/dateFormat")
public String dateFormat(@Format("dd/MM/yyyy hh:mm:ss a z") @Header ZonedDateTime date) {
    // ...
}
@Get("/date")
String date(@Header ZonedDateTime date) {
    // ...
}

@Get("/dateFormat")
String dateFormat(@Format("dd/MM/yyyy hh:mm:ss a z") @Header ZonedDateTime date) {
    // ...
}
@Get("/date")
fun date(@Header date: ZonedDateTime): String {
    // ...
}

@Get("/dateFormat")
fun dateFormat(@Format("dd/MM/yyyy hh:mm:ss a z") @Header date: ZonedDateTime): String {
    // ...
}

基于類型的綁定參數(shù)

一些參數(shù)通過(guò)它們的類型而不是它們的注釋來(lái)識(shí)別。下表總結(jié)了參數(shù)類型及其用途,并提供了一個(gè)示例:

類型 描述 示例

BasicAuth

允許綁定基本授權(quán)憑據(jù)

BasicAuth basicAuth

可變分辨率

Micronaut 嘗試按以下順序填充方法參數(shù):

  1. URI 變量,如 /{id}。

  2. 如果請(qǐng)求是 GET 請(qǐng)求(例如?foo=bar),則來(lái)自查詢參數(shù)。

  3. 如果有 @Body 并且請(qǐng)求允許主體,則將主體綁定到它。

  4. 如果請(qǐng)求可以有主體并且沒有定義 @Body 則嘗試解析主體(JSON 或表單數(shù)據(jù))并從主體綁定方法參數(shù)。

  5. 最后,如果無(wú)法填充方法參數(shù),則返回 400 BAD REQUEST。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)