目錄
1.1 RESTful風格接口介紹
RESTful是目前最流行的 API 設計規範,用於 Web 數據接口的設計。通過不同的HTTP請求方法來實現對資源的CRUD操作,請求URL爲"/資源名稱/資源標識"的形式,用到的HTTP請求方法如下:
- GET:讀取(Read)
- POST:新建(Create)
- PUT:更新(Update)
- PATCH:更新(Update),通常是部分更新
- DELETE:刪除(Delete)
舉例:
操作 | 普通接口URL | RESTful接口URL |
---|---|---|
查詢全部 | /getDevices | (GET) /devices |
查詢單個 | /getDevice?id=xxx | (GET) /devices/{id} |
添加 | /addDevice | (POST) /devices |
修改 | /updateDevice | (PUT) /devices/{id} |
刪除 | /deleteDevice | (DELETE) /devices/{id} |
通過上面的對比,可以看到RESTful風格接口更加簡潔。
關於更爲詳細的RESTful接口介紹推薦這一篇文章
1.2 編寫Controller
Controller的編寫非常簡單,只需要兩個註解+一個處理方法即可。
在controller包下編寫UserController類,代碼結構如下:
@Controller
public class UserController
{
@PostMapping("/login")
public String login()
{
....
return "success";
}
}
- @Controller表明該類爲Spring MVC的一個控制器
- @PostMapping("/login")表示只要是uri爲/login的POST請求,就交由該方法處理。等同於@RequestMapping(value = “/login”,method = RequestMethod.POST)
- return "success"表示返回/success界面。
1.3 前端提交數據給後端
1.3.1 獲取單個值
編寫login.html,代碼如下:
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用戶登陸</title>
</head>
<body>
<form action="/login" method="POST">
用戶名:<input type="text" name="userName"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</body>
</html>
UserController代碼如下:
@Controller
public class UserController
{
Logger logger = LoggerFactory.getLogger(UserController.class);
@PostMapping("/login")
public String login(@RequestParam(value = "userName") String userName,@RequestParam(value = "password") String password)
{
logger.info("username:" + userName + ",password:" + password);
return "success";
}
}
在這裏使用@RequestParam註解單個獲取值,並注入到參數列表中。可以看到成功獲取到了數據:
注:如果Controller中的變量名和html頁面中的變量名一致,可以不加@RequestParam註解,Spring會自動匹配。
public String login(String userName,String password)
{
...
}
1.3.2 表單提交數據到對象
新創建User對象,代碼如下:
public class User
{
private String userName;
private String password;
getter().....
setter().....
toString()....
}
修改Controller代碼如下:
@PostMapping("/login")
public String login(User user)
{
logger.info(String.valueOf(user));
return "success";
}
值同樣被注入進來了
1.3.3 獲取PathValue
在前面的RESTful章節,我們看到了類似於這樣的URI:devices/{id},那這裏的id怎麼獲取呢?接下來演示一下:
在UserController新創建以下方法:
@GetMapping("/user/{id}")
public String get(@PathVariable("id") Integer id)
{
logger.info("id = " + id);
return "success";
}
訪問試一下:
成功打印出了id
@PathVariable(“id”) Integer id的作用是將uri裏的{id}綁定到變量id中。
1.3.4 獲取前端提交的JSON數據到對象
現在很多公司web開發施行前後端分離策略,前後端之間通信的數據載體一般是JSON字符串。Spring MVC對此做了很好的支持。還是以提交User數據爲例,JSON字符串可能是這樣寫的:
{
"userName":"admin",
"password":"123456"
}
Controller改寫代碼如下:
@PostMapping("/login")
public String login(@RequestBody User user)
{
logger.info(String.valueOf(user));
return "success";
}
使用Postman提交數據後,可以看到服務器有日誌輸出:
@RequestBody修飾User對象的作用是將客戶端傳遞過來的JSON數據自動綁定到該對象中。
1.3.5 數據校驗器
Spring MVC支持對客戶端傳遞過來的數據進行JSR303數據校驗。關於JSR303可看我前面寫的文章:SpringBoot學習篇1[配置文件知多少]的2.3.3節數據校驗。
假設我們限定用戶名必須爲Email,密碼不得小於6位。修改User類,定義的校驗規則如下:
public class User
{
@Email
private String userName;
@Length(min = 6)
private String password;
......
}
UserController的login方法修改如下:
@PostMapping("/login")
public String login(@Validated User user, BindingResult bindingResult)
{
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError error : allErrors)
{
System.out.println(error);
}
if(!allErrors.isEmpty())
return "error";
logger.info(String.valueOf(user));
return "success";
}
@Validation註解表示啓用數據校驗,BindingResult對象用於存放校驗結果。
當輸入一個錯誤的郵箱格式時:
當密碼長度小於6時:
出現錯誤信息時的default message支持自定義,直接指定message的值即可:
@Email(message = "請輸入郵箱")
private String userName;
效果如下 :
1.3.6 Controller支持的內部對象
Controller方法默認支持的參數類型有以下4個
- HttpServletRequest
- HttpServletResponse
- HttpSession
- Model
當我們需要用到上述對象時,直接在形參列表中聲明此參數即可。比如用戶登陸時,一般用HttpSession對象存儲當前成功登陸的用戶:
@PostMapping("/login")
public String login(@RequestBody User user, HttpSession session)
{
.......
session.setAttribute("user",user);
return "success";
}
還可以直接用HttpServletRequest對象獲取html頁面提交表單時傳遞過來的參數:
@PostMapping("/login")
public String login(HttpServletRequest request)
{
String userName = request.getParameter("userName");
String password = request.getParameter("password");
logger.info("username:" + userName + ",password:" + password);
return "success";
}
打印結果:
1.3.7 小結
- @Controller註解聲明當前對象爲Spring MNV控制器對象
- @RequestMapping、@GetMapping、@PostMapping、@PutMapping…綁定請求和處理方法
- 如果客戶端傳遞過來的屬性名與處理請求的方法參數列表的名稱一致時,支持自動綁定參數到對象(需要有對應的setter方法)或普通變量中。
- @RequestParam用於將形參變量與前端傳遞過來的屬性綁定
- @PathVariable用於獲取RESTful形式接口的資源標識
- @RequestBody用於從JSON字符串中獲取值,並自動注入Java Bean
- @Validated用於啓用校驗,校驗結果將存儲到BindingResult對象中
1.4 後端傳遞數據給前端界面
準備工作:在控制器層新創建EmployeeController類,用該類演示後端傳遞數據給前端。新創建/templates/employee/list.html模板頁面,用於顯示後端傳遞給前端的數據。
list.html代碼如下:
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>員工列表</title>
</head>
<body>
<table border="1px">
<tr>
<td>姓名</td>
<td>工號</td>
<td>工資</td>
</tr>
<tr th:each="employee : ${employeeList}">
<td th:text="${employee.name}"></td>
<td th:text="${employee.id}"></td>
<td th:text="${employee.salary}"></td>
</tr>
</table>
</body>
</html>
1.4.1 利用Model對象傳遞數據
@GetMapping(value = "/employees")
public String list(Model model)
{
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = new Employee();
employee1.setter......
employee2.setter......
employee3.setter......
List<Employee> employeeList = new LinkedList<>();
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
model.addAttribute("employeeList",employeeList);
return "employee/list";
}
通過model.addAttribute方法可以把數據封裝到Request對象中,在HTML界面通過變量表達式就可以直接獲取到值。
1.4.2 利用Map集合傳遞數據
利用Map集合傳遞數據給前端和利用Model對象基本一致。
@GetMapping(value = "/employees")
public String list(Map<String,Object> map)
{
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = new Employee();
employee1.setter......
employee2.setter......
employee3.setter......
List<Employee> employeeList = new LinkedList<>();
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
map.put("employeeList",employeeList);
return "employee/list";
}
1.4.3 利用Session對象傳遞數據
@GetMapping(value = "/employees")
public String list(HttpSession session)
{
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = new Employee();
employee1.setter......
employee2.setter......
employee3.setter......
List<Employee> employeeList = new LinkedList<>();
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
session.setAttribute("employeeList",employeeList);
return "employee/list";
}
此時HTML界面代碼就要改一下了:
<tr th:each="employee : ${session.employeeList}">
...
</tr>
1.4.4 接口返回JSON對象
將JSON字符串自動注入JavaBean時使用的是@RequestBody註解修飾要注入的對象,如果要返回JSON對象,只需要用@ResponseBody註解修飾該方法,並return要返回的對象即可。
@GetMapping(value = "/employees")
@ResponseBody
public List<Employee> list()
{
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Employee employee3 = new Employee();
employee1.setter......
employee2.setter......
employee3.setter......
List<Employee> employeeList = new LinkedList<>();
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
return employeeList;
}
使用Postman可以看到接口成功得到返回了數據
1.4.5 數據回顯
有時用戶輸入了數據提交表單後,需要將用戶輸入的數據回顯在界面上。數據回顯的本質是將數據綁定到request域中,前端再從request域獲取值,這一點其實並不難。提交表單界面獲取request域中的值:
<form th:action="@{/employees}" th:method="POST">
姓名:<input type="text" name="name" th:value="${employee.name}"><br>
工號:<input type="text" name="id" th:value="${employee.id}"><br>
工資:<input type="number" name="salary" th:value="${employee.salary}"><br>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
綁定數據到request域
@PostMapping("/employees")
public String add(@ModelAttribute(value = "employee") Employee employee)
{
System.out.println(employee);
return "employee/add";
}
加上@ModelAttribute(value = “employee”)註解,會自動將數據綁定到request域中。相當於寫了model.addAttribute(“employee”,employee)這一行程序。
1.4.6 小結
- 使用Model對象和Map集合都是將數據放入request域,其生命週期是這次request請求。
- Session生命週期在客戶端第一次訪問服務器開始,在超時時間過後或服務器手動銷燬session對象後結束,前後端數據傳遞原則上能不使用Session就不使用Session。
- 使用@Controller修飾控制器類,默認情況下請求處理方法返回視圖。
- 用@ResponseBody修飾請求處理方法,可以讓該方法返回JSON數據。
- 可以直接用@RestController註解或@Controller + @ResponseBody修飾控制器類,這種情況下請求處理方法默認返回JSON數據,@RestController就相當於@Controller + @ResponseBody。
- 數據回顯,本質就是將前端送上來的數據再放到request域中,可直接用@ModelAttribute註解。
1.5 擴展Spring MVC功能
Spring Boot爲Spring MVC提供了自動配置,可與大多數應用程序完美配合。
自動配置在Spring默認值的基礎上添加了以下功能:
- 包含ContentNegotiatingViewResolver和BeanNameViewResolver。
- 支持服務靜態資源,包括對WebJars的支持
- 自動註冊Converter,GenericConverter和Formatter
- 支持HttpMessageConverters
- 自動註冊MessageCodesResolver
- 靜態index.html支持
- 定製Favicon支持
- 自動使用ConfigurableWebBindingInitializer
如果想使用Spring Boot MVC功能,並且想要添加其他MVC配置(攔截器、參數轉化器、視圖控制器等),則可以自定義WebMvcConfigurer,並將其注入Spring IOC容器。
1.5.1 配置視圖解析器
步驟:
[1] 自定義配置類
[2] 注入自定義WebMvcConfigurer對象到Spring容器
[3] 自定義WebMvcConfigurer對象重寫addViewControllers方法
自定義config.MySpringMvcConfigurer代碼如下:
@Configuration
public class MySpringMvcConfigurer {
@Bean
public WebMvcConfigurer webMvcConfigurer()
{
return new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("public/login");
}
};
}
}
這段代碼的作用是將localhost:8080這個url解析到templates/public/login.html模板頁面下。
1.5.2 自定義攔截器
自定義攔截器同樣是在自定義的WebMvcConfigurer下做文章。定義攔截器的步驟如下:
[1] 自定義攔截處理器
[2] 在自定義WebMvcConfigurer對象中重寫addInterceptors方法。
[1] 自定義攔截處理器interceptor.LoginHandlerInterceptor,代碼如下:
public class LoginHandlerInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getSession().getAttribute("loginUser") == null) {
request.setAttribute("msg","沒有權限,請先登錄");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
return true;
}
}
[2] 在自定義WebMvcConfigurer對象中重寫addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
//攔截所有請求
.addPathPatterns("/**")
//不攔截登陸請求
.excludePathPatterns("/","/login")
//springBoot2+之後需要將靜態資源文件的訪問路徑也排除
.excludePathPatterns("/css/*","/img/*","/js/*");
}
處理登陸的Controller如下:
@PostMapping("/login")
public String login(HttpServletRequest request)
{
String userName = request.getParameter("userName");
String password = request.getParameter("password");
logger.info("username:" + userName + ",password:" + password);
User user = new User();
if(userName.equals("admin") && password.equals("123456"))
{
user.setUserName(userName);
user.setPassword(password);
request.getSession().setAttribute("loginUser",user);
return "redirect:/employees";
}
return "redirect:/";
}
登陸成功,直接跳轉到員工列表,登陸失敗,返回登陸頁面。
1.5.3 自定義參數轉換器
以獲取前端傳遞過去的時間日期爲例
date.html代碼
<form method="post" action="/date">
<input type="text" name="date">
<input type="submit" value="提交">
</form>
控制器代碼:
@PostMapping("/date")
public String date(@RequestParam("date")Date date)
{
System.out.println(date);
return "success";
}
提交數據
結果是程序報錯了:
自定義參數轉換器步驟:
1.自定義類實現Converter接口
2.將該類注入Spring IOC容器
代碼如下:
@Component
public class MyMessageConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
再次提交數據,可以看到成功解析出來了前端傳遞過去的時間日期