SpringMVC(2)- 非同步呼叫、非同步請求-跨域訪問、攔截器、異常處理、實用技術
1 非同步呼叫
1.1 傳送非同步請求(回顧)
<a href="javascript:void(0);" id="testAjax">訪問controller</a>
<script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
$("#testAjax").click(function(){ //為id="testAjax"的元件繫結點選事件
$.ajax({ //傳送非同步呼叫
type:"POST", //請求方式: POST請求
url:"ajaxController", //請求引數(也就是請求內容)
data:'ajax message', //請求引數(也就是請求內容)
dataType:"text", //響應正文型別
contentType:"application/text", //請求正文的MIME型別
});
});
});
</script>
1.2 接受非同步請求引數
名稱: @RequestBody
型別: 形參註解
位置:處理器類中的方法形參前方
作用:將非同步提交資料組織成標準請求引數格式,並賦值給形參
範例:
@RequestMapping("/ajaxController")
public String ajaxController(@RequestBody String message){
System.out.println(message);
return "page.jsp";
}
- 註解新增到Pojo引數前方時,封裝的非同步提交資料按照Pojo的屬性格式進行關係對映
- 註解新增到集合引數前方時,封裝的非同步提交資料按照集合的儲存結構進行關係對映
@RequestMapping("/ajaxPojoToController")
//如果處理引數是POJO,且頁面傳送的請求資料格式與POJO中的屬性對應,@RequestBody註解可以自動對映對應請求資料到POJO中
//注意:POJO中的屬性如果請求資料中沒有,屬性值為null,POJO中沒有的屬性如果請求資料中有,不進行對映
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
//如果處理引數是List集合且封裝了POJO,且頁面傳送的資料是JSON格式的物件陣列,資料將自動對映到集合引數中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
1.3 非同步請求接受響應資料
- 方法返回值為Pojo時,自動封裝資料成json物件資料
@RequestMapping("/ajaxReturnJson")
@ResponseBody
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jockme");
user.setAge(40);
return user;
}
- 方法返回值為List時,自動封裝資料成json物件陣列資料
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
//基於jackon技術,使用@ResponseBody註解可以將返回的儲存POJO物件的集合轉成json陣列格式資料
public List ajaxReturnJsonList(){
System.out.println("controller return json list...");
User user1 = new User();
user1.setName("Tom");
user1.setAge(3);
User user2 = new User();
user2.setName("Jerry");
user2.setAge(5);
ArrayList al = new ArrayList();
al.add(user1);
al.add(user2);
return al;
}
2 非同步請求-跨域訪問
2.1 跨域訪問介紹
- 當通過域名A下的操作訪問域名B下的資源時,稱為跨域訪問
- 跨域訪問時,會出現無法訪問的現象
2.2 跨域環境搭建
- 為當前主機新增備用域名
- 修改windows安裝目錄中的host檔案
- 格式: ip 域名
- 動態重新整理DNS
- 命令: ipconfig /displaydns
- 命令: ipconfig /flushdns
2.3 跨域訪問支援
名稱: @CrossOrigin
型別: 方法註解 、 類註解
位置:處理器類中的方法上方 或 類上方
作用:設定當前處理器方法/處理器類中所有方法支援跨域訪問
範例:
@RequestMapping("/cross")
@ResponseBody
//使用@CrossOrigin開啟跨域訪問
//標註在處理器方法上方表示該方法支援跨域訪問
//標註在處理器類上方表示該處理器類中的所有處理器方法均支援跨域訪問
@CrossOrigin
public User cross(HttpServletRequest request){
System.out.println("controller cross..."+request.getRequestURL());
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
3 攔截器
3.1 攔截器概念
- 請求處理過程解析
攔截器( Interceptor)是一種動態攔截方法呼叫的機制
作用:
1. 在指定的方法呼叫前後執行預先設定後的的程式碼
2. 阻止原始方法的執行
核心原理: AOP思想
攔截器鏈:多個攔截器按照一定的順序,對原始被呼叫功能進行增強
- 攔截器VS過濾器
歸屬不同: Filter屬於Servlet技術, Interceptor屬於SpringMVC技術
攔截內容不同: Filter對所有訪問進行增強, Interceptor僅針對SpringMVC的訪問進行增強
3.2 自定義攔截器開發過程
-
實現HandlerInterceptor介面
//自定義攔截器需要實現HandleInterceptor介面 public class MyInterceptor implements HandlerInterceptor { //處理器執行之前執行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("前置執行----a1"); //返回值為false將攔截原始處理器的執行 //如果配置多攔截器,返回值為false將終止當前攔截器後面配置的攔截器的執行 return true; } //處理器執行之後執行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("後置執行----b1"); } //所有攔截器的後置執行全部結束後,執行該操作 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("完成執行----c1"); } //三個方法的執行順序為 preHandle -> postHandle -> afterCompletion //如果preHandle返回值為false,三個方法僅執行preHandle }
-
配置攔截器
配置攔截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/showPage"/>
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
注意:配置順序為先配置執行位置,後配置執行類
3.3 攔截器執行流程
3.4 攔截器配置與方法引數
3.4.1 前置處理方法
原始方法之前執行
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
- 引數
request:請求物件
response:響應物件
handler:被呼叫的處理器物件,本質上是一個方法物件,對反射中的Method物件進行了再包裝 - 返回值
返回值為false,被攔截的處理器將不執行
3.4.2 後置處理方法
原始方法執行後執行,如果原始方法被攔截,則不執行
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
引數
modelAndView:如果處理器執行完成具有返回結果,可以讀取到對應資料與頁面資訊,並進行調整
3.4.3 完成處理方法
攔截器最後執行的方法,無論原始方法是否執行
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion");
}
引數
ex:如果處理器執行過程中出現異常物件,可以針對異常情況進行單獨處理
3.5 攔截器配置項
<mvc:interceptors>
<!--開啟具體的攔截器的使用,可以配置多個-->
<mvc:interceptor>
<!--設定攔截器的攔截路徑,支援*通配-->
<!--/** 表示攔截所有對映-->
<!--/* 表示攔截所有/開頭的對映-->
<!--/user/* 表示攔截所有/user/開頭的對映-->
<!--/user/add* 表示攔截所有/user/開頭,且具體對映名稱以add開頭的對映-->
<!--/user/*All 表示攔截所有/user/開頭,且具體對映名稱以All結尾的對映-->
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<!--設定攔截排除的路徑,配置/**或/*,達到快速配置的目的-->
<mvc:exclude-mapping path="/b*"/>
<!--指定具體的攔截器類-->
<bean class="MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.6 多攔截器配置
責任鏈模式
責任鏈模式是一種行為模式
特徵:
沿著一條預先設定的任務鏈順序執行,每個節點具有獨立的工作任務
優勢:
獨立性:只關注當前節點的任務,對其他任務直接放行到下一節點
隔離性:具備鏈式傳遞特徵,無需知曉整體鏈路結構,只需等待請求到達後進行處理即可
靈活性:可以任意修改鏈路結構動態新增或刪減整體鏈路責任
解耦:將動態任務與原始任務解耦
弊端:
鏈路過長時,處理效率低下
可能存在節點上的迴圈引用現象,造成死迴圈,導致系統崩潰
4 異常處理
4.1 異常處理器
HandlerExceptionResolver介面(異常處理器)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("異常處理器正在執行中");
ModelAndView modelAndView = new ModelAndView();
//定義異常現象出現後,反饋給使用者檢視的資訊
modelAndView.addObject("msg","出錯啦! ");
//定義異常現象出現後,反饋給使用者檢視的頁面
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
根據異常的種類不同,進行分門別類的管理,返回不同的資訊
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("my exception is running ...."+ex);
ModelAndView modelAndView = new ModelAndView();
if( ex instanceof NullPointerException){
modelAndView.addObject("msg","空指標異常");
}else if ( ex instanceof ArithmeticException){
modelAndView.addObject("msg","算數運算異常");
}else{
modelAndView.addObject("msg","未知的異常");
}
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
4.2 註解開發異常處理器
- 使用註解實現異常分類管理
名稱: @ControllerAdvice
型別: 類註解
位置:異常處理器類上方
作用:設定當前類為異常處理器類
範例:
@Component
@ControllerAdvice
public class ExceptionAdvice {
}
- 使用註解實現異常分類管理
名稱: @ExceptionHandler
型別: 方法註解
位置:異常處理器類中針對指定異常進行處理的方法上方
作用:設定指定異常的處理方式
範例:
說明:處理器方法可以設定多個
@ExceptionHandler(Exception.class)
@ResponseBody
public String doOtherException(Exception ex){
return "出錯啦,請聯絡管理員! ";
}
4.3 異常處理解決方案
- 異常處理方案
- 業務異常:
傳送對應訊息傳遞給使用者,提醒規範操作 - 系統異常:
傳送固定訊息傳遞給使用者,安撫使用者
傳送特定訊息給運維人員,提醒維護
記錄日誌 - 其他異常:
傳送固定訊息傳遞給使用者,安撫使用者
傳送特定訊息給程式設計人員,提醒維護
納入預期範圍內
記錄日誌
- 業務異常:
4.4 自定義異常
-
異常定義格式
//自定義異常繼承RuntimeException,覆蓋父類所有的構造方法 public class BusinessException extends RuntimeException { public BusinessException() { } public BusinessException(String message) { super(message); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
-
異常觸發方式
if(user.getName().trim().length()<4) { throw new BusinessException("使用者名稱長度必須在2-4位之間,請重新輸入! "); }
-
通過自定義異常將所有的異常現象進行分類管理,以統一的格式對外呈現異常訊息
5 實用技術
5.1 檔案上傳下載
- 上傳檔案過程分析
-
MultipartResolver介面
-
MultipartResolver介面定義了檔案上傳過程中的相關操作,並對通用性操作進行了封裝
-
MultipartResolver介面底層實現類CommonsMultipartResovler
-
CommonsMultipartResovler並未自主實現檔案上傳下載對應的功能,而是呼叫了apache的檔案上傳下載元件
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
-
檔案上傳下載實現
- 頁面表單
<form action="/fileupload" method="post" enctype="multipart/form-data"> 上傳LOGO: <input type="file" name="file"/><br/> <input type="submit" value="上傳"/> </form>
- SpringMVC配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean>
- 控制器
@RequestMapping(value = "/fileupload") public void fileupload(MultipartFile file){ file.transferTo(new File("file.png")); }
5.2 檔案上傳注意事項
- 檔案命名問題, 獲取上傳檔名,並解析檔名與副檔名
- 檔名過長問題
- 檔案儲存路徑
- 重名問題
@RequestMapping(value = "/fileupload")
//引數中定義MultipartFile引數,用於接收頁面提交的type=file型別的表單,要求表單名稱與引數名相同
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException {
System.out.println("file upload is running ..."+file);
// MultipartFile引數中封裝了上傳的檔案的相關資訊
// System.out.println(file.getSize());
// System.out.println(file.getBytes().length);
// System.out.println(file.getContentType());
// System.out.println(file.getName());
// System.out.println(file.getOriginalFilename());
// System.out.println(file.isEmpty());
//首先判斷是否是空檔案,也就是儲存空間佔用為0的檔案
if(!file.isEmpty()){
//如果大小在範圍要求內正常處理,否則丟擲自定義異常告知使用者(未實現)
//獲取原始上傳的檔名,可以作為當前檔案的真實名稱儲存到資料庫中備用
String fileName = file.getOriginalFilename();
//設定儲存的路徑
String realPath = request.getServletContext().getRealPath("/images");
//儲存檔案的方法,指定儲存的位置和檔名即可,通常檔名使用隨機生成策略產生,避免檔名衝突問題
file.transferTo(new File(realPath,file.getOriginalFilename()));
}
//測試一次性上傳多個檔案
if(!file1.isEmpty()){
String fileName = file1.getOriginalFilename();
//可以根據需要,對不同種類的檔案做不同的儲存路徑的區分,修改對應的儲存位置即可
String realPath = request.getServletContext().getRealPath("/images");
file1.transferTo(new File(realPath,file1.getOriginalFilename()));
}
if(!file2.isEmpty()){
String fileName = file2.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/images");
file2.transferTo(new File(realPath,file2.getOriginalFilename()));
}
return "page.jsp";
}
5.4 Restful風格配置
5.4.1 Rest
- Rest( REpresentational State Transfer) 一種網路資源的訪問風格,定義了網路資源的訪問方式
- 傳統風格訪問路徑
http://localhost/user/get?id=1
http://localhost/deleteUser?id=1 - Rest風格訪問路徑
http://localhost/user/1
- 傳統風格訪問路徑
- Restful是按照Rest風格訪問網路資源
- 優點
隱藏資源的訪問行為,通過地址無法得知做的是何種操作
書寫簡化
5.4.2 Rest行為約定方式
GET(查詢) http://localhost/user/1 GET
POST(儲存) http://localhost/user POST
PUT(更新) http://localhost/user PUT
DELETE(刪除) http://localhost/user DELETE
**注意:**上述行為是約定方式,約定不是規範,可以打破,所以稱Rest風格,而不是Rest規範
5.4.3 Restful開發入門
//設定rest風格的控制器
@RestController
//設定公共訪問路徑,配合下方訪問路徑使用
@RequestMapping("/user/")
public class UserController {
//rest風格訪問路徑完整書寫方式
@RequestMapping("/user/{id}")
//使用@PathVariable註解獲取路徑上配置的具名變數,該配置可以使用多次
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running ....");
return "success.jsp";
}
//rest風格訪問路徑簡化書寫方式,配合類註解@RequestMapping使用
@RequestMapping("{id}")
public String restLocation2(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收GET請求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.GET)
//接收GET請求簡化配置方式
@GetMapping("{id}")
public String get(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收POST請求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.POST)
//接收POST請求簡化配置方式
@PostMapping("{id}")
public String post(@PathVariable Integer id){
System.out.println("restful is running ....post:"+id);
return "success.jsp";
}
//接收PUT請求簡化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.PUT)
//接收PUT請求簡化配置方式
@PutMapping("{id}")
public String put(@PathVariable Integer id){
System.out.println("restful is running ....put:"+id);
return "success.jsp";
}
//接收DELETE請求簡化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
//接收DELETE請求簡化配置方式
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
<!--配置攔截器,解析請求中的引數_method,否則無法發起PUT請求與DELETE請求,配合頁面表單使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
開啟SpringMVC對Restful風格的訪問支援過濾器,即可通過頁面表單提交PUT與DELETE請求
頁面表單使用隱藏域提交請求型別,引數名稱固定為_method,必須配合提交型別method=post使用
<form action="/user/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit"/>
</form>
- Restful請求路徑簡化配置方式
@RestController
public class UserController {
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
public String restDelete(@PathVariable String id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
5.5 postman工具安裝與使用
postman 是 一款可以傳送Restful風格請求的工具,方便開發除錯。首次執行需要聯網註冊
相關文章
- SpringMVC【校驗器、統一處理異常、RESTful、攔截器】SpringMVCREST
- SpringMvc-10.14上傳、攔截器、異常處理SpringMVC
- 【SpringMVC】檔案上傳與下載、攔截器、異常處理器SpringMVC
- tornado非同步請求非阻塞非同步
- options 請求跨域問題處理跨域
- SpringBoot解決跨域請求攔截Spring Boot跨域
- .net webapi 處理前端請求跨域問題WebAPI前端跨域
- 實戰SpringCloud通用請求欄位攔截處理SpringGCCloud
- DeferredResult——非同步請求處理非同步
- 用.Net處理xmlHttp傳送非同步請求XMLHTTP非同步
- Ajax+SpringMVC實現跨域請求SpringMVC跨域
- 使用cors完成跨域請求處理CORS跨域
- springMVC(二)——處理返回值、常用註解、檔案上傳下載、異常處理、過濾器、攔截器SpringMVC過濾器
- vue+axois 封裝請求+攔截器(請求鎖+統一處理錯誤碼)Vue封裝
- 基於 HTTP 請求攔截,快速解決跨域和代理 MockHTTP跨域Mock
- Angular專案簡單使用攔截器 httpClient 請求響應處理AngularHTTPclient
- SpringMVC異常處理SpringMVC
- 同步非同步,阻塞非阻塞非同步
- 非同步、同步、阻塞、非阻塞非同步
- 同步、非同步、阻塞、非阻塞非同步
- SpringMVC攔截器SpringMVC
- SVN異常處理——禁止訪問
- 面試常見的非技術問題面試
- SpringMVC請求處理流程SpringMVC
- springmvc處理ajax請求SpringMVC
- iOS 同步請求 非同步請求 GET請求 POST請求iOS非同步
- 同步非同步 與 阻塞非阻塞非同步
- 理解阻塞、非阻塞、同步、非同步非同步
- 同步、非同步,阻塞、非阻塞理解非同步
- 同步、非同步、阻塞與非阻塞非同步
- 同步、非同步、阻塞和非阻塞非同步
- 同步阻塞、同步非阻塞、多路複用的介紹
- SpringBoot 教程之處理非同步請求Spring Boot非同步
- python非同步IO與批量請求處理Python非同步
- 【SpringMVC】 4.2 異常處理SpringMVC
- tidb之dm叢集同步異常處理TiDB
- MYSQL 主從庫同步 異常處理彙總MySql
- Jquery 非同步跨域問題jQuery非同步跨域