1、本文內容
2、控制器的聲明
2.1、啟用組件自動掃描
2.2、使用@Controller標注
3、映射請求路徑
3.1、在類上使用 @RequestMapping聲明
3.2、在方法上使用@RequestMapping聲明
3.3、@GetMapping、@PostMapping、@PutMapping等
4、獲取URL請求參數
4.1、利用請求路徑獲取參數 URI Template Pattern & @PathVariable
4.2、獲取URL請求參數 @RequestParam
5、獲取表單數據
6、控制器返回視圖
6.1、返回視圖
6.2、攜帶數據返回視圖
7、控制器轉跳
7.1、控制器之間跳轉
7.2、跳轉到外部鏈接
Controller是由很多內容組成的,包括將一個類配置為控制器、將類或方法映射為請求路徑、從URL請求中解析參數、從表單中解析參數、控制器之間的跳轉、請求的重定向、返回視圖、構造模型等等內容
本文對這些控制器的常用部分做一個大致的梳理
本文的內容主要基於Java API 和注解方式配置
Spring配置中的啟用組件自動掃描配合使用,將控制器所在的包設置為自動掃描的目標目錄
package com.oolong.config; import com.oolong.controller.ControllerScanHook; @Configuration @EnableWebMvc @ComponentScan(basePackageClasses={ControllerScanHook.class}) public class WebConfig extends WebMvcConfigurerAdapter { }
package com.oolong.controller; import org.springframework.stereotype.Controller; @Controller public class HomeController { }
這相當於給這個控制器中的所有方法定義一個跟路徑,然后在其方法上使用 @RequestMapping配置映射時,所有的映射都是相對於類聲明上配置的這個跟路徑的相對路徑
@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(path = "/{day}", method = RequestMethod.GET) public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso = ISO.DATE) Date day, Model model) { return appointmentBook.getAppointmentsForDay(day); } @RequestMapping(path = "/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"; } }
觀察這個案例中的內容,當使用 @RequestMapping標注在類上面聲明了一個路徑之后,方法的配置分為這樣幾種情況:
1)GET方式請求路徑“/appointments”,將會進入 get() 方法
2)POST方式請求路徑“/appointments”,將會進入 add() 方法
3)GET方式請求“/appointment/new”,將會進入 getNewForm() 方法
4)GET方式請求“/appointment/2016-12-06”,將會進入 getForDay() 方法【參考路徑模版】
在上面的例子中@RequestMapping用在了兩個地方,首相在類上聲明,作為一個“基礎”路徑,然后在方法上的聲明是相對於這個基礎路徑的相對路徑。
除了這種用法,還可以不在類上面標注 @RequestMapping,而直接在方法上使用,這就表示絕對路徑的聲明,例如下面這樣:
@Controller public class HomeController { @RequestMapping(value = "/", method = RequestMethod.GET) public String home() { return "home"; } @RequestMapping(value = "/welcome", method = RequestMethod.GET) public String welcome() { return "welcome"; } }
1)GET方式請求“/”,就會進入 home()方法
2)GET方式請求“/welcome”,就會進入 welcome() 方法
3.3、@GetMapping、@PostMapping、@PutMapping等
Spring框架從4.3開始引入了一個簡化 @ReuqestMapping的注解,也就是:
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
這幾個注解實際上就是設置了請求類型的 @RequestMapping 注解,與其對應關系可以參考下面的示例:
@RequestMapping(method = RequestMethod.GET) @GetMapping @RequestMapping(path = "/new", method = RequestMethod.GET) @GetMapping("/new") @RequestMapping(method = RequestMethod.POST) @PostMapping
4.1、利用請求路徑獲取參數 URI Template Pattern & @PathVariable
在使用 @RequestMapping或者@GetMapping等注解標注映射時,可以使用URL Template Patterns來設置請求路徑,並從中獲取參數。
例如,下面是一個URI的模版:
http://www.example.com/users/{userId}
注意這個模版,看起來與普通的URL沒有什么區別,但是仔細看,其路徑的最后一部分是 {userId} ,這樣一個使用大括號括起來的字符串,這個就是一個URI模版,與這個模版對應的實際請求如下:
http://www.example.com/users/12345
在SpringMVC中,對於在 @RequestMapping中配置的這種請求路徑,可以使用 @PathVariable注解來獲取值:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
在這個例子中的URI路徑模版就是“/owners/{ownerId}”,然后在控制器方法的參數中使用 @PathVariable 注解標注一個參數,Spring MVC就會在獲取請求i之后自動的將{ownerId}所表示的內容設置到這個參數中了。
注意,這里方法的參數名稱要與URI中的{var}名稱一樣。
此外,@PathVariable注解可以指定方法參數對應的URI模版中占位符的名稱:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
利用 @RequestParam可以從傳統的URL中獲取參數
@RequestMapping(value = "/requestparam", method = RequestMethod.GET)
public String acceptingRequestInput(@RequestParam("bookname") String name, @RequestParam("count") int count) { System.out.println("bookname: " + name); System.out.println("count: " + count); return "requestInput"; }
注意這個方法的參數,這個方法有兩個參數,分別使用 @RequestParam注解進行標注了,參考上一小節從路徑中獲取參數的方式,應該能夠聯想到這種方式是如何獲取參數的。
對應於這個方法的請求路徑:
/requestparam?bookname=thinkingjava&count=5
這種傳統的URL請求,一個“?”后面跟着若干個鍵值對參數,可以使用 @RequestParam的方式獲取參數。
獲取表單數據可以直接在控制器中的方法中給出一個模型作為參數即可,表單中的name值對應着模型中的屬性值,然后數據會被注入到一個自動構建的參數對象中。
form表單
<form action="${basePath}pm/usecase/save" method="POST"> <label for="num">Num</label> <input type="text" id="Num" name="num"><br> <label for="name">Name</label> <input type="text" id="name" name="name"><br> <label for="description">Description</label> <textarea rows="3" name="description" id="description"></textarea><br> <label for="status">Status</label> <select class="form-control" id="status" name="status"> <option value="1">創建</option> <option value="2">完成</option> </select><br> <label for="author">Author</label> <input type="text" id="author" name="author"><br> <label for="complitionDate">ComplitionDate</label> <input type="date" id="complitionDate" name="complitionDate"> <input type="time" id="complitionTime" name="complitionTime"><br> <button type="submit" class="btn btn-default">Save</button> </form>
模型
注意這個模型中的屬性名稱與表單中的name屬性是一一對應的,如此才能夠將值自動注入到相應的屬性中
public class UsecaseViewModel { public UsecaseViewModel() { } private String wid; private String num; private String name; private String description; private int status; private String author; private String complitionDate; private String complitionTime; private String createDate; private String moduleWid; // getter and setter methods }
控制器方法
此處控制器的參數,直接使用這個模型類型,這告訴SpringMVC將相關的數據自動構造成這個類型的一個對象,然后注入到這個方法中
@RequestMapping(value = "/pm/usecase/save", method = RequestMethod.POST) public String saveUsecase(UsecaseViewModel model) { usecaseBus.saveUsecase(model); return "redirect:/pm/usecase/list/" + model.getModuleWid(); }
在SpringMVC中,控制器中的方法並不直接返回視圖的完整路徑,而是返回一個視圖的名稱,然后根據在Spring中配置的視圖解析器,結合視圖名稱確定真正的視圖。
根據在SpringMVC的配置類中配置的視圖解析器:
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
上面配置的內容表示在控制器返回的視圖名稱加上前綴“/views/”,和后綴“.jsp”,然后到WebContent下尋找對應的文件。
因此對於下面的控制器中返回的視圖名稱:
@GetMapping("/welcome")
public String welcome() {
return "welcome";
}
其實際對應的視圖文件應該是在WebContent目錄下的:/views/welcome.jsp
上面的方式只是簡單的返回一個視圖,但在實際開發中通常需要將一些數據展示到視圖中,那么如何將這些數據攜帶到視圖中呢?
觀察下面的方法,這個方法的返回值不再是一個字符串,而是一個 ModelAndView 類型,然后在這個方法中創建了一個ModelAndView 對象,這個構造函數中接受的字符串,就是視圖的名稱,此外可以向這個對象中添加鍵值對,這些添加到ModeAndView中的鍵值對實際上會被添加到Request中,然后被轉發給JSP頁面,這樣我們就可以使用EL表達式直接獲取這些值了。
@RequestMapping(value = "/todo/detail/{wid}")
public ModelAndView todoDetail(@PathVariable String wid) {
ModelAndView mv = new ModelAndView("todoDetail");
TodoDetailViewModel detail = todoBus.getDetail(wid);
mv.addObject("detail", detail);
return mv;
}
在JSP頁面,可以直接使用EL表達式獲取,此外還可以使用JSTL。
<c:forEach items="${detail.items}" var="item" varStatus="status"> <tr> <td>${status.index+1 }</td> <td>${item.content}</td> <td>${item.createDate}</td> <td><a class="btn btn-default btn-sm" href="###" role="button">刪除</a></td> </tr> </c:forEach>
使用“redirect:”前綴,后面跟上@RequestMapping中配置的路徑,即可實現控制器之間的跳轉
return "redirect:/pm/usecase/list/" + model.getModuleWid();
如果需要跳轉到外部鏈接,則需要給出完整的路徑,當然“redirect:”前綴也不能丟掉
return "redirect:http://myhost.com/absolutepath"
