SpringBoot學習筆記(三)——Spring MVC控制器、 Restful、Swagger
一、Spring MVC控制器
1.1、控制器
控制器提供訪問應用程式的行為,通常通過服務介面定義或註解定義兩種方法實現。 控制器解析使用者的請求並將其轉換為一個模型。在Spring MVC中一個控制器可以包含多個Action(動作、方法)。
註解有三種:
@Controller | 處理http請求 |
@RestController | spring4之後新加的註解,原來返回json需要@ResponseBody配合@Controller |
@RequestMapping | 配置url對映(從請求url(可能還包括請求方法、引數(pathvariable或parameter)等到控制器及對應方法的對映)) |
使用註解@Controller定義控制器。org.springframework.stereotype.Controller註解型別用於宣告Spring類的例項是一個控制器(在講IOC時還提到了另外3個註解);Spring可以使用掃描機制來找到應用程式中所有基於註解的控制器類,為了保證Spring能找到你的控制器,需要在配置檔案中宣告元件掃描。
建立一個名了Bar的類,定義為一個控制器,類的具體實現如下:
package com.zhangguo.springmvc02.controllers; import org.springframework.stereotype.Controller;import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; /** * 定義控制器 */ //BarController類的例項是一個控制器,會自動新增到Spring上下文中 @Controller public class BarController { //對映訪問路徑 @RequestMapping("/bar") public String index(Model model){ //Spring MVC會自動例項化一個Model物件用於向檢視中傳值model.addAttribute("message", "這是通過註解定義的一個控制器中的Action"); //返回檢視位置 return "foo/index"; } }
1.2、@RequestMapping
@RequestMapping註釋用於對映url到控制器類或一個特定的處理程式方法。可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。該註解共有8個屬性,註解原始碼如下:
package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.Callable; import org.springframework.core.annotation.AliasFor; /** * 用於對映url到控制器類或一個特定的處理程式方法. */ //該註解只能用於方法或型別上 @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { /** * 指定對映的名稱 */ String name() default ""; /** * 指定請求的路徑對映,指定的地址可以是uri模板,別名為path */ @AliasFor("path") String[] value() default {}; /** 別名為value,使用path更加形象 * 只有用在一個Servlet環境:路徑對映URI(例如“/myPath.do”)。 * Ant風格的路徑模式,同時也支援(例如,“/myPath/*.do”)。在方法層面,在主要的對映在型別級別表示相對路徑(例如,“edit.do”) * 的支援。路徑對映的URI可能包含佔位符(例如“/$ {}連線”) */ @AliasFor("value") String[] path() default {}; /** * 指定請求謂詞的型別如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. 收窄請求範圍 The * HTTP request methods to map to, narrowing the primary mapping: GET, POST, * HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. */ RequestMethod[] method() default {}; /** * 對映請求的引數,收窄請求範圍 The parameters of the mapped request, narrowing the * primary mapping. */ String[]params() default {}; /** * 對映請求頭部,收窄請求範圍 The headers of the mapped request, narrowing the primary * mapping. RequestMapping(value = "/something", headers = * "content-type=text/*") */ String[] headers() default {}; /** * 指定處理請求的提交內容型別(Content-Type),例如application/json, text/html,收窄請求範圍 The * consumable media types of the mapped request, narrowing the primary * mapping. */ String[] consumes() default {}; /** * 指定返回的內容型別,僅當request請求頭中的(Accept)型別中包含該指定型別才返回 The producible media types * of the mapped request, narrowing the primary mapping. produces = * "text/plain" produces = {"text/plain", "application/*"} produces = * "application/json; charset=UTF-8" */ String[] produces() default {}; }
從上面的原始碼可以發現除了name基本都是陣列型別,在設定時我們可以指定單個值,如@RequestMapping(value="/foo");也可以同時指定多個值如:@RequestMapping(value={"/foo","/bar"})。
1.2.1、value 屬性指定對映路徑或URL模板
指定請求的實際地址,指定的地址可以是URL模板,正則表示式或路徑佔位,該屬性與path互為別名關係,@RequestMapping("/foo")} 與 @RequestMapping(path="/foo")相同。該屬性是使用最頻繁,最重要的一個屬性,如果只指定該屬性時可以把value略去。Spring Framework 4.2引入了一流的支援宣告和查詢註釋屬性的別名。@AliasFor註解可用於宣告一雙別名屬性,來給註解的屬性起別名, 讓使用註解時, 更加的容易理解(比如給value屬性起別名, 更容易讓人理解)。先看一個官網的示例:
@Controller @RequestMapping("/appointments") public class AppointmentsController { private final AppointmentBook appointmentBook; @Autowired public AppointmentsController(AppointmentBook appointmentBook) { this.appointmentBook = appointmentBook; } @RequestMapping(method = RequestMethod.GET) public Map<String, Appointment> get() { return appointmentBook.getAppointmentsForToday(); } @RequestMapping(value = "/{day}", method = RequestMethod.GET) public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso = ISO.DATE) Date day, Model model) { return appointmentBook.getAppointmentsForDay(day); } @RequestMapping(value = "/new", method = RequestMethod.GET) public AppointmentForm getNewForm() { return new AppointmentForm(); } @RequestMapping(method = RequestMethod.POST) public String add(@Valid AppointmentForm appointment, BindingResult result) { if (result.hasErrors()) { return "appointments/new"; } appointmentBook.addAppointment(appointment); return "redirect:/appointments"; } }
1.2.2、指定具體路徑字元
1.2.2.1 只註解方法
@Controller public class FooBarController { @RequestMapping("/action1") public String action1(){ return "foo/index"; } }
訪問路徑:http://localhost:8087/SpringMVC02/action1
1.2.2.2 同時註解類與方法
@Controller @RequestMapping("/foobar") public class FooBarController { @RequestMapping("/action1") public String action1(){ return "foo/index"; } }
訪問路徑:http://localhost:8087/SpringMVC02/foobar/action1
需要先指定類的路徑再指定方法的路徑
1.2.2.3 當value為空值
註解在方法上時,如果value為空則表示該方法為類下預設的Action。
@Controller @RequestMapping("/foobar") public class FooBarController { @RequestMapping("/action1") public String action1(Model model){ //在模型中新增屬性message值為action1,渲染頁面時使用 model.addAttribute("message", "action1"); return "foo/index"; } @RequestMapping public String action2(Model model){ //在模型中新增屬性message值為action2,渲染頁面時使用 model.addAttribute("message", "action2"); return "foo/index"; } }
訪問action2的路徑是:http://localhost:8087/SpringMVC02/foobar,如果加上action2就錯誤了。
註解在類上時,當value為空值則為預設的控制器,可以用於設定專案的起始頁。
@Controller @RequestMapping public class FooBarController { @RequestMapping("/action1") public String action1(Model model){ //在模型中新增屬性message值為action1,渲染頁面時使用 model.addAttribute("message", "action1"); return "foo/index"; } @RequestMapping public String action2(Model model){ //在模型中新增屬性message值為action2,渲染頁面時使用 model.addAttribute("message", "action2"); return "foo/index"; } }
訪問路徑:http://localhost:8087/SpringMVC02/,同時省去了控制器名與Action名稱,可用於歡迎頁。
訪問action1的路徑是:http://localhost:8087/SpringMVC02/action1
1.2.3、路徑變數佔位,URI模板模式
在Spring MVC可以使用@PathVariable 註釋方法引數的值繫結到一個URI模板變數。
@RequestMapping("/action3/{p1}/{p2}") public int action1(@PathVariable("p1") int n1, @PathVariable("p2")int n2){ return n1+n2; }
執行結果:
使用路徑變數的好處:使路徑變得更加簡潔;獲得引數更加方便,框架會自動進行型別轉換。通過路徑變數的型別可以約束訪問引數,如果型別不一樣,則訪問不到action,如這裡訪問是的路徑是/action3/1/a,則路徑與方法不匹配,而不會是引數轉換失敗。
1.2.4、正則表示式模式的URI模板
@RequestMapping(value="/action4/{id:\\d{6}}-{name:[a-z]{3}}") public String action4(@PathVariable int id,@PathVariable String name){ }
正則要求id必須為6位的數字,而name必須為3位小寫字母,訪問結果如下:
1.2.5、Ant風格路徑模式
@RequestMapping註解也支援ant風格的路徑模式,如/myPath/*.do,/owners/*/pets/{petId},示例程式碼如下:
//Ant風格路徑模式 @RequestMapping(value = "/action6/*.do") public String action6(){ }
執行結果:
當然還有關於路徑匹配的規則,特殊的優先順序高過一般的,更多規則可以參考官方幫助。
ANT萬用字元有三種:
1.2.6、@RequestMapping 來處理多個 URI
你可以將多個請求對映到一個方法上去,只需要新增一個帶有請求路徑值列表的 @RequestMapping 註解就行了。
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping(value = { "", "/page", "page*", "view/*",
"**/msg" }) String indexMultipleMapping() { return "Hello from index multiple mapping."; } }
如你在這段程式碼中所看到的,@RequestMapping 支援統配符以及ANT風格的路徑。前面這段程式碼中,如下的這些 URL 都會由 indexMultipleMapping() 來處理:
localhost:8080/home
localhost:8080/home/
localhost:8080/home/page
localhost:8080/home/pageabc
localhost:8080/home/view/
localhost:8080/home/view/view
1.2.7、method屬性指定謂詞型別
用於約束請求的謂詞型別,可以收窄請求範圍。指定請求謂詞的型別如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE,如下程式碼所示:
//謂詞型別 @RequestMapping(value = "/action6",method={RequestMethod.POST,RequestMethod.DELETE}) public String action6(Model model) { }
要訪問action7請求謂詞型別必須是POST或者為DELETE,當我們從瀏覽器的URL欄中直接請求時為一個GET請求,則結果是405,如下所示:
如果將POST修改為GET則正常了,如下所示:
//謂詞型別 @RequestMapping(value = "/action6",method=RequestMethod.GET) public String action6(Model model) { model.addAttribute("message", "請求謂詞只能是GET"); return "foo/index"; }
Spring MVC 的 @RequestMapping 註解能夠處理 HTTP 請求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的請求預設都會是 HTTP GET 型別的。
為了能降一個請求對映到一個特定的 HTTP 方法,你需要在 @RequestMapping 中使用 method 來宣告 HTTP 請求所使用的方法型別,如下所示:
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping(method = RequestMethod.GET) String get() { return "Hello from get"; } @RequestMapping(method = RequestMethod.DELETE) String delete() { return "Hello from delete"; } @RequestMapping(method = RequestMethod.POST) String post() { return "Hello from post"; } @RequestMapping(method = RequestMethod.PUT) String put() { return "Hello from put"; } @RequestMapping(method = RequestMethod.PATCH) String patch() { return "Hello from patch"; } }
在上述這段程式碼中, @RequestMapping 註解中的 method 元素聲明瞭 HTTP 請求的 HTTP 方法的型別。
所有的處理處理方法會處理從這同一個 URL( /home)進來的請求, 但要看指定的 HTTP 方法是什麼來決定用哪個方法來處理。
例如,一個 POST 型別的請求 /home 會交給 post() 方法來處理,而一個 DELETE 型別的請求 /home 則會由 delete() 方法來處理。
你會看到 Spring MVC 將使用這樣相同的邏輯來對映其它的方法。
1.2.8、consumes屬性指定請求的Content-Type
@RequestMapping 註解的 produces 和 consumes 這兩個元素來縮小請求對映型別的範圍,達到處理生產和消費物件的目的。
指定處理請求的提交內容型別(Content-Type),例如application/json, text/html,收窄請求範圍,如果使用者傳送的請求內容型別不匹配則方法不會響應請求,具體使用如下程式碼所示:
package com.zhangguo.springmvc02.controllers; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/home") public class HomeController { // 請求內容型別必須為text/html,注意瀏覽器預設沒有指定Content-type @RequestMapping(value = "/action8",consumes="text/html") public String action8(Model model) { model.addAttribute("message", "請求的提交內容型別(Content-Type)是text/html"); return "foo/index"; } }
在action8的註解中約束髮送到伺服器的Content-Type必須是text/html型別,如果型別不一致則會報錯(415),測試結果如下:
從兩個圖的對比可以看出當內容型別為text/plain時報客戶端錯誤415,當內容型別為text/html時則響應正常,響應的結果如下:
請求的提交內容型別(Content-Type)是text/html
注意:可以使用!號,如consumes="!text/html"
1.2.9、produces屬性指定響應的Content-Type,約束Accept型別
指定返回的內容型別,僅當request請求頭中的(Accept)型別中包含該指定型別才返回,方法才處理客戶端的請求否則會報406錯誤,常用設定如下:
produces = "text/plain" //客戶端只接收純文字
produces = {"text/plain", "application/*"} //客戶端接收純文字與application/*型別的內容
produces = "application/json; charset=UTF-8" //客戶端接收json且編碼為utf-8
//客戶端接收json且編碼為utf-8,多數瀏覽器Accept設定的為*/*,接收任意型別 @RequestMapping(value = "/action9",produces="application/json; charset=UTF-8") public String action9(Model model) { model.addAttribute("message", "客戶端可以接收的型別是application/json; charset=UTF-8"); return "foo/index"; }
執行結果:
注意:可以使用!號,如produces="!text/html"
1.2.10、params屬性指定請求中必須有特定引數與值
對映請求的引數,收窄請求範圍。可以限制客戶端傳送到伺服器的請求引數為某些特定值或不為某些值,如下程式碼所示:
//請求的引數必須包含id=215與name不等於abc @RequestMapping(value = "/action10",params={"id=215","name!=abc"}) public String action10(Model model) { model.addAttribute("message", "請求的引數必須包含id=215與name不等於abc"); return "foo/index"; }
執行結果如下:
name的值如沒有指定也是通過的;可以使用不等於;
1.2.11、headers屬性指定請求中必須有特定header值
對映請求頭部,收窄請求範圍。約束客戶端傳送的請求頭部資訊中必須包含某個特定的值或不包含某個值,作用範圍明顯大於前面講過的幾種,示例程式碼如下:
//請求頭部資訊中必須包含Host=localhost:8088 @RequestMapping(value = "/action11",headers="Host=localhost:8088") public String action11(Model model) { model.addAttribute("message", "請求頭部資訊中必須包含Host=localhost:8088"); return "foo/index"; }
執行結果:
修改Host為8087時執行就正常了:
這裡同樣可以使用!號;可以使用萬用字元如:Content-Type="application/*"
1.2.12、name屬性指定名稱
為當前對映指定一個名稱,不常用,一般不會指定。
1.2.13、path屬性指定路徑
先看原始碼中的path與value,定義如下:
@AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {};
從Spring 4.2開始引入了@AliasFor註解,可以實現屬性的別名,如value本身並沒有特定的含義,而path會更加具體,能見名知義,通俗說可以認為兩者在使用中是一樣的如:@RequestMapping("/foo")} 與 @RequestMapping(path="/foo")相同。示例程式碼如下:
//對映訪問路徑為/action12或/myaction,指定對映名稱為actionTest @RequestMapping(path ={"/action12","/myaction"},name="actionTest") public String action12(Model model) { model.addAttribute("message", "對映訪問路徑為/action12或/myaction,指定對映名稱為actionTest"); return "foo/index"; }
執行結果:
1.2.14、@RequestMapping 快捷方式
Spring 4.3 引入了方法級註解的變體,也被叫做 @RequestMapping 的組合註解。組合註解可以更好的表達被註解方法的語義。它們所扮演的角色就是針對 @RequestMapping 的封裝,而且成了定義端點的標準方法。
例如,@GetMapping 是一個組合註解,它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一個快捷方式。
方法級別的註解變體有如下幾個:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMappin
如下面兩個action就是基本等價的:
@RequestMapping(value = "/action3",method = RequestMethod.GET) public String action3(Model model){ model.addAttribute("msg","action3 get請求"); return "hi"; } @GetMapping("/action4") public String action4(Model model){ model.addAttribute("msg","action4 get請求"); return "hi"; }
action4的寫法要簡單一些,GetMapping與RequestMapping的具體用法一樣,原始碼如下:
/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.Callable; import org.springframework.core.annotation.AliasFor; /** * Annotation for mapping web requests onto specific handler classes and/or * handler methods. Provides a consistent style between Servlet and Portlet * environments, with the semantics adapting to the concrete environment. * * <p><b>NOTE:</b> The set of features supported for Servlets is a superset * of the set of features supported for Portlets. The places where this applies * are marked with the label "Servlet-only" in this source file. For Servlet * environments there are some further distinctions depending on whether an * application is configured with {@literal "@MVC 3.0"} or * {@literal "@MVC 3.1"} support classes. The places where this applies are * marked with {@literal "@MVC 3.1-only"} in this source file. For more * details see the note on the new support classes added in Spring MVC 3.1 * further below. * * <p>Handler methods which are annotated with this annotation are allowed to * have very flexible signatures. They may have parameters of the following * types, in arbitrary order (except for validation results, which need to * follow right after the corresponding command object, if desired): * <ul> * <li>Request and/or response objects (Servlet API or Portlet API). * You may choose any specific request/response type, e.g. * {@link javax.servlet.ServletRequest} / {@link javax.servlet.http.HttpServletRequest} * or {@link javax.portlet.PortletRequest} / {@link javax.portlet.ActionRequest} / * {@link javax.portlet.RenderRequest}. Note that in the Portlet case, * an explicitly declared action/render argument is also used for mapping * specific request types onto a handler method (in case of no other * information given that differentiates between action and render requests). * <li>Session object (Servlet API or Portlet API): either * {@link javax.servlet.http.HttpSession} or {@link javax.portlet.PortletSession}. * An argument of this type will enforce the presence of a corresponding session. * As a consequence, such an argument will never be {@code null}. * <i>Note that session access may not be thread-safe, in particular in a * Servlet environment: Consider switching the * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#setSynchronizeOnSession * "synchronizeOnSession"} flag to "true" if multiple requests are allowed to * access a session concurrently.</i> * <li>{@link org.springframework.web.context.request.WebRequest} or * {@link org.springframework.web.context.request.NativeWebRequest}. * Allows for generic request parameter access as well as request/session * attribute access, without ties to the native Servlet/Portlet API. * <li>{@link java.util.Locale} for the current request locale * (determined by the most specific locale resolver available, * i.e. the configured {@link org.springframework.web.servlet.LocaleResolver} * in a Servlet environment and the portal locale in a Portlet environment). * <li>{@link java.io.InputStream} / {@link java.io.Reader} for access * to the request's content. This will be the raw InputStream/Reader as * exposed by the Servlet/Portlet API. * <li>{@link java.io.OutputStream} / {@link java.io.Writer} for generating * the response's content. This will be the raw OutputStream/Writer as * exposed by the Servlet/Portlet API. * <li>{@link org.springframework.http.HttpMethod} for the HTTP request method</li> * <li>{@link PathVariable @PathVariable} annotated parameters (Servlet-only) * for access to URI template values (i.e. /hotels/{hotel}). Variable values will be * converted to the declared method argument type. By default, the URI template * will match against the regular expression {@code [^\.]*} (i.e. any character * other than period), but this can be changed by specifying another regular * expression, like so: /hotels/{hotel:\d+}. * Additionally, {@code @PathVariable} can be used on a * {@link java.util.Map Map<String, String>} to gain access to all * URI template variables. * <li>{@link MatrixVariable @MatrixVariable} annotated parameters (Servlet-only) * for access to name-value pairs located in URI path segments. Matrix variables * must be represented with a URI template variable. For example /hotels/{hotel} * where the incoming URL may be "/hotels/42;q=1". * Additionally, {@code @MatrixVariable} can be used on a * {@link java.util.Map Map<String, String>} to gain access to all * matrix variables in the URL or to those in a specific path variable. * <li>{@link RequestParam @RequestParam} annotated parameters for access to * specific Servlet/Portlet request parameters. Parameter values will be * converted to the declared method argument type. Additionally, * {@code @RequestParam} can be used on a {@link java.util.Map Map<String, String>} or * {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>} * method parameter to gain access to all request parameters. * <li>{@link RequestHeader @RequestHeader} annotated parameters for access to * specific Servlet/Portlet request HTTP headers. Parameter values will be * converted to the declared method argument type. Additionally, * {@code @RequestHeader} can be used on a {@link java.util.Map Map<String, String>}, * {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}, or * {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to * gain access to all request headers. * <li>{@link RequestBody @RequestBody} annotated parameters (Servlet-only) * for access to the Servlet request HTTP contents. The request stream will be * converted to the declared method argument type using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. Such parameters may optionally be annotated with {@code @Valid} * and also support access to validation results through an * {@link org.springframework.validation.Errors} argument. * Instead a {@link org.springframework.web.bind.MethodArgumentNotValidException} * exception is raised. * <li>{@link RequestPart @RequestPart} annotated parameters * (Servlet-only, {@literal @MVC 3.1-only}) * for access to the content * of a part of "multipart/form-data" request. The request part stream will be * converted to the declared method argument type using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. Such parameters may optionally be annotated with {@code @Valid} * and support access to validation results through a * {@link org.springframework.validation.Errors} argument. * Instead a {@link org.springframework.web.bind.MethodArgumentNotValidException} * exception is raised. * <li>{@link SessionAttribute @SessionAttribute} annotated parameters for access * to existing, permanent session attributes (e.g. user authentication object) * as opposed to model attributes temporarily stored in the session as part of * a controller workflow via {@link SessionAttributes}. * <li>{@link RequestAttribute @RequestAttribute} annotated parameters for access * to request attributes. * <li>{@link org.springframework.http.HttpEntity HttpEntity<?>} parameters * (Servlet-only) for access to the Servlet request HTTP headers and contents. * The request stream will be converted to the entity body using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. * <li>{@link java.util.Map} / {@link org.springframework.ui.Model} / * {@link org.springframework.ui.ModelMap} for enriching the implicit model * that will be exposed to the web view. * <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes} * (Servlet-only, {@literal @MVC 3.1-only}) to specify the exact set of attributes * to use in case of a redirect and also to add flash attributes (attributes * stored temporarily on the server-side to make them available to the request * after the redirect). {@code RedirectAttributes} is used instead of the * implicit model if the method returns a "redirect:" prefixed view name or * {@code RedirectView}. * <li>Command/form objects to bind parameters to: as bean properties or fields, * with customizable type conversion, depending on {@link InitBinder} methods * and/or the HandlerAdapter configuration - see the "webBindingInitializer" * property on RequestMappingHandlerMethodAdapter. * Such command objects along with their validation results will be exposed * as model attributes, by default using the non-qualified command class name * in property notation (e.g. "orderAddress" for type "mypackage.OrderAddress"). * Specify a parameter-level {@link ModelAttribute @ModelAttribute} annotation for * declaring a specific model attribute name. * <li>{@link org.springframework.validation.Errors} / * {@link org.springframework.validation.BindingResult} validation results * for a preceding command/form object (the immediate preceding argument). * <li>{@link org.springframework.web.bind.support.SessionStatus} status handle * for marking form processing as complete (triggering the cleanup of session * attributes that have been indicated by the {@link SessionAttributes @SessionAttributes} * annotation at the handler type level). * <li>{@link org.springframework.web.util.UriComponentsBuilder} * (Servlet-only, {@literal @MVC 3.1-only}) * for preparing a URL relative to the current request's host, port, scheme, * context path, and the literal part of the servlet mapping. * </ul> * * <p><strong>Note:</strong> Java 8's {@code java.util.Optional} is supported * as a method parameter type with annotations that provide a {@code required} * attribute (e.g. {@code @RequestParam}, {@code @RequestHeader}, etc.). The use * of {@code java.util.Optional} in those cases is equivalent to having * {@code required=false}. * * <p>The following return types are supported for handler methods: * <ul> * <li>A {@code ModelAndView} object (Servlet MVC or Portlet MVC), * with the model implicitly enriched with command objects and the results * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * <li>A {@link org.springframework.ui.Model Model} object, with the view name implicitly * determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator} * and the model implicitly enriched with command objects and the results * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * <li>A {@link java.util.Map} object for exposing a model, * with the view name implicitly determined through a * {@link org.springframework.web.servlet.RequestToViewNameTranslator} * and the model implicitly enriched with command objects and the results * of {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * <li>A {@link org.springframework.web.servlet.View} object, with the * model implicitly determined through command objects and * {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * The handler method may also programmatically enrich the model by * declaring a {@link org.springframework.ui.Model} argument (see above). * <li>A {@link String} value which is interpreted as view name, * with the model implicitly determined through command objects and * {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * The handler method may also programmatically enrich the model by * declaring a {@link org.springframework.ui.ModelMap} argument * (see above). * <li>{@link ResponseBody @ResponseBody} annotated methods (Servlet-only) * for access to the Servlet response HTTP contents. The return value will * be converted to the response stream using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. * <li>An {@link org.springframework.http.HttpEntity HttpEntity<?>} or * {@link org.springframework.http.ResponseEntity ResponseEntity<?>} object * (Servlet-only) to access to the Servlet response HTTP headers and contents. * The entity body will be converted to the response stream using * {@linkplain org.springframework.http.converter.HttpMessageConverter message * converters}. * <li>An {@link org.springframework.http.HttpHeaders HttpHeaders} object to * return a response with no body.</li> * <li>A {@link Callable} which is used by Spring MVC to obtain the return * value asynchronously in a separate thread transparently managed by Spring MVC * on behalf of the application. * <li>A {@link org.springframework.web.context.request.async.DeferredResult} * which the application uses to produce a return value in a separate * thread of its own choosing, as an alternative to returning a Callable. * <li>A {@link org.springframework.util.concurrent.ListenableFuture} * which the application uses to produce a return value in a separate * thread of its own choosing, as an alternative to returning a Callable. * <li>A {@link java.util.concurrent.CompletionStage} (implemented by * {@link java.util.concurrent.CompletableFuture} for example) * which the application uses to produce a return value in a separate * thread of its own choosing, as an alternative to returning a Callable. * <li>A {@link org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter} * can be used to write multiple objects to the response asynchronously; * also supported as the body within {@code ResponseEntity}.</li> * <li>An {@link org.springframework.web.servlet.mvc.method.annotation.SseEmitter} * can be used to write Server-Sent Events to the response asynchronously; * also supported as the body within {@code ResponseEntity}.</li> * <li>A {@link org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody} * can be used to write to the response asynchronously; * also supported as the body within {@code ResponseEntity}.</li> * <li>{@code void} if the method handles the response itself (by * writing the response content directly, declaring an argument of type * {@link javax.servlet.ServletResponse} / {@link javax.servlet.http.HttpServletResponse} * / {@link javax.portlet.RenderResponse} for that purpose) * or if the view name is supposed to be implicitly determined through a * {@link org.springframework.web.servlet.RequestToViewNameTranslator} * (not declaring a response argument in the handler method signature; * only applicable in a Servlet environment). * <li>Any other return type will be considered as single model attribute * to be exposed to the view, using the attribute name specified through * {@link ModelAttribute @ModelAttribute} at the method level (or the default attribute * name based on the return type's class name otherwise). The model will be * implicitly enriched with command objects and the results of * {@link ModelAttribute @ModelAttribute} annotated reference data accessor methods. * </ul> * * <p><b>NOTE:</b> {@code @RequestMapping} will only be processed if an * an appropriate {@code HandlerMapping}-{@code HandlerAdapter} pair * is configured. This is the case by default in both the * {@code DispatcherServlet} and the {@code DispatcherPortlet}. * However, if you are defining custom {@code HandlerMappings} or * {@code HandlerAdapters}, then you need to add * {@code DefaultAnnotationHandlerMapping} and * {@code AnnotationMethodHandlerAdapter} to your configuration.</code>. * * <p><b>NOTE:</b> Spring 3.1 introduced a new set of support classes for * {@code @RequestMapping} methods in Servlet environments called * {@code RequestMappingHandlerMapping} and * {@code RequestMappingHandlerAdapter}. They are recommended for use and * even required to take advantage of new features in Spring MVC 3.1 (search * {@literal "@MVC 3.1-only"} in this source file) and going forward. * The new support classes are enabled by default from the MVC namespace and * with use of the MVC Java config ({@code @EnableWebMvc}) but must be * configured explicitly if using neither. * * <p><b>NOTE:</b> When using controller interfaces (e.g. for AOP proxying), * make sure to consistently put <i>all</i> your mapping annotations - such as * {@code @RequestMapping} and {@code @SessionAttributes} - on * the controller <i>interface</i> rather than on the implementation class. * * @author Juergen Hoeller * @author Arjen Poutsma * @author Sam Brannen * @since 2.5 * @see GetMapping * @see PostMapping * @see PutMapping * @see DeleteMapping * @see PatchMapping * @see RequestParam * @see RequestAttribute * @see PathVariable * @see ModelAttribute * @see SessionAttribute * @see SessionAttributes * @see InitBinder * @see org.springframework.web.context.request.WebRequest * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter * @see org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping * @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { /** * Assign a name to this mapping. * <p><b>Supported at the type level as well as at the method level!</b> * When used on both levels, a combined name is derived by concatenation * with "#" as separator. * @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder * @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy */ String name() default ""; /** * The primary mapping expressed by this annotation. * <p>In a Servlet environment this is an alias for {@link #path}. * For example {@code @RequestMapping("/foo")} is equivalent to * {@code @RequestMapping(path="/foo")}. * <p>In a Portlet environment this is the mapped portlet modes * (i.e. "EDIT", "VIEW", "HELP" or any custom modes). * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this primary mapping, narrowing it for a specific handler method. */ @AliasFor("path") String[] value() default {}; /** * In a Servlet environment only: the path mapping URIs (e.g. "/myPath.do"). * Ant-style path patterns are also supported (e.g. "/myPath/*.do"). * At the method level, relative paths (e.g. "edit.do") are supported within * the primary mapping expressed at the type level. Path mapping URIs may * contain placeholders (e.g. "/${connect}") * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this primary mapping, narrowing it for a specific handler method. * @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE * @since 4.2 */ @AliasFor("value") String[] path() default {}; /** * The HTTP request methods to map to, narrowing the primary mapping: * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this HTTP method restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). * <p>Supported for Servlet environments as well as Portlet 2.0 environments. */ RequestMethod[] method() default {}; /** * The parameters of the mapped request, narrowing the primary mapping. * <p>Same format for any environment: a sequence of "myParam=myValue" style * expressions, with a request only mapped if each such parameter is found * to have the given value. Expressions can be negated by using the "!=" operator, * as in "myParam!=myValue". "myParam" style expressions are also supported, * with such parameters having to be present in the request (allowed to have * any value). Finally, "!myParam" style expressions indicate that the * specified parameter is <i>not</i> supposed to be present in the request. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this parameter restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). * <p>In a Servlet environment, parameter mappings are considered as restrictions * that are enforced at the type level. The primary path mapping (i.e. the * specified URI value) still has to uniquely identify the target handler, with * parameter mappings simply expressing preconditions for invoking the handler. * <p>In a Portlet environment, parameters are taken into account as mapping * differentiators, i.e. the primary portlet mode mapping plus the parameter * conditions uniquely identify the target handler. Different handlers may be * mapped onto the same portlet mode, as long as their parameter mappings differ. */ String[] params() default {}; /** * The headers of the mapped request, narrowing the primary mapping. * <p>Same format for any environment: a sequence of "My-Header=myValue" style * expressions, with a request only mapped if each such header is found * to have the given value. Expressions can be negated by using the "!=" operator, * as in "My-Header!=myValue". "My-Header" style expressions are also supported, * with such headers having to be present in the request (allowed to have * any value). Finally, "!My-Header" style expressions indicate that the * specified header is <i>not</i> supposed to be present in the request. * <p>Also supports media type wildcards (*), for headers such as Accept * and Content-Type. For instance, * <pre class="code"> * @RequestMapping(value = "/something", headers = "content-type=text/*") * </pre> * will match requests with a Content-Type of "text/html", "text/plain", etc. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this header restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). * <p>Maps against HttpServletRequest headers in a Servlet environment, * and against PortletRequest properties in a Portlet 2.0 environment. * @see org.springframework.http.MediaType */ String[] headers() default {}; /** * The consumable media types of the mapped request, narrowing the primary mapping. * <p>The format is a single media type or a sequence of media types, * with a request only mapped if the {@code Content-Type} matches one of these media types. * Examples: * <pre class="code"> * consumes = "text/plain" * consumes = {"text/plain", "application/*"} * </pre> * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches * all requests with a {@code Content-Type} other than "text/plain". * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings override * this consumes restriction. * @see org.springframework.http.MediaType * @see javax.servlet.http.HttpServletRequest#getContentType() */ String[] consumes() default {}; /** * The producible media types of the mapped request, narrowing the primary mapping. * <p>The format is a single media type or a sequence of media types, * with a request only mapped if the {@code Accept} matches one of these media types. * Examples: * <pre class="code"> * produces = "text/plain" * produces = {"text/plain", "application/*"} * produces = "application/json; charset=UTF-8" * </pre> * <p>It affects the actual content type written, for example to produce a JSON response * with UTF-8 encoding, {@code "application/json; charset=UTF-8"} should be used. * <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches * all requests with a {@code Accept} other than "text/plain". * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings override * this produces restriction. * @see org.springframework.http.MediaType */ String[] produces() default {}; }View Code
1.3、@ResponseBody
@ResponseBody這個註解通常使用在控制層(controller)的方法上,其作用是將方法的返回值以特定的格式寫入到response的body區域,進而將資料返回給客戶端。當方法上面沒有寫ResponseBody,底層會將方法的返回值封裝為ModelAndView物件。
假如是字串則直接將字串寫到客戶端,假如是一個物件,此時會將物件轉化為json串然後寫到客戶端。這裡需要注意的是,如果返回物件,按utf-8編碼。如果返回String,預設按iso8859-1編碼,頁面可能出現亂碼。因此在註解中我們可以手動修改編碼格式,例如@RequestMapping(value="/cat/query",produces="text/html;charset=utf-8"),前面是請求的路徑,後面是編碼格式。
那麼,控制層方法的返回值是如何轉化為json格式的字串的呢?其實是通過HttpMessageConverter中的方法實現的,因為它是一個介面,因此由其實現類完成轉換。如果是bean物件,會呼叫物件的getXXX()方法獲取屬性值並且以鍵值對的形式進行封裝,進而轉化為json串。如果是map集合,採用get(key)方式獲取value值,然後進行封裝。
@ResponseBody是作用在方法上的,@ResponseBody 表示該方法的返回結果直接寫入 HTTP response body 中,一般在非同步獲取資料時使用【也就是AJAX】,在使用 @RequestMapping後,返回值通常解析為跳轉路徑,但是加上 @ResponseBody 後返回結果不會被解析為跳轉路徑,而是直接寫入 HTTP response body 中。 比如非同步獲取 json 資料,加上 @ResponseBody 後,會直接返回 json 資料。
User:
package com.zhangguo.autoconfigdemo; public class User { public User() { } public User(String name, int age) { this.name = name; this.age = age; } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
UserController
package com.zhangguo.autoconfigdemo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @Controller public class UserController { private static List<User> users=new ArrayList<>(); static { users.add(new User("jack",18)); users.add(new User("rose",19)); users.add(new User("lili",95)); } @RequestMapping("/users") @ResponseBody public List<User> findUsers() { return users; } }
測試
1.4、@RequestBody
@RequestBody 將 HTTP 請求正文插入方法中,使用適合的 HttpMessageConverter 將請求體寫入某個物件。
@RequestMapping(value = "person/login") @ResponseBody public Person login(@RequestBody Person person) { // 將請求中的 datas 寫入 Person 物件中 return person; // 不會被解析為跳轉路徑,而是直接寫入 HTTP 響應正文中 }
後臺 Controller類中對應的方法:
@RequestMapping("/login.do")
@ResponseBody
public Object login(String name, String password, HttpSession session) {
user = userService.checkLogin(name, password);
session.setAttribute("user", user);
return new JsonResult(user);
}
如上面的登入後臺程式碼可以改為:
@RequestMapping("/login.do")
@ResponseBody
public Object login(@RequestBody User loginUuser, HttpSession session) {
user = userService.checkLogin(loginUser);
session.setAttribute("user", user);
return new JsonResult(user);
}
@RequestBody 註解則是將 HTTP 請求正文插入方法中,使用適合的 HttpMessageConverter 將請求體寫入某個物件。
作用:
該註解用於讀取Request請求的body部分資料,使用系統預設配置的HttpMessageConverter進行解析,然後把相應的資料繫結
到要返回的物件上;
再把HttpMessageConverter返回的物件資料繫結到 controller中方法的引數上。
使用時機:
A) GET、POST方式提時, 根據request header Content-Type的值來判斷:
application/x-www-form-urlencoded, 可選(即非必須,因為這種情況的資料@RequestParam, @ModelAttribute也可以處理,當然@RequestBody也能處理);
multipart/form-data, 不能處理(即使用@RequestBody不能處理這種格式的資料);
其他格式, 必須(其他格式包括application/json, application/xml等。這些格式的資料,必須使用@RequestBody來處理);
B) PUT方式提交時, 根據request header Content-Type的值來判斷:
application/x-www-form-urlencoded, 必須;multipart/form-data, 不能處理;其他格式, 必須;
說明:request的body部分的資料編碼格式由header部分的Content-Type指定;
3、示例下載
https://zhangguo5.coding.net/public/SpringMVCDemo/SpringMVCDemo/git
4、視訊
https://www.bilibili.com/video/av16991874/
5、作業
5.1、重現文中所有示例
5.2、完成個人專案前臺頁面與後臺頁面設計
5.3、預習第三章內容、完成任務指導手冊中Spring MVC部分的內容
二、Restful
三、Swagger
四、作業
五、視訊