SpringMVC 教程 - Handler Method

原文連接:https://www.codemore.top/cates/Backend/post/2018-04-21/spring-mvc-handler-methodshtml

由註解@RequestMapping註解修飾的處理請求的函數的簽名很是的靈活,可使用controller函數支持的一系列參數和返回值。java

函數參數

下列表格列出了controller方法能夠接受的參數,稍後會對其進行詳細的解釋。 對於 JDK 8的java.util.Optional 能夠在包含required屬性的註解中使用,例如:@RequestParam,@RequestHeader等,至關於required=falseweb

函數參數 解釋
WebRequest,NativeWebRequest 無需直接使用Servlet API來訪問請求參數,請求屬性和session的屬性。
javax.servlet.ServletRequest,javax.servlet.ServletResponse 能夠是指定的請求和響應類型例如ServletRequestHttpServletRequest,也能夠是Spring的MultipartRequest,MultipartHttpServletRequest
HttpSession 參數永不爲null,訪問session非線程安全,若是多個請求訪問一個session,須要設置RequestMappingHandlerAdaptersynchronizeOnSession爲true。
PushBuilder Servlet 4.0 支持HTTP/2 push的API,若是客戶端不支持則爲null
Principal 當前受權用戶。
HttpMethod 請求的HTTP方法
Locale 當前請求的區域,由LocaleResolver解析
TimeZone,ZoneId LocaleContextResolver解析的當前請求的時區
InputStream,Reader 訪問由Servlet API暴露的請求體
OutputStream,Writer 訪問由Servlet API 暴露的響應體
@PathVairable 訪問URI變量
@MatrixVariable 訪問URI中的name-value值。例如 pets/42;q=11 @MatrixVariable int q
@Requestparam 訪問請求中參數
@RequestHeader 訪問請求頭
@CookieValue 訪問cookie值
@RequestBody 訪問請求體,將請求體轉換爲相應類型
HttpEntity<B> 訪問請求頭和請求體
Map,Model,ModelMap 訪問會在渲染模板時使用的變量。
RedirectAttributes 重定向時使用的屬性
@ModelAttribute 訪問model中的屬性,同時進行數據綁定和校驗
Errors, BindingResult 訪問在數據綁定和校驗是出現的錯誤。

類級別的 @SessionAttributes,SessionStatus | 在不一樣的請求中存儲session UriComponentsBuilder | 相對於當前請求的host,port,scheme等 @SessionAttribute | 訪問session中的屬性 @RequestAttribute| 訪問請求的屬性。 其餘類型 | 若是參數非上述類型,那麼將當成@RequestParam來處理spring

返回值

下列表格列出了支持的返回類型json

返回值類型 解釋
@ResponseBody 返回值由HttpMessageConverters轉換,直接寫到響應體
HttpEntity<B>ResponseEntity<B> 返回值包括,http header和body
HttpHeaders 只返回HTTP header
String ViewResolver解析出具體的模板渲染。
View 返回具體的視圖
Map,Model model包含的屬性,視圖由RequestToViewNameTranslator解析
@ModelAttribute 返回添加到Model的屬性,視圖由RequestToViewNameTranslator解析.
ModelAndView 返回具體視圖和添加的model
void 返回void,則Spring MVC會認爲Controller內部已經處理好響應內容了。
DeferredResult<V> 異步返回結果,能夠由任意線程處理
Callback<V> 異步返回,現成由Spring MVC管理
ListenableFuture<V>,CompletionStage<V>,CompletableFuture<V> DefferedResult
ResponseBodyEmitter,SseEmitter 使用HttpMessageConverter異步將對象以流的方式發到響應。
StreamingResponseBody 異步將響應發送的輸出流
Reactor, RxJava, 等Reactive類型 DeferredResult
其餘類型 若是不返回以上類型,默認看成視圖名稱處理。
類型轉換

一些須要參數的註解,例如@RequestParam,@RequestHeader,@PathVariabl,@MatrixVariable@CookieValue,若是他麼的參數並不是String,那麼久須要進行類型轉換。 類型轉換自動由Spring MVC中註冊的轉換器來進行轉換,默認狀況下支持,int,long,Date等簡單類型。對於不支持的類型能夠經過WebDataBinder或者由FormattingConversionService註冊的Formatter來進行轉換。瀏覽器

Matrix 變量

RFC 3986規定了在路徑中添加name-value對。在Spring MVC中,將其定義爲matrix變量。 Matrix變量能夠出如今任意的路徑中,每一個變量由分號隔開,多個值由逗號隔開,例如:/cars;color=red,green;year=2012。多個值一樣可也但是經過分離名字來指定,例如:color=red;color=green。 若是想要在路徑中添加Matrix變量,那麼就必須保證相應的controller方法包含接收matrix變量,而且請求映射不收到Matrix變量的影響。例如:spring-mvc

// GET /pets/42;q=11;r=22 @GetMapping("/pets/{petId}") public void findPet(@PathVariable String petId, @MatrixVariable int q) { // petId == 42 // q == 11 }

由於全部的路徑都有可能包辦Matrix變量,能夠經過指定路徑的形式分辨某個Matrix變量屬於哪一個路徑例如:安全

@GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable(name="q", pathVar="ownerId") int q1, @MatrixVariable(name="q", pathVar="petId") int q2) { // q1 == 11 // q2 == 22 }

Matrix變量能夠是可選的,指定默認值.例如:cookie

// GET /pets/42 @GetMapping("/pets/{petId}") public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { // q == 1 }

可使用MultiValueMap獲取全部的Matrix變量,例如:session

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 @GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable MultiValueMap<String, String> matrixVars, @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) { // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] // petMatrixVars: ["q" : 22, "s" : 23] }

默認狀況下Spring MVC是不啓用Matrix變量的,若是是用Java配置,能夠經過配置UrlPathHelperremoveSemicolonContent=false啓用,若是是使用XML配置,可使用<mvc:annotation-driven enable-matrix=variable="true"/>啓用。

@RequestParam

@RequestParam能夠將Servlet請求參數綁定到controller函數中的變量.例如:

@Controller @RequestMapping("/pets") public class EditPetForm { // ... @GetMapping public String setupForm(@RequestParam("petId") int petId, Model model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ... }

@RequestParam的變量required 默認狀況下是true,若是不但願必須指定某個參數能夠設置required=false或者若是使用Java 8 可使用java.util.Optional。 若是函數的參數非String類型,那麼將會進行自動類型轉換。 若是@RequsetParam修飾的是Map<String,String>或者MultiValueMap<String,String>那麼就會獲取全部的請求參數。

@RequestHeader

@RequestHeader將header的值綁定到controller的方法參數中。 例如一下做爲請求header:

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-EncodingKeep-Aliveheader

@GetMapping("/demo") public void handle( @RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) { //... }

一樣,若是參數非String類型,也會自動進行類型轉換,若是修飾的是Map<String,String>,MultiValueMap<String,String>或者HttpHeaders,也是獲取全部的header值

@CookieValue

使用@CookieValue將cookie值綁定到controller的方法參數中 例如如下cookie:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

下列代碼便可獲取:

@GetMapping("/demo") public void handle(@CookieValue("JSESSIONID") String cookie) { //... }

一樣的,若是參數類型非String,會自動進行類型轉換。

@ModelAttribute

使用@ModelAttribute修飾的函數參數能夠訪問Model中的屬性,或者其未初始化是初始化。方法參數名和請求參數名相同,model 屬性一樣也能夠覆蓋其請求參數,這樣就不須要本身再從請求參數中解析了。例如:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@ModelAttribute Pet pet) { }

Pet 示例的獲取:

  • 若是Model中存在,則從Model中解析
  • 經過@SessionAttributes獲取
  • 從URI的路徑變量中獲取
  • 經過默認的構造函數獲取
  • 經過和Servlet請求參數相匹配的帶參數的構造函數獲取。參數名由@ConstructorProperties獲取或者字節碼獲取。

固然通常都是使用Model來填充其值的,另外一個選擇使用URI的路徑變量,其值經過註冊的Converter<String,T>轉換。下面這個例子就是@ModelAttribute修飾的值和路徑匹配,經過Converter<String,Account>進行類型轉換。

@PutMapping("/accounts/{account}") public String save(@ModelAttribute("account") Account account) { // ... }

獲取了屬性值的實例後就能夠開始進行數據綁定了。WebDataBinder類經過匹配Servlet 的請求參數名(查詢參數和form字段)來將字段名對應到對象中。當類型轉換完以後填充匹配的字段。DataBinderValidation將在後面章節詳細描述。 數據綁定是會產生錯誤的,默認狀況下會拋出BindException異常,爲了在controller的方法中捕獲這個異常,能夠在方法參數中加入BindingResult獲取異常。例如:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }

某些狀況下只想要訪問屬性之而不須要數據綁定。這種狀況下能夠將設置@ModelAttribute(binding=false)。例如:

@ModelAttribute public AccountForm setUpForm() { return new AccountForm(); } @ModelAttribute public Account findAccount(@PathVariable String accountId) { return accountRepository.findOne(accountId); } @PostMapping("update") public String update(@Valid AccountUpdateForm form, BindingResult result, @ModelAttribute(binding=false) Account account) { // ... }

添加javax.util.validation.Valid或者Spring的@Validated註解,在數據綁定完成後會自動校驗。例如:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }
@SessionAttributes

@SessionAttributes用於在不一樣的請求間存儲Servlet session的屬性值。主要是列出須要在接下來的請求訪問的session的值自動的保存到session中。例如:

@Controller @SessionAttributes("pet") public class EditPetForm { // ... }

第一次請求後這個帶有pet名字的屬性值將會自動的存到session中。直到另一個帶有SessionStatus參數的方法將其清除。例如:

@Controller @SessionAttributes("pet") public class EditPetForm { // ... @PostMapping("/pets/{id}") public String handle(Pet pet, BindingResult errors, SessionStatus status) { if (errors.hasErrors) { // ... } status.setComplete(); // ... } } }
@SessionAttribute

若是想要訪問一個以前存在的session的屬性,可使用@SessionAttribute訪問。例如:

@RequestMapping("/") public String handle(@SessionAttribute User user) { // ... }

請他狀況下,須要添加或者刪除session的時候,能夠經過注入org.springframework.web.context.request.WebRequest或者javax.servlet.http.HttpSession實現Session的管理。

@RequestAttribute

@SessionAttribute類似,@RequestAttribute能夠訪問請求以前(例如,Filter,HandlerInterceptor)建立的請求屬性。例如:

@GetMapping("/") public String handle(@RequestAttribute Client client) { // ... }
重定向屬性值

默認狀況下,在重定向url中全部的屬性值都經過URI的模版變量暴露。 例如:

@PostMapping("/files/{path}") public String upload(...) { // ... return "redirect:files/{path}"; }
Flash屬性值

Flash屬性值能夠保存一個請求的數據使得另外一個請求可使用他的數據。最經常使用的場景就是重定向,例如:Post/Redirect/Get模式。在重定向以前臨時將Flash屬性保存(通常保存在session中)。這樣在另外一個請求中就能夠獲取保存值,以後就會被當即刪除。 Spring MVC 經過FlashMapFlashMapManager支持Flash屬性。FlashMap保存值,FlashMapManager用來保存,查詢,管理FlashMap實例。 Flash屬性默認開啓,若是不使用則不會建立HTTP session。對於每一個請求來講都有一個input的FlashMap,包含了上一個請求傳遞的屬性和一個output的FlashMap包含須要傳遞的屬性。這兩個FlashMap均可以經過RequestContextUtils中的靜態方法來獲取。 通常來說controller不會直接使用FlashMap。其方法參數RedirectAttributes默認狀況下使用flash map存儲須要重定向的數據,保存到output的FlashMap中,重定向後,自動從input的FlashMap中獲取數據添加到Model中。

Multipart

啓用MultipartResolver後,若是POST請求包含了multipart/form-data,則其將會解析請求參數,獲取Multipart。下面是上次文件的示例:

@Controller public class FileUploadController { @PostMapping("/form") public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; } }

若是使用Servlet 3.0 則能夠用javax.servlet.http.Part代替Spring的MultipartFile。 Multipart 的內容一樣能夠做爲數據綁定的一部分,例如:

class MyForm { private String name; private MultipartFile file; // ... } @Controller public class FileUploadController { @PostMapping("/form") public String handleFormUpload(MyForm form, BindingResult errors) { if (!form.getFile().isEmpty()) { byte[] bytes = form.getFile().getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; } }

Multipart請求一樣能夠經過非瀏覽器提交,例如:下面是一個JSON的示例:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data" Content-Type: application/json; charset=UTF-8 Content-Transfer-Encoding: 8bit { "name": "value" } --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="file-data"; filename="file.properties" Content-Type: text/xml Content-Transfer-Encoding: 8bit ... File Data ...

能夠經過@RequestParam來獲取元信息,可是更好的作法是使用@RequestPart來獲取其元信息。例如:

@PostMapping("/") public String handle(@RequestPart("meta-data") MetaData metadata, @RequestPart("file-data") MultipartFile file) { // ... }

@RequestPart能夠和javax.validation.Valid或者Spring的@Validated註解一同使用,經過標準的bean驗證來校驗數據的準確性。默認狀況下校驗錯誤拋出MethodArgumentNotValidException的異常,會直接返回404的錯誤。一樣能夠經過BindingResult來本身處理異常狀況。

@RequestBody

使用了@RequestBody的參數經過HttpMessageConverter來將請求體反序列化成一個對象。下面是使用@RequestBody的示例:

@PostMapping("/accounts") public void handle(@RequestBody Account account) { // ... }

@RequestBody一樣能夠和javax.validation.Valid或者Spring的@Validated註解一同使用。默認拋出的異常是MethodArgumentNotValidException處理方法同@RequestPart

@PostMapping("/accounts") public void handle(@Valid @RequestBody Account account, BindingResult result) { // ... }
HttpEntity

HttpEntity的使用和@RequestBody類似,不過他能夠同時包含header和body。使用方法以下:

@PostMapping("/accounts") public void handle(HttpEntity<Account> entity) { // ... }
@ReponseBody

在方法中使用@ResponseBody修飾,則會自動的將返回值經過HttpMessageConverter的轉換寫入到響應體中。 使用方法以下:

@GetMapping("/accounts/{id}") @ResponseBody public Account handle() { // ... }

@ResponseBody一樣支持類級別,若是修飾controller類,那麼全部的方法都會繼承這個註解。這個和@RestController同樣,@RestController就是@Controller@RequestBody的組合。

ResponseEntity

ResponseEntity@ResponseBody類似,只是其同時包含了響應的header和body。使用以下:

@PostMapping("/something") public ResponseEntity<String> handle() { // ... URI location = ... ; return ResponseEntity.created(location).build(); }
Jackson JSON

Spring MVC 內監了對Jackson序列化視圖的支持。在使用ResponseEntity@ResponseBody的時候可使用@JsonView來啓動序列化的視圖類。使用以下:

public class UserController { @GetMapping("/user") @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; } }

若是controller的方法返回的是一個字符串的視圖,能夠將其放到model中啓用:

@Controller public class UserController extends AbstractController { @GetMapping("/user") public String getUser(Model model) { model.addAttribute("user", new User("eric", "7!jd#h23")); model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class); return "userView"; } }
Jackson JSONP

爲了開啓@ResponseBodyResonseEntity的JSONP的支持,能夠經過定義一個@ControllerAdvice的bean繼承AbstractJsonpResponseBodyAdvice,其默認構造參數就是JSONP的查詢參數,使用以下:

@ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
相關文章
相關標籤/搜索