使用@RequestMapping
注解的處理方法可以擁有非常靈活的方法簽名,它支持的方法參數(shù)及返回值類型將在接下來(lái)的小節(jié)講述。大多數(shù)參數(shù)都可以任意的次序出現(xiàn),除了唯一的一個(gè)例外:BindingResult
參數(shù)。這在下節(jié)也會(huì)詳細(xì)描述。
Spring 3.1中新增了一些類,用以增強(qiáng)注解了
@RequestMapping
的處理方法,分別是RequestMappingHandlerMapping
類和RequestMappingHandlerAdapter
類。我們鼓勵(lì)使用這組新的類,如果要使用Spring 3.1及以后版本的新特性,這組類甚至是必須使用的。這些增強(qiáng)類在MVC的命名空間配置和MVC的Java編程方式配置中都是默認(rèn)開啟的,如果不是使用這兩種方法,那么就需要顯式地配置。
下面列出所有支持的方法參數(shù)類型:
ServletRequest
或HttpServletRequest
對(duì)象等。HttpSession
類型的會(huì)話對(duì)象(Servlet API)。使用該類型的參數(shù)將要求這樣一個(gè)session的存在,因此這樣的參數(shù)永不為null
。存取session可能不是線程安全的,特別是在一個(gè)Servlet的運(yùn)行環(huán)境中。如果應(yīng)用可能有多個(gè)請(qǐng)求同時(shí)并發(fā)存取一個(gè)session場(chǎng)景,請(qǐng)考慮將RequestMappingHandlerAdapter類中的"synchronizeOnSession"標(biāo)志設(shè)置為"true"。
org.springframework.web.context.request.WebRequest
或org.springframework.web.context.request.NativeWebRequest
。允許存取一般的請(qǐng)求參數(shù)和請(qǐng)求/會(huì)話范圍的屬性(attribute),同時(shí)無(wú)需綁定使用Servlet/Portlet的APIjava.util.Locale
,由已配置的最相關(guān)的地區(qū)解析器解析得到。在MVC的環(huán)境下,就是應(yīng)用中配置的LocaleResolver
或LocaleContextResolver
java.util.TimeZone
(java 6以上的版本)/java.time.ZoneId
(java 8),由LocaleContextResolver
解析得到java.io.InputStream
或java.io.Reader
。該對(duì)象與通過(guò)Servlet API拿到的輸入流/Reader是一樣的java.io.OutputStream
或java.io.Writer
。該對(duì)象與通過(guò)Servlet API拿到的輸出流/Writer是一樣的org.springframework.http.HttpMethod
。可以拿到HTTP請(qǐng)求方法java.security.Principal
@PathVariable
注解的方法參數(shù),其存放了URI模板變量中的值。詳見(jiàn)“URI模板變量”一節(jié)@MatrixVariable
注解的方法參數(shù),其存放了URI路徑段中的鍵值對(duì)。詳見(jiàn)“矩陣變量”一節(jié)@RequestParam
注解的方法參數(shù),其存放了Servlet請(qǐng)求中所指定的參數(shù)。參數(shù)的值會(huì)被轉(zhuǎn)換成方法參數(shù)所聲明的類型。詳見(jiàn)“使用@RequestParam注解綁定請(qǐng)求參數(shù)至方法參數(shù)”一節(jié)@RequestHeader
注解的方法參數(shù),其存放了Servlet請(qǐng)求中所指定的HTTP請(qǐng)求頭的值。參數(shù)的值會(huì)被轉(zhuǎn)換成方法參數(shù)所聲明的類型。詳見(jiàn)“使用@RequestHeader注解映射請(qǐng)求頭屬性”一節(jié).@RequestBody
注解的參數(shù),提供了對(duì)HTTP請(qǐng)求體的存取。參數(shù)的值通過(guò)HttpMessageConverter
被轉(zhuǎn)換成方法參數(shù)所聲明的類型。詳見(jiàn)“使用@RequestBody注解映射請(qǐng)求體”一節(jié)"@RequestPart
注解的參數(shù),提供了對(duì)一個(gè)"multipart/form-data請(qǐng)求塊(request part)內(nèi)容的存取。更多的信息請(qǐng)參考21.10.5 “處理客戶端文件上傳的請(qǐng)求”一節(jié)和21.10 “Spring對(duì)多部分文件上傳的支持”一節(jié)HttpEntity<?>
類型的參數(shù),其提供了對(duì)HTTP請(qǐng)求頭和請(qǐng)求內(nèi)容的存取。請(qǐng)求流是通過(guò)HttpMessageConverter
被轉(zhuǎn)換成entity對(duì)象的。詳見(jiàn)“HttpEntity”一節(jié)java.util.Map
/org.springframework.io.Model
/org.springframework.ui.ModelMap
類型的參數(shù),用以增強(qiáng)默認(rèn)暴露給視圖層的模型(model)的功能org.springframework.web.servlet.mvc.support.RedirectAttributes
類型的參數(shù),用以指定重定向下要使用到的屬性集以及添加flash屬性(暫存在服務(wù)端的屬性,它們會(huì)在下次重定向請(qǐng)求的范圍中有效)。詳見(jiàn)“向重定向請(qǐng)求傳遞參數(shù)”一節(jié)@InitBinder
注解和/或HanderAdapter
的配置來(lái)定制這個(gè)過(guò)程的類型轉(zhuǎn)換。具體請(qǐng)參考RequestMappingHandlerAdapter
類webBindingInitializer
屬性的文檔。這樣的命令對(duì)象,以及其上的驗(yàn)證結(jié)果,默認(rèn)會(huì)被添加到模型model中,鍵名默認(rèn)是該命令對(duì)象類的類名——比如,some.package.OrderAddress
類型的命令對(duì)象就使用屬性名orderAddress
類獲取。ModelAttribute
注解可以應(yīng)用在方法參數(shù)上,用以指定該模型所用的屬性名org.springframework.validation.Errors
/ org.springframework.validation.BindingResult
驗(yàn)證結(jié)果對(duì)象,用于存儲(chǔ)前面的命令或表單對(duì)象的驗(yàn)證結(jié)果(緊接其前的第一個(gè)方法參數(shù))。org.springframework.web.bind.support.SessionStatus
對(duì)象,用以標(biāo)記當(dāng)前的表單處理已結(jié)束。這將觸發(fā)一些清理操作:@SessionAttributes
在類級(jí)別注解的屬性將被移除org.springframework.web.util.UriComponentsBuilder
構(gòu)造器對(duì)象,用于構(gòu)造當(dāng)前請(qǐng)求URL相關(guān)的信息,比如主機(jī)名、端口號(hào)、資源類型(scheme)、上下文路徑、servlet映射中的相對(duì)部分(literal part)等在參數(shù)列表中,Errors
或BindingResult
參數(shù)必須緊跟在其所綁定的驗(yàn)證對(duì)象后面。這是因?yàn)?,在參?shù)列表中允許有多于一個(gè)的模型對(duì)象,Spring會(huì)為它們創(chuàng)建不同的BindingResult
實(shí)例。因此,下面這樣的代碼是不能工作的:
BindingResult與@ModelAttribute錯(cuò)誤的參數(shù)次序
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { ... }
上例中,因?yàn)樵谀P蛯?duì)象Pet
和驗(yàn)證結(jié)果對(duì)象BindingResult
中間還插了一個(gè)Model
參數(shù),這是不行的。要達(dá)到預(yù)期的效果,必須調(diào)整一下參數(shù)的次序:
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { ... }
對(duì)于一些帶有
required
屬性的注解(比如@RequestParam
、@RequestHeader
等),JDK 1.8的java.util.Optional
可以作為被它們注解的方法參數(shù)。在這種情況下,使用java.util.Optional
與required=false
的作用是相同的。
以下是handler方法允許的所有返回類型:
ModelAndView
對(duì)象,其中model隱含填充了命令對(duì)象,以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值。Model
對(duì)象,其中視圖名稱默認(rèn)由RequestToViewNameTranslator
決定,model隱含填充了命令對(duì)象以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值Map
對(duì)象,用于暴露model,其中視圖名稱默認(rèn)由RequestToViewNameTranslator
決定,model隱含填充了命令對(duì)象以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值View
對(duì)象。其中model隱含填充了命令對(duì)象,以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值。handler方法也可以增加一個(gè)Model
類型的方法參數(shù)來(lái)增強(qiáng)modelString
對(duì)象,其值會(huì)被解析成一個(gè)邏輯視圖名。其中,model將默認(rèn)填充了命令對(duì)象以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值。handler方法也可以增加一個(gè)Model
類型的方法參數(shù)來(lái)增強(qiáng)modelvoid
。如果處理器方法中已經(jīng)對(duì)response響應(yīng)數(shù)據(jù)進(jìn)行了處理(比如在方法參數(shù)中定義一個(gè)ServletResponse
或HttpServletResponse
類型的參數(shù)并直接向其響應(yīng)體中寫東西),那么方法可以返回void。handler方法也可以增加一個(gè)Model
類型的方法參數(shù)來(lái)增強(qiáng)modelResponseBody
,那么返回類型將被寫到HTTP的響應(yīng)體中,而返回值會(huì)被HttpMessageConverters
轉(zhuǎn)換成所方法聲明的參數(shù)類型。詳見(jiàn)使用"@ResponseBody注解映射響應(yīng)體"一節(jié)HttpEntity<?>
或ResponseEntity<?>
對(duì)象,用于提供對(duì)Servlet HTTP響應(yīng)頭和響應(yīng)內(nèi)容的存取。對(duì)象體會(huì)被HttpMessageConverters
轉(zhuǎn)換成響應(yīng)流。詳見(jiàn)使用HttpEntity一節(jié)HttpHeaders
對(duì)象,返回一個(gè)不含響應(yīng)體的responseCallable<?>
對(duì)象。當(dāng)應(yīng)用希望異步地返回方法值時(shí)使用,這個(gè)過(guò)程由Spring MVC自身的線程來(lái)管理DeferredResult<?>
對(duì)象。當(dāng)應(yīng)用希望方法的返回值交由線程自身決定時(shí)使用ListenableFuture<?>
對(duì)象。當(dāng)應(yīng)用希望方法的返回值交由線程自身決定時(shí)使用ResponseBodyEmitter
對(duì)象,可用它異步地向響應(yīng)體中同時(shí)寫多個(gè)對(duì)象,also supported as the body within a ResponseEntity
SseEmitter
對(duì)象,可用它異步地向響應(yīng)體中寫服務(wù)器端事件(Server-Sent Events),also supported as the body within a ResponseEntity
StreamingResponseBody
對(duì)象,可用它異步地向響應(yīng)對(duì)象的輸出流中寫東西。also supported as the body within a ResponseEntity
@ModelAttribute
所注解的字段名(或者以返回類型的類名作為默認(rèn)的屬性名)。model隱含填充了命令對(duì)象以及注解了@ModelAttribute
字段的存取器被調(diào)用所返回的值你可以使用@RequestParam
注解將請(qǐng)求參數(shù)綁定到你控制器的方法參數(shù)上。
下面這段代碼展示了它的用法:
@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {
// ...
@RequestMapping(method = RequestMapping.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ,..
}
若參數(shù)使用了該注解,則該參數(shù)默認(rèn)是必須提供的,但你也可以把該參數(shù)標(biāo)注為非必須的:只需要將@RequestParam
注解的required
屬性設(shè)置為false
即可(比如,@RequestParam(path="id", required=false)
)。
若所注解的方法參數(shù)類型不是String
,則類型轉(zhuǎn)換會(huì)自動(dòng)地發(fā)生。詳見(jiàn)"方法參數(shù)與類型轉(zhuǎn)換"一節(jié)
若@RequestParam
注解的參數(shù)類型是Map<String, String>
或者MultiValueMap<String, String>
,則該Map中會(huì)自動(dòng)填充所有的請(qǐng)求參數(shù)。
方法參數(shù)中的@RequestBody
注解暗示了方法參數(shù)應(yīng)該被綁定了HTTP請(qǐng)求體的值。舉個(gè)例子:
@RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
請(qǐng)求體到方法參數(shù)的轉(zhuǎn)換是由HttpMessageConverter
完成的。HttpMessageConverter
負(fù)責(zé)將HTTP請(qǐng)求信息轉(zhuǎn)換成對(duì)象,以及將對(duì)象轉(zhuǎn)換回一個(gè)HTTP響應(yīng)體。對(duì)于@RequestBody
注解,RequestMappingHandlerAdapter
提供了以下幾種默認(rèn)的HttpMessageConverter
支持:
ByteArrayHttpMessageConverter
用以轉(zhuǎn)換字節(jié)數(shù)組StringHttpMessageConverter
用以轉(zhuǎn)換字符串FormHttpMessageConverter
用以將表格數(shù)據(jù)轉(zhuǎn)換成MultiValueMap<String, String>
或從MultiValueMap<String, String>
中轉(zhuǎn)換出表格數(shù)據(jù)SourceHttpMessageConverter
用于javax.xml.transform.Source
類的互相轉(zhuǎn)換關(guān)于這些轉(zhuǎn)換器的更多信息,請(qǐng)參考"HTTP信息轉(zhuǎn)換器"一節(jié)。另外,如果使用的是MVC命名空間或Java編程的配置方式,會(huì)有更多默認(rèn)注冊(cè)的消息轉(zhuǎn)換器。更多信息,請(qǐng)參考"啟用MVC Java編程配置或MVC XML命令空間配置"一節(jié)。
若你更傾向于閱讀和編寫XML文件,那么你需要配置一個(gè)MarshallingHttpMessageConverter
并為其提供org.springframework.oxm
包下的一個(gè)Marshaller
和Unmarshaller
實(shí)現(xiàn)。下面的示例就為你展示如何直接在配置文件中配置它。但如果你的應(yīng)用是使用MVC命令空間或MVC Java編程的方式進(jìn)行配置的,則請(qǐng)參考"啟用MVC Java編程配置或MVC XML命令空間配置"這一節(jié)。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="stringHttpMessageConverter"/>
<ref bean="marshallingHttpMessageConverter"/>
</util:list>
</property
</bean>
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="castorMarshaller"/>
<property name="unmarshaller" ref="castorMarshaller"/>
</bean>
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
注解了@RequestBody
的方法參數(shù)還可以被@Valid
注解,這樣框架會(huì)使用已配置的Validator
實(shí)例來(lái)對(duì)該參數(shù)進(jìn)行驗(yàn)證。若你的應(yīng)用是使用MVC命令空間或MVC Java編程的方式配置的,框架會(huì)假設(shè)在classpath路徑下存在一個(gè)符合JSR-303規(guī)范的驗(yàn)證器,并自動(dòng)將其作為默認(rèn)配置。
與@ModelAttribute
注解的參數(shù)一樣,Errors
也可以被傳入為方法參數(shù),用于檢查錯(cuò)誤。如果沒(méi)有聲明這樣一個(gè)參數(shù),那么程序會(huì)拋出一個(gè)MethodArgumentNotValidException
異常。該異常默認(rèn)由DefaultHandlerExceptionResolver
處理,處理程序會(huì)返回一個(gè)400
錯(cuò)誤給客戶端。
關(guān)于如何通過(guò)MVC命令空間或MVC Java編程的方式配置消息轉(zhuǎn)換器和驗(yàn)證器,也請(qǐng)參考"啟用MVC Java編程配置或MVC XML命令空間配置"一節(jié)。
@ResponseBody
注解與@RequestBody
注解類似。@ResponseBody
注解可被應(yīng)用于方法上,標(biāo)志該方法的返回值(更正,原文是return type,看起來(lái)應(yīng)該是返回值)應(yīng)該被直接寫回到HTTP響應(yīng)體中去(而不會(huì)被被放置到Model中或被解釋為一個(gè)視圖名)。舉個(gè)例子:
@RequestMapping(path = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
return "Hello World"
}
上面的代碼結(jié)果是文本Hello World
將被寫入HTTP的響應(yīng)流中。
與@RequestBody
注解類似,Spring使用了一個(gè)HttpMessageConverter
來(lái)將返回對(duì)象轉(zhuǎn)換到響應(yīng)體中。關(guān)于這些轉(zhuǎn)換器的更多信息,請(qǐng)參考"HTTP信息轉(zhuǎn)換器"一節(jié)。
當(dāng)今讓控制器實(shí)現(xiàn)一個(gè)REST API是非常常見(jiàn)的,這種場(chǎng)景下控制器只需要提供JSON、XML或其他自定義的媒體類型內(nèi)容即可。你不需要在每個(gè)@RequestMapping
方法上都增加一個(gè)@ResponseBody
注解,更簡(jiǎn)明的做法是,給你的控制器加上一個(gè)@RestController
的注解。
@RestController
是一個(gè)原生內(nèi)置的注解,它結(jié)合了@ResponseBody
與@Controller
注解的功能。不僅如此,它也讓你的控制器更表義,而且在框架未來(lái)的發(fā)布版本中,它也可能承載更多的意義。
與普通的@Controller
無(wú)異,@RestController
也可以與@ControllerAdvice
bean配合使用。更多細(xì)節(jié),請(qǐng)見(jiàn)使用@ControllerAdvice輔助控制器。
HttpEntity
與@RequestBody
和@ResponseBody
很相似。除了能獲得請(qǐng)求體和響應(yīng)體中的內(nèi)容之外,HttpEntity
(以及專門負(fù)責(zé)處理響應(yīng)的ResponseEntity
子類)還可以存取請(qǐng)求頭和響應(yīng)頭,像下面這樣:
@RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
byte[] requestBody = requestEntity.getBody();
// do something with request header and body
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
上面這段示例代碼先是獲取了MyRequestHeader
請(qǐng)求頭的值,然后讀取請(qǐng)求體的主體內(nèi)容。讀完以后往影響頭中添加了一個(gè)自己的響應(yīng)頭MyResponseHeader
,然后向響應(yīng)流中寫了字符串Hello World
,最后把響應(yīng)狀態(tài)碼設(shè)置為201(創(chuàng)建成功)。
與@RequestBody
與@ResponseBody
注解一樣,Spring使用了HttpMessageConverter
來(lái)對(duì)請(qǐng)求流和響應(yīng)流進(jìn)行轉(zhuǎn)換。關(guān)于這些轉(zhuǎn)換器的更多信息,請(qǐng)閱讀上一小節(jié)以及"HTTP信息轉(zhuǎn)換器"這一節(jié)。
@ModelAttribute
注解可被應(yīng)用在方法或方法參數(shù)上。本節(jié)將介紹其被注解于方法上時(shí)的用法,下節(jié)會(huì)介紹其被用于注解方法參數(shù)的用法。
注解在方法上的@ModelAttribute
說(shuō)明了方法的作用是用于添加一個(gè)或多個(gè)屬性到model上。這樣的方法能接受與@RequestMapping
注解相同的參數(shù)類型,只不過(guò)不能直接被映射到具體的請(qǐng)求上。在同一個(gè)控制器中,注解了@ModelAttribute
的方法實(shí)際上會(huì)在@RequestMapping
方法之前被調(diào)用。以下是幾個(gè)例子:
// Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountManager.findAccount(number);
}
// Add multiple attributes
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountManager.findAccount(number));
// add more ...
}
@ModelAttribute
方法通常被用來(lái)填充一些公共需要的屬性或數(shù)據(jù),比如一個(gè)下拉列表所預(yù)設(shè)的幾種狀態(tài),或者寵物的幾種類型,或者去取得一個(gè)HTML表單渲染所需要的命令對(duì)象,比如Account
等。
留意@ModelAttribute
方法的兩種風(fēng)格。在第一種寫法中,方法通過(guò)返回值的方式默認(rèn)地將添加一個(gè)屬性;在第二種寫法中,方法接收一個(gè)Model
對(duì)象,然后可以向其中添加任意數(shù)量的屬性。你可以在根據(jù)需要,在兩種風(fēng)格中選擇合適的一種。
一個(gè)控制器可以擁有數(shù)量不限的@ModelAttribute
方法。同個(gè)控制器內(nèi)的所有這些方法,都會(huì)在@RequestMapping
方法之前被調(diào)用。
@ModelAttribute
方法也可以定義在@ControllerAdvice
注解的類中,并且這些@ModelAttribute
可以同時(shí)對(duì)許多控制器生效。具體的信息可以參考使用@ControllerAdvice輔助控制器。
屬性名沒(méi)有被顯式指定的時(shí)候又當(dāng)如何呢?在這種情況下,框架將根據(jù)屬性的類型給予一個(gè)默認(rèn)名稱。舉個(gè)例子,若方法返回一個(gè)
Account
類型的對(duì)象,則默認(rèn)的屬性名為"account"。你可以通過(guò)設(shè)置@ModelAttribute
注解的值來(lái)改變默認(rèn)值。當(dāng)向Model
中直接添加屬性時(shí),請(qǐng)使用合適的重載方法addAttribute(..)
-即,帶或不帶屬性名的方法。
@ModelAttribute
注解也可以被用在@RequestMapping
方法上。這種情況下,@RequestMapping
方法的返回值將會(huì)被解釋為model的一個(gè)屬性,而非一個(gè)視圖名。此時(shí)視圖名將以視圖命名約定來(lái)方式來(lái)決議,與返回值為void的方法所采用的處理方法類似——請(qǐng)見(jiàn)視圖:請(qǐng)求與視圖名的對(duì)應(yīng)。
如上一小節(jié)所解釋,@ModelAttribute
注解既可以被用在方法上,也可以被用在方法參數(shù)上。這一小節(jié)將介紹它注解在方法參數(shù)上時(shí)的用法。
注解在方法參數(shù)上的@ModelAttribute
說(shuō)明了該方法參數(shù)的值將由model中取得。如果model中找不到,那么該參數(shù)會(huì)先被實(shí)例化,然后被添加到model中。在model中存在以后,請(qǐng)求中所有名稱匹配的參數(shù)都會(huì)填充到該參數(shù)中。這在Spring MVC中被稱為數(shù)據(jù)綁定,一個(gè)非常有用的特性,節(jié)約了你每次都需要手動(dòng)從表格數(shù)據(jù)中轉(zhuǎn)換這些字段數(shù)據(jù)的時(shí)間。
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Pet pet) { }
以上面的代碼為例,這個(gè)Pet類型的實(shí)例可能來(lái)自哪里呢?有幾種可能:
@ModelAttribute
方法已經(jīng)存在于model中——正如上一小節(jié)所敘述的@ModelAttribute
方法常用于從數(shù)據(jù)庫(kù)中取一個(gè)屬性值,該值可能通過(guò)@SessionAttributes
注解在請(qǐng)求中間傳遞。在一些情況下,使用URI模板變量和類型轉(zhuǎn)換的方式來(lái)取得一個(gè)屬性是更方便的方式。這里有個(gè)例子:
@RequestMapping(path = "/accounts/{account}", method = RequestMethod.PUT)
public String save(@ModelAttribute("account") Account account) {
}
上面這個(gè)例子中,model屬性的名稱("account")與URI模板變量的名稱相匹配。如果你配置了一個(gè)可以將String
類型的賬戶值轉(zhuǎn)換成Account
類型實(shí)例的轉(zhuǎn)換器Converter<String, Account>
,那么上面這段代碼就可以工作的很好,而不需要再額外寫一個(gè)@ModelAttribute
方法。
下一步就是數(shù)據(jù)的綁定。WebDataBinder
類能將請(qǐng)求參數(shù)——包括字符串的查詢參數(shù)和表單字段等——通過(guò)名稱匹配到model的屬性上。成功匹配的字段在需要的時(shí)候會(huì)進(jìn)行一次類型轉(zhuǎn)換(從String類型到目標(biāo)字段的類型),然后被填充到model對(duì)應(yīng)的屬性中。數(shù)據(jù)綁定和數(shù)據(jù)驗(yàn)證的問(wèn)題在第8章 驗(yàn)證,數(shù)據(jù)綁定和類型轉(zhuǎn)換中提到。如何在控制器層來(lái)定制數(shù)據(jù)綁定的過(guò)程,在這一節(jié) "定制WebDataBinder的初始化"中提及。
進(jìn)行了數(shù)據(jù)綁定后,則可能會(huì)出現(xiàn)一些錯(cuò)誤,比如沒(méi)有提供必須的字段、類型轉(zhuǎn)換過(guò)程的錯(cuò)誤等。若想檢查這些錯(cuò)誤,可以在注解了@ModelAttribute
的參數(shù)緊跟著聲明一個(gè)BindingResult
參數(shù):
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
拿到BindingResult
參數(shù)后,你可以檢查是否有錯(cuò)誤。有時(shí)你可以通過(guò)Spring的<errors>
表單標(biāo)簽來(lái)在同一個(gè)表單上顯示錯(cuò)誤信息。
BindingResult
被用于記錄數(shù)據(jù)綁定過(guò)程的錯(cuò)誤,因此除了數(shù)據(jù)綁定外,你還可以把該對(duì)象傳給自己定制的驗(yàn)證器來(lái)調(diào)用驗(yàn)證。這使得數(shù)據(jù)綁定過(guò)程和驗(yàn)證過(guò)程出現(xiàn)的錯(cuò)誤可以被搜集到一處,然后一并返回給用戶:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
又或者,你可以通過(guò)添加一個(gè)JSR-303規(guī)范的@Valid
注解,這樣驗(yàn)證器會(huì)自動(dòng)被調(diào)用。
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
關(guān)于如何配置并使用驗(yàn)證,可以參考第8.8小節(jié) "Spring驗(yàn)證"和第8章 驗(yàn)證,數(shù)據(jù)綁定和類型轉(zhuǎn)換。
類型級(jí)別的@SessionAttributes
注解聲明了某個(gè)特定處理器所使用的會(huì)話屬性。通常它會(huì)列出該類型希望存儲(chǔ)到session或converstaion中的model屬性名或model的類型名,一般是用于在請(qǐng)求之間保存一些表單數(shù)據(jù)的bean。
以下的代碼段演示了該注解的用法,它指定了模型屬性的名稱
@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
上一小節(jié)講述了如何使用@ModelAttribute
支持客戶端瀏覽器的多次表單提交請(qǐng)求。對(duì)于不是使用的瀏覽器的客戶端,我們也推薦使用這個(gè)注解來(lái)處理請(qǐng)求。但當(dāng)請(qǐng)求是一個(gè)HTTP PUT方法的請(qǐng)求時(shí),有一個(gè)事情需要注意。瀏覽器可以通過(guò)HTTP的GET方法或POST方法來(lái)提交表單數(shù)據(jù),非瀏覽器的客戶端還可以通過(guò)HTTP的PUT方法來(lái)提交表單。這就設(shè)計(jì)是個(gè)挑戰(zhàn),因?yàn)樵赟ervlet規(guī)范中明確規(guī)定,ServletRequest.getParameter*()
系列的方法只能支持通過(guò)HTTP POST方法的方式提交表單,而不支持HTTP PUT的方式。
為了支持HTTP的PUT類型和PATCH類型的請(qǐng)求,Spring的spring-web
模塊提供了一個(gè)過(guò)濾器HttpPutFormContentFilter
。你可以在web.xml
文件中配置它:
<filter>
<filter-name>httpPutFormFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpPutFormFilter</filter-name>
<servlet-name>dispatcherServlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
上面的過(guò)濾器將會(huì)攔截內(nèi)容類型(content type)為application/x-www-form-urlencoded
、HTTP方法為PUT或PATCH類型的請(qǐng)求,然后從請(qǐng)求體中讀取表單數(shù)據(jù),把它們包裝在ServletRequest
中。這是為了使表單數(shù)據(jù)能夠通過(guò)ServletRequest.getParameter*()
系列的方法來(lái)拿到。
因?yàn)?code>HttpPutFormContentFilter會(huì)消費(fèi)請(qǐng)求體的內(nèi)容,因此,它不應(yīng)該用于處理那些依賴于其他
application/x-www-form-urlencoded
轉(zhuǎn)換器的PUT和PATCH請(qǐng)求,這包括了@RequestBodyMultiValueMap<String, String>
和HttpEntity<MultiValueMap<String, String>>
。
@CookieValue
注解能將一個(gè)方法參數(shù)與一個(gè)HTTP cookie的值進(jìn)行綁定。
看一個(gè)這樣的場(chǎng)景:以下的這個(gè)cookie存儲(chǔ)在一個(gè)HTTP請(qǐng)求中:
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
下面的代碼演示了拿到JSESSIONID
這個(gè)cookie值的方法:
@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) {
//...
}
若注解的目標(biāo)方法參數(shù)不是String
類型,則類型轉(zhuǎn)換會(huì)自動(dòng)進(jìn)行。詳見(jiàn)"方法參數(shù)與類型轉(zhuǎn)換"一節(jié)。
這個(gè)注解可以注解到處理器方法上,在Servlet環(huán)境和Portlet環(huán)境都能使用。
@RequestHeader
注解映射請(qǐng)求頭屬性@RequestHeader
注解能將一個(gè)方法參數(shù)與一個(gè)請(qǐng)求頭屬性進(jìn)行綁定。
以下是一個(gè)請(qǐng)求頭的例子:
Host localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
以下的代碼片段展示了如何取得Accept-Encoding
請(qǐng)求頭和Keep-Alive
請(qǐng)求頭的值:
@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
若注解的目標(biāo)方法參數(shù)不是String
類型,則類型轉(zhuǎn)換會(huì)自動(dòng)進(jìn)行。"方法參數(shù)與類型轉(zhuǎn)換"一節(jié)。
如果@RequestHeader
注解應(yīng)用在Map<String, String>
、MultiValueMap<String, String>
或HttpHeaders
類型的參數(shù)上,那么所有的請(qǐng)求頭屬性值都會(huì)被填充到map中。
Spring內(nèi)置支持將一個(gè)逗號(hào)分隔的字符串(或其他類型轉(zhuǎn)換系統(tǒng)所能識(shí)別的類型)轉(zhuǎn)換成一個(gè)String類型的列表/集合。舉個(gè)例子,一個(gè)注解了
@RequestHeader("Accept")
的方法參數(shù)可以是一個(gè)String
類型,但也可以是String[]
或List<String>
類型的。
這個(gè)注解可以注解到處理器方法上,在Servlet環(huán)境和Portlet環(huán)境都能使用。
從請(qǐng)求參數(shù)、路徑變量、請(qǐng)求頭屬性或者cookie中抽取出來(lái)的String
類型的值,可能需要被轉(zhuǎn)換成其所綁定的目標(biāo)方法參數(shù)或字段的類型(比如,通過(guò)@ModelAttribute
將請(qǐng)求參數(shù)綁定到方法參數(shù)上)。如果目標(biāo)類型不是String
,Spring會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換。所有的簡(jiǎn)單類型諸如int、long、Date都有內(nèi)置的支持。如果想進(jìn)一步定制這個(gè)轉(zhuǎn)換過(guò)程,你可以通過(guò)WebDataBinder
(詳見(jiàn)"定制WebDataBinder的初始化"一節(jié)),或者為Formatters
配置一個(gè)FormattingConversionService
(詳見(jiàn)8.6節(jié) "Spring字段格式化"一節(jié))來(lái)做到。
如果想通過(guò)Spring的WebDataBinder
在屬性編輯器中做請(qǐng)求參數(shù)的綁定,你可以使用在控制器內(nèi)使用@InitBinder
注解的方法、在注解了@ControllerAdvice
的類中使用@InitBinder
注解的方法,或者提供一個(gè)定制的WebBindingInitializer
。更多的細(xì)節(jié),請(qǐng)參考使用@ControllerAdvice輔助控制器一節(jié)。
使用@InitBinder
注解控制器的方法,你可以直接在你的控制器類中定制應(yīng)用的數(shù)據(jù)綁定。@InitBinder
用來(lái)標(biāo)記一些方法,這些方法會(huì)初始化一個(gè)WebDataBinder
并用以為處理器方法填充命令對(duì)象和表單對(duì)象的參數(shù)。
除了命令/表單對(duì)象以及相應(yīng)的驗(yàn)證結(jié)果對(duì)象,這樣的“綁定器初始化”方法能夠接收@RequestMapping
所支持的所有參數(shù)類型?!敖壎ㄆ鞒跏蓟狈椒ú荒苡蟹祷刂?,因此,一般將它們聲明為void
返回類型。特別地,當(dāng)WebDataBinder
與WebRequest
或java.util.Locale
一起作為方法參數(shù)時(shí),你可以在代碼中注冊(cè)上下文相關(guān)的編輯器。
下面的代碼示例演示了如何使用@InitBinder
來(lái)配置一個(gè)CustomerDateEditor
,后者會(huì)對(duì)所有java.util.Date
類型的表單字段進(jìn)行操作:
@Controller
public class MyFormController {
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
// ...
}
或者,你可以使用Spring 4.2提供的addCustomFormatter
來(lái)指定Formatter
的實(shí)現(xiàn),而非通過(guò)PropertyEditor
實(shí)例。這在你擁有一個(gè)需要Formatter
的setup方法,并且該方法位于一個(gè)共享的FormattingConversionService
中時(shí)非常有用。這樣對(duì)于控制器級(jí)別的綁定規(guī)則的定制,代碼更容易被復(fù)用。
@Controller
public class MyFormController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
// ...
}
為了externalize數(shù)據(jù)綁定的初始化過(guò)程,你可以為WebBindingInitializer
接口提供一個(gè)自己的實(shí)現(xiàn),在其中你可以為AnnotationMethodHandlerAdapter
提供一個(gè)默認(rèn)的配置bean,以此來(lái)覆寫默認(rèn)的配置。
以下的代碼來(lái)自PetClinic的應(yīng)用,它展示了為WebBindingInitializer
接口提供一個(gè)自定義實(shí)現(xiàn):org.springframework.samples.petclinic.web.ClinicBindingInitializer
完整的配置過(guò)程。后者中配置了PetClinic應(yīng)用中許多控制器所需要的屬性編輯器PropertyEditors。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="cacheSeconds" value="0"/>
<property name="webBindingInitializer">
<bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer"/>
</property>
</bean>
@InitBinder
方法也可以定義在@ControllerAdvice
注解的類上,這樣配置可以為許多控制器所共享。這提供了除使用WebBindingInitializer
外的另外一種方法。更多細(xì)節(jié)請(qǐng)參考使用@ControllerAdvice輔助控制器一節(jié)。
@ControllerAdvice
是一個(gè)組件注解,它使得其實(shí)現(xiàn)類能夠被classpath掃描自動(dòng)發(fā)現(xiàn)。若應(yīng)用是通過(guò)MVC命令空間或MVC Java編程方式配置,那么該特性默認(rèn)是自動(dòng)開啟的。
注解@ControllerAdvice
的類可以擁有@ExceptionHandler
、@InitBinder
或@ModelAttribute
注解的方法,并且這些方法會(huì)被應(yīng)用至控制器類層次??的所有@RequestMapping
方法上。
你也可以通過(guò)@ControllerAdvice
的屬性來(lái)指定其只對(duì)一個(gè)子集的控制器生效:
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class BasePackageAdvice {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class AssignableTypesAdvice {}
更多的細(xì)節(jié),請(qǐng)查閱@ControllerAdvice
的文檔。
下面兩節(jié),還看不太懂,待譯。
It can sometimes be useful to filter contextually the object that will be serialized to the HTTP response body. In order to provide such capability, Spring MVC has built-in support for rendering with Jackson's Serialization Views.
To use it with an @ResponseBody
controller method or controller methods that return ResponseEntity
, simply add the @JsonView
annotation with a class argument specifying the view class or interface to be used:
_@RestController_
public class UserController {
_@RequestMapping(path = "/user", method = RequestMethod.GET)_
_@JsonView(User.WithoutPasswordView.class)_
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
_@JsonView(WithoutPasswordView.class)_
public String getUsername() {
return this.username;
}
_@JsonView(WithPasswordView.class)_
public String getPassword() {
return this.password;
}
}
Note | |
---|---|
Note that despite @JsonView
allowing for more than one class to be specified, the use on a controller method is only supported with exactly one class argument. Consider the use of a composite interface if you need to enable multiple views.
For controllers relying on view resolution, simply add the serialization view class to the model:
_@Controller_
public class UserController extends AbstractController {
_@RequestMapping(path = "/user", method = RequestMethod.GET)_
public String getUser(Model model) {
model.addAttribute("user", new User("eric", "7!jd#h23"));
model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);
return "userView";
}
}
In order to enable JSONP support for @ResponseBody
and ResponseEntity
methods, declare an @ControllerAdvice
bean that extends AbstractJsonpResponseBodyAdvice
as shown below where the constructor argument indicates the JSONP query parameter name(s):
_@ControllerAdvice_
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
For controllers relying on view resolution, JSONP is automatically enabled when the request has a query parameter named jsonp
or callback
. Those names can be customized through jsonpParameterNames
property.
更多建議: