文章目錄
一、RESTFUL 的設計風格
1、RESTFUL 概述
(1)RESTFUL 是一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次。在RESTFUL的設計中服務器的任何內容都被當成資源來處理,例如網頁、文件、數據庫的數據等等,而同一種資源共享同一個接口地址,然後再通過請求方式的不同來區分對資源進行的不同操作。
(2)請求方式如下:
- GET請求表示獲取數據
- POST請求表示新增數據
- PUT/PATCH請求表示修改數據
- DELETE請求表示刪除數據
結合請求地址來看:
- GET方式發送 /users請求時,表示查詢users的所有數據
- DELETE方式發送/users請求時,則表示刪除users數據
- POST方式發送/users請求時,則表示要新增一個用戶數據
- PUT方式發送/users請求時,則表示要更新一個用戶數據
- PATCH方式發送/users請求時,則表示要更新一個用戶數據中的部分字段
2、RESTFUL 設計風格示例
(1)SpringMVC 提供了對RESTFUL的支持,這種支持體現在兩個方面,SpringMVC 在進行請求地址映射時,不但會匹配請求地址的名稱,而且還會匹配發送請求的方式,也就是說只有當請求地址和請求方式都匹配時,才能執行業務控制器中的方法。例如:
@RequestMapping(value="user",method= {RequestMethod.GET})
public ResultModel selectAll(){
return null;
}
另外@RequestMapping()
還可以用對應的請求方式代替,如:
@GetMapping("user")
public ResultModel selectAll(){
System.out.println("查詢所有");
System.out.println(us.selectAll());
return new ResultModel(us.selectAll(),null,"success",null);
}
- GET請求對應
@GetMapping
- POST請求對應
@PostMapping
- PUT/PATCH對應
@PutMapping
- DELETE對應
@DeleteMapping
(2)在上述的請求地址映射中,客戶端通過get方式發送user請求能夠執行業務控制器方法,但是如果使用了除get以外的其他請求方式,就不能夠執行該方法,這個設置爲同一個地址不同請求方式提供了支持。SpingMVC在請求地址中可以提供佔位符,而且佔位符中的數據可以在方法中獲取到,這就提供了一種非常簡潔的傳參方式。
例如:
@GetMapping("user/{userId}")
public ResultModel selectById(@PathVariable("userId")int userId){
return null;
}
通過上述配置,我們就可以在頁面發送get方式的請求user/1表示要查詢id爲1的用戶數據。
以下是RESTFUL所設計的一組接口(增刪改查):
@RestController
public class UsersController {
@Resource
private UsersService us; //業務層對象,便於調用業務層對應方法
//定義請求地址映射第一種:@RequestMapping(value="user",method= {RequestMethod.GET})
//定義請求地址映射第二種:@GetMapping("user"),下面都使用第二種
@GetMapping("user")
public ResultModel selectAll() throws Exception {
return new ResultModel(us.selectAll(),null,"success",null);
}
@GetMapping("user/{userId}")
public ResultModel selectById(@PathVariable("userId")int userId) throws Exception{
return new ResultModel(null,us.selectById(userId),"success",null);
}
@PostMapping("user")
public ResultModel insert(@Validated Users users,BindingResult br) throws Exception {
//自定義參數校驗異常判斷,參數不對的時候會執行自定義異常的報錯
if(br.hasErrors()) {
throw new ParamException();
}
us.insert(users);
return new ResultModel(null,null,"success",null);
}
@PutMapping("user")
public ResultModel updateById(@Validated Users users,BindingResult br) throws Exception {
if(br.hasErrors()) {
throw new ParamException();
}
us.updateById(users);
return new ResultModel(null,null,"success",null);
}
@DeleteMapping("user/{userId}")
public ResultModel deleteById(@PathVariable("userId")int userId) throws Exception {
us.deleteById(userId);
return new ResultModel(null,null,"success",null);
}
}
(3)如果是PUT請求,需要添加一個額外的過濾器,控制器中才能獲取到傳遞的參數。
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
接口的請求地址設計好之後,我們還需要統一對返回的JSON數據進行規範,通常可以定義一個返回值類,在返回值類中封裝執行狀態以及一些需要返回給前端的數據。
(4)在AJAX的項目中也可以使用異常處理器進行統一的異常處理,但是要注意的是,在異常處理器中響應視圖信息時必須響應JSON類型的視圖信息。
代碼如下:需要導入 fastjson 的 jar 包
@Component
public class ExceptionResolver implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception ex) {
FastJsonJsonView view = new FastJsonJsonView();
ex.printStackTrace();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "error");
if(ex instanceof ParamException) {
map.put("message", "參數錯誤");
}else {
map.put("message", "服務器異常");
}
view.setAttributesMap(map);
ModelAndView mav = new ModelAndView();
mav.setView(view);
return mav;
}
}
(5)前端請求類
<body>
<a href="javascript:" onclick="selectAll()">查詢所有</a>
<table id="table">
<tr>
<td>姓名</td>
<td>密碼</td>
<td>操作</td>
</tr>
</table>
<form id="insert-form">
<input name="userName"><br>
<input type="password" name="userPassword"><br>
<input type="button" onclick="insert()" value="新增">
</form>
<input id="userId" placeholder="請輸入用戶id"><input type="button" value="搜索用戶" onclick="selectById()">
<form id="update-form">
<input name="userId">
<input name="userName"><br>
<input type="password" name="userPassword"><br>
<input type="button" onclick="updateById()" value="修改">
</form>
<script type="text/javascript">
function selectAll(){
$.ajax({
url: "/SpringMVC_Demo1/user",
type: "get",
dataType: "json",
success:function(data){
for(var i=0;i<data.list.length;i++){
$("#table").append("<tr id='deleted"+data.list[i].userId+"'>"+
"<td>"+data.list[i].userId+"</td>"+
"<td>"+data.list[i].userName+"</td>"+
"<td>"+data.list[i].userPassword+"</td>"+
"<td><a href='javascript:' onclick='deleteById("+data.list[i].userId+")'>刪除</a></td>"+
"</tr>");
}
}
});
}
function deleteById(userId){
$.ajax({
url: "/SpringMVC_Demo1/user/"+userId,
type: "delete",
dataType: "json",
success:function(data){
console.log(data.code);
$("tr[id=deleted"+userId+"]").remove();
}
});
}
function insert(){
$.ajax({
url: "/SpringMVC_Demo1/user",
type: "post",
data: $("#insert-form").serializeJSON(),
dataType: "json",
success:function(data){
console.log(data.code);
}
});
}
function updateById(){
$.ajax({
url:"/SpringMVC_Demo1/user",
type:"put",
data:$("#update-form").serializeJSON(),
dataType:"json",
success:function(data){
console.log(data.code);
}
});
}
function selectById(){
$.ajax({
url:"/SpringMVC_Demo1/user/"+$("#userId").val(),
type:"get",
dataType:"json",
success:function(data){
$("#update-form input[name=userId]").val(data.object.userId);
$("#update-form input[name=userName]").val(data.object.userName);
$("#update-form input[name=userPassword]").val(data.object.userPassword);
}
});
}
</script>
</body>
二、SpringMVC 攔截器
Spring Web MVC 的處理器攔截器類似於 Servlet 開發中的過濾器 Filter,用於對處理器進行預處理和後處理。
1、SpringMVC 攔截器使用步驟
(1)攔截器定義,實現 HandlerInterceptor 接口
@Component
public class Interceptors implements HandlerInterceptor{
/**
* 請求處理之前調用攔截器
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("請求處理之前");
//返回true表示放行,返回false表示攔截
return true;
}
/**
* 請求處理之後
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("請求處理後");
}
/**
* 請求處理之後,提交到界面之前
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("請求處理後,提交數據到界面之前");
}
}
preHandle
方法在執行處理器之前執行,可以用於登錄驗證、權限驗證等等postHandler
方法在出行完處理器之後,進入視圖解析器之前執行,可以對視圖進行處理afterCompletion
方法在響應視圖之前執行,主要用於關閉資源
(2)如果沒有在攔截器類上面添加@Component
註解,則需要手動配置攔截器,在 SpringMVC 的配置文件中添加標籤加載攔截器
<!-- 配置攔截器 -->
<bean id="Interceptors" class="com.springmvc.interceptor.Interceptors"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/js/**"/>
<!--配置從根目錄下發送的以js開頭的所有請求不進入攔截器 -->
<ref bean="Interceptors"/>
</mvc:interceptor>
</mvc:interceptors>
Springmvc 不會攔截.jsp
的請求,但是會攔截靜態資源請求,如果不想讓用戶直接訪問 jsp 頁面,需要將 jsp 頁面放到 web-inf 中,對於靜態資源需要提供靜態資源處理器以及通過<mvc:exclude-mapping path="/js/**"/>
對靜態資源放行。
三、SpringMVC 文件上傳和下載
無論是 Servlet 文件上傳,Struts2 文件上傳還是 SpringMVC 文件上傳,前端是不變的既可以使用普通的表單上傳也可以使用AJAX上傳。區別主要在於後端代碼的處理。
1、SpringMVC 上傳文件
(1)加載多部件解析器
<!-- 配置多部件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設置上傳文件的最大字節數 -->
<property name="maxUploadSize" value="20971520"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
(2)頁面採用表單提交或者AJAX提交
<form action="upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="head"><input type="submit" value="點擊上傳">
</form>
(3)SpringMVC 上傳可以使用 commons-upload 的包來簡化操作
(4)Controller 中處理文件
@Controller
public class FileController {
@RequestMapping("upload")
@ResponseBody
public String upload(MultipartFile head) throws Exception {
//獲取上傳文件的名稱
String filename=head.getOriginalFilename();
//獲取文件的後綴
String suffix=filename.substring(filename.lastIndexOf("."));
//隨機生成文件保存時的名字
String name=UUID.randomUUID().toString().replace("-", "");
filename=name+suffix;
//獲取保存文件的文件夾
String path = request.getServletContext().getRealPath("/upload");
File dir = new File(path);
//判斷目標文件夾是否存在,不存在則創建
if(!dir.exists()){
dir.mkdirs();
}
//生成目標文件夾
File file=new File(dir,filename);
//保存文件
head.transferTo(file);
//在後端直接發送一個前端頁面的彈窗
return "<script>alert('ok nb')</script>";
}
}
2、SpringMVC 文件下載
(1)文件下載核心代碼
// 設置響應頭,響應類型爲附件,瀏覽器將會用下載的方式處理響應的信息
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filePath, "UTF-8"));
(2)示例
① Controller 層
@Controller
public class FileController {
@RequestMapping("download")
public void download(String filename,HttpServletResponse response) throws Exception{
//獲取下載文件所在地址和文件名,拼接成下載路徑
FileInputStream fis=new FileInputStream("D:\\upload\\"+filename);
//下載文件核心代碼
response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(filename, "UTF-8"));
//創建輸出流寫入文件
OutputStream os=response.getOutputStream();
byte[] b=new byte[1024];
int length=0;
while((length=fis.read(b))>0) {
os.write(b, 0, length);
}
fis.close();
os.close();
}
}
② 前端發送請求簡單示例
<a href="download.do?filename=1.zip">點擊下載</a>