9uu域名更新自動轉(zhuǎn)跳seo怎么優(yōu)化步驟
目錄
九、SpringMVC 中的 AJAX 請求
1、簡單示例
2、@RequestBody(重點關(guān)注“賦值形式”)
3、@ResponseBody(經(jīng)常用)
4、為什么不用手動接收 JSON 字符串、轉(zhuǎn)換 JSON 字符串
5、@RestController
十、文件上傳與下載
1、ResponseEntity
2、文件下載
3、文件上傳
4、解決文件重名導(dǎo)致內(nèi)容覆蓋的問題
十一、攔截器
1、攔截器的三個方法
2、攔截器的簡單示例
3、攔截器的配置方法
4、多個攔截器的執(zhí)行順序
5、preHandle 返回 false 的情況
十二、控制器方法異常處理
1、異常處理解析器
2、使用 xml 配置
3、顯示域?qū)ο蟮臄?shù)據(jù)
4、使用注解配置
九、SpringMVC 中的 AJAX 請求
Ajax 請求的前端發(fā)送有很多種方式,可以使用 Vue,也可以使用 JQuery。
1、簡單示例
下面是一個服務(wù)器獲取請求頭中的信息,并響應(yīng)一個字符串的簡單 Ajax 示例。
(1)index.html?
使用 Ajax 請求傳遞兩個請求參數(shù),并接收服務(wù)器端的響應(yīng)數(shù)據(jù),格式為 text。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" th:src="@{/static/js/JQuery-3.7.0.js}"></script><script type="text/javascript">$(function() {$("#ajaxButton").click(function() {$.ajax({url:"/Demo_Rest/testAjax",data: { // 在請求頭"username": "admin","password": "123456",},type: "get",dataType: "text",success: function(data) {alert(data);}});});});</script>
</head>
<body><h1>index 頁面</h1><input id="ajaxButton" type="button" value="發(fā)起 ajax 請求"/>
</body>
</html>
(2)java 代碼
注意,因為 Ajax 請求是做局部更新,因此不需要進行轉(zhuǎn)發(fā),也不需要重定向,返回類型是用 void 即可。?
@RequestMapping(value = "/testAjax")
public void testAjax(String username, String password, HttpServletResponse resp) throws IOException {System.out.println("username: " + username + ", password: " + password);resp.getWriter().write("testAjax");return;
}
2、@RequestBody(重點關(guān)注“賦值形式”)
@RequestBody 可以獲取請求體,在控制器方法設(shè)置一個形參,使用 @RequestBody 進行標識,當前請求的請求體就會為當前注解所標識的形參賦值。
需要注意的是,使用了 @RequestBody 后,Ajax 就要發(fā)送 POST 請求,而不能發(fā)送 GET 請求??梢?#xff1a;https://juejin.cn/post/7222833868503236667
(1)導(dǎo)入依賴 jackson
Spring 默認的 json 解析器就是 jackson,其實沒必要導(dǎo)入,但是可以寫上。
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version>
</dependency>
(2)@RequestBody 的賦值形式
當?@RequestBody 修飾一個方法參數(shù)時,會將所有的請求參數(shù)都賦值給方法參數(shù)。什么意思呢?
假設(shè)請求體中的請求參數(shù)為 username?: "admin", password : "123456", age : 12:
- 若方法參數(shù)為 @RequestBody String user:
- 若方法參數(shù)為 @RequestBody User user:
- 若方法參數(shù)為 @RequestBody Map<String, Object> map:
關(guān)于?@RequestBody 與 @RequestParam 的區(qū)別?以及?是否添加 @RequestBody 注解,這兩個問題也很重要。
可以參考:https://blog.csdn.net/weixin_43606226/article/details/106545024
(3)使用注意
僅對使用 JQuery 的情況而言,其他方法(比如:vue)不需要注意。
當我們傳遞 JSON 格式的數(shù)據(jù)時:
- 需要添加?contentType: "application/json";
- 需要使用?JSON.stringify(data);
<script type="text/javascript">$(function() {$("#ajaxButton").click(function() {var user = {username: "admin",password: "123456",age: 12,};$.ajax({url:"/Demo_Rest/testAjax",data: JSON.stringify(user),contentType: "application/json",type: "post",dataType: "text", /* 返回數(shù)據(jù)類型 */success: function(data) {alert(data);}});});});
</script>
3、@ResponseBody(經(jīng)常用)
@ResponseBody 用于標識一個控制器方法,可以將該方法的返回值(String、實體類)直接作為響應(yīng)體響應(yīng)到瀏覽器。
一般情況下,就是將一個 JSON 格式的 json 字符串,響應(yīng)到客戶端,再由客戶端瀏覽器將其解析成 json 對象。而使用了 @ResponseBody 和 @RequestBody 之后,就不是這樣的了,具體后面解釋。
(1)返回普通字符串的情況
@RequestMapping(value = "/testResponseBody")
@ResponseBody
public String responseBody() {return "success";
}
訪問 /testResponseBody,那么就會出現(xiàn)如下結(jié)果:
而這顯然不是我們之前所編寫的 success.html 頁面,而僅僅是一個字符串數(shù)據(jù)。
(2)返回實體類的情況
將 user 的信息發(fā)送給服務(wù)器,服務(wù)器修改 username 后,響應(yīng)新的 user 信息。
(2-1)index.html
<script type="text/javascript">$(function() {$("#ajaxButton").click(function() {var user = {username: "admin",password: "123456",age: 12,};$.ajax({url:"/Demo_Rest/testResponseBody",data: JSON.stringify(user),contentType: "application/json",type: "post",dataType: "json", /* 返回數(shù)據(jù)類型 */success: function(data) {alert(data.username);}});});});
</script>
(2-2)Java 代碼
@RequestMapping(value = "/testResponseBody")
@ResponseBody
public User responseBody(@RequestBody User user) {System.out.println(user);user.setUsername("wyt");return user;
}
(3)常見的 Java 對象轉(zhuǎn)換為 Json 之后的類型
- 實體類:Json 對象;
- Map:Json 對象;
- List:Json 數(shù)組;
4、為什么不用手動接收 JSON 字符串、轉(zhuǎn)換 JSON 字符串
在 SpringMVC 的核心配置文件中開啟 mvc 的注解驅(qū)動:
<mvc:annotation-driven/>
此時在 HandlerAdaptor 中會自動裝配一個消息轉(zhuǎn)換器:MappingJackson2HttpMessageConverter,它是由 JackSon 這個 JSON 解析器提供的,其作用有:
- 既可以將瀏覽器傳遞的請求參數(shù)構(gòu)成的 JSON 字符串轉(zhuǎn)換為 JSON 對象
- 也可以將響應(yīng)到瀏覽器的 Java 對象轉(zhuǎn)換為 JSON 格式的字符串
5、@RestController
實際開發(fā)中,@ResponseBody 使用的情況非常多,而我們只需要給控制類寫上一個 @RestController,就可以同時起到 @Controller 和 @ResponseBody 的作用。
十、文件上傳與下載
文件上傳與下載的方式有很多,不一定要用 ResponseEntity 這種方法。
1、ResponseEntity
ResponseEntity 是一個類型,用于控制器方法的返回值類型,該控制器方法的返回值就是響應(yīng)到瀏覽器的響應(yīng)報文。
有關(guān)文件上傳與下載的步驟,可以查看:https://blog.csdn.net/joyride_run/article/details/132814877
2、文件下載
在此最奉上喜歡的圖片(R-C.jpg):
(1)Java 代碼?
@RequestMapping(value = "/fileDownload", method = RequestMethod.GET)
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {// 1.獲取要下載的文件名String downloadFilename = "R-C.jpg";// 2.獲取ServletContext對象ServletContext servletContext = session.getServletContext();// 3.獲取服務(wù)器中文件的真實路徑String realPath = servletContext.getRealPath("/static/img/" + downloadFilename);// 4.創(chuàng)建輸入流InputStream inputStream = new FileInputStream(realPath);// 5.創(chuàng)建字節(jié)數(shù)組int count = 0;while (count == 0) count = inputStream.available();// 防止數(shù)據(jù)未送達,導(dǎo)致count=0,但是本地讀文件一般不會有這種錯誤byte[] bytes = new byte[count];// 6.將流讀到字節(jié)數(shù)組中inputStream.read(bytes);// 7.創(chuàng)建HttpHeaders對象設(shè)置響應(yīng)頭信息MultiValueMap<String, String> headers = new HttpHeaders();headers.add("Content-Disposition", "attachment;filename=" + downloadFilename);// 8.設(shè)置響應(yīng)狀態(tài)碼HttpStatus statusCode = HttpStatus.OK;// 9.創(chuàng)建ResponseEntity對象ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);// 10.關(guān)閉輸入流inputStream.close();return responseEntity;
}
(2)index.html?
<a th:href="@{/fileDownload}">下載R-C.jpg文件</a>
(3)效果
3、文件上傳
(1)如何獲取上傳的文件信息
- SpringMVC 將上傳的文件封裝到 MultipartFile 對象中,通過此對象可以獲取文件相關(guān)信息
- 使用?MultipartFile 對象需要配置文件上傳解析器
- 使用?MultipartFile 對象需要引入依賴 commons-fileupload
由于 MultipartResolver 是一個接口,我們需要配置它的實現(xiàn)類 CommonsMulitipartResolver。
<!-- 配置文件上傳解析器id 必須為 multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.5</version>
</dependency>
(2)輸出文件名
- 使用 MultipartFile 的類方法?getOriginalFilename(),可以獲取上傳文件的文件名。
(2-1)Java 代碼
@RequestMapping(value = "/fileUpload", method = RequestMethod.POST)
public String upload(MultipartFile multipartFile) {String filename = multipartFile.getOriginalFilename();System.out.println(filename);return "success";
}
(2-2)index.html
- 這里有一個很重要的點,就是表單上傳文件的 name 的屬性值必須要與 MultipartFile 的參數(shù)名相同。
<form th:action="@{/fileUpload}" method="post" enctype="multipart/form-data">頭像:<input type="file" name="multipartFile"/> <br/><input type="submit" value="上傳"/>
</form>
(3)保存文件
- 保存文件使用 SpringMVC 封裝的 transferTo() 方法,則不需要再使用 IO 流進行持久化存儲;
- 因為我們不知道目錄之間的分隔符是 / 還是 \,所以可以使用 File.separator;
@RequestMapping(value = "/fileUpload", method = RequestMethod.POST)
public String upload(MultipartFile multipartFile, HttpSession session) throws IOException {// 1.獲取上傳的文件名String filename = multipartFile.getOriginalFilename();// 2.獲取文件保存的路徑ServletContext servletContext = session.getServletContext();String dir = servletContext.getRealPath("/static/img/upload");// 3.創(chuàng)建文件對象File file = new File(dir);if (!file.exists()) {file.mkdirs(); // mkdir 只能創(chuàng)建下一級目錄}// 4.保存文件String savePath = dir + File.separator + filename;System.out.println(savePath);multipartFile.transferTo(new File(savePath));return "success";
}
4、解決文件重名導(dǎo)致內(nèi)容覆蓋的問題
解決方法:
- 使用時間戳;
- 使用 UUID;
(1)使用 UUID
String filename = multipartFile.getOriginalFilename();
String extensionName = filename.substring(filename.lastIndexOf("."));
filename = UUID.randomUUID().toString() + extensionName;
十一、攔截器
SpringMVC 中的攔截器用于攔截控制器方法的執(zhí)行。作用類似于 Filter,但不是完全相同。
默認情況下,未指定對哪些資源進行攔截,則會攔截所有交給 DispatcherServlet 的請求,比如:對 index 的訪問,對 controller 的訪問。
1、攔截器的三個方法
SpringMVC 中的攔截器有三個抽象方法:
- preHandle:控制器方法執(zhí)行之前執(zhí)行,其返回值表示對控制器方法的放行(true)或攔截(false);
- postHandle:控制器方法執(zhí)行之后執(zhí)行;
- afterComplation:處理完視圖和模型數(shù)據(jù),渲染視圖完畢之后執(zhí)行;
2、攔截器的簡單示例
SpringMVC 中的攔截器需要實現(xiàn) HandlerInterceptor,并且在 SpringMVC 的配置文件中進行配置:
(1)SpringMVC 配置文件
- 由 SpringMVC 加載,不需要寫 id
<!-- 配置攔截器 -->
<mvc:interceptors><bean class="com.demo.interceptor.FirstInterceptor"></bean>
</mvc:interceptors>
(2)FirstInterceptor.java
- 重寫三個方法,其中 preHandle 的返回 false
package com.demo.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class FirstInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("FirstInterceptor -- preHandle");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("FirstInterceptor -- postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("FirstInterceptor -- afterCompletion");}
}
(3)運行結(jié)果
訪問 index.html,觀察頁面響應(yīng)結(jié)果和控制臺輸出:
- 首頁無法訪問,以及如下輸出
3、攔截器的配置方法
(1)直接嵌套 <bean> 標簽
- 在 SpringMVC 的配置文件中直接寫上:
<mvc:interceptors><bean class="com.demo.interceptor.FirstInterceptor"></bean>
</mvc:interceptors>
(2)鏈接外部?<bean>?
- <ref> 鏈接 <mvc:interceptors> 外部的 <bean>?
<bean id="firstInterceptor" class="com.demo.interceptor.FirstInterceptor"/>
<!-- 配置攔截器 -->
<mvc:interceptors><ref bean="firstInterceptor"/>
</mvc:interceptors>
(3)掃描注解方式
- 在?FirstInterceptor 上添加 @Component,然后直接 <ref>
@Component(value = "firstInterceptor")
public class FirstInterceptor implements HandlerInterceptor {}
<mvc:interceptors><ref bean="firstInterceptor"/>
</mvc:interceptors>
(4)<mvc:interceptor>
它有兩個重要的屬性:
- mvc:mapping path ="":表示需要攔截的請求;
- mvc:exclude-mapping path="":表示不需要攔截的請求;
- <ref> 和 <bean> 要寫在這兩個屬性之后;
?設(shè)置了這兩個請求之后,就會按照設(shè)置的內(nèi)容進行攔截,而不是去攔截 DispatcherServlet。
<mvc:interceptors><mvc:interceptor><mvc:mapping path="/*"/><mvc:exclude-mapping path="/static"/><ref bean="firstInterceptor"/></mvc:interceptor>
</mvc:interceptors>
還需要注意,path = " /* ",表示的是,攔截 / 后的一層目錄的請求,也就是說 /abc/* 這類請求是無法攔截的。
想要攔截對所有資源的請求,又要求使用?mvc:mapping,那么可以寫 path = " /** "。
4、多個攔截器的執(zhí)行順序
我們設(shè)置兩個攔截器:FirstInterceptor、SecondInterceptor。將他們的 preHandler 都設(shè)置為放行(true)。
(1)在 SpringMVC 中作如下順序
- 先配置 FirstInterceptor,后配置 SecondInterceptor。
<mvc:interceptors><ref bean="firstInterceptor"/><ref bean="secondInterceptor"/>
</mvc:interceptors>
(2)訪問 index.html,觀察輸出
顯然可以看出,執(zhí)行 preHandle() 方法,是按照配置順序來執(zhí)行;執(zhí)行另外 2 個方法,是按照配置的逆序來執(zhí)行。
5、preHandle 返回 false 的情況
當攔截器 C 的 preHandle() 返回了 false:
- C 以及 C 之前的攔截器的 preHandle() 都會執(zhí)行;
- 所有的 postHandle() 都不執(zhí)行;
- C 之前的攔截器的 afterComplation() 都會執(zhí)行;
十二、控制器方法異常處理
1、異常處理解析器
對于異常處理解析器,我們可以配置,也可以不配置,SpringMVC 已經(jīng)默認使用了解析器。
- SpringMVC 提供了一個處理控制器方法執(zhí)行過程中所出現(xiàn)的異常的接口:HandlerExceptionResolver。
- HandlerExceptionResolver 接口的實現(xiàn)類有:DefaultHandlerExceptionResolver(SpringMVC 默認使用)和 SimpleMappingExceptionResolver(用戶自定義使用)。
在他們的底層實現(xiàn)中,會返回一個 ModelAndView,這代表著我們可以利用 Model 將錯誤信息保存到 Request 域,還可以利用 View 進行頁面跳轉(zhuǎn)。比如:跳轉(zhuǎn)至 error404.html 頁面,并顯示錯誤信息。?
2、使用 xml 配置
(1)Java 代碼
- 手動添加一個 1 / 0 的異常。
@RequestMapping(value = "/testException")
public String testException() {int i = 1 / 0;return "success";
}
(2)index.html 以及 error666.html
- 訪問控制器方法
<body><a th:href="@{/testException}">測試異常處理</a>
</body>
- 展示異常頁面
<body><h1>error666 頁面</h1>
</body>
(3)配置異常解析器的 View
- prop 的 key 表示處理器方法執(zhí)行過程中出現(xiàn)的異常;
- prop的 value 表示若出現(xiàn)指定異常時,設(shè)置一個邏輯視圖,跳轉(zhuǎn)到指定頁面;
<!-- 配置異常解析器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><prop key="java.lang.ArithmeticException">error666</prop><!--.... 其他異常--></props></property>
</bean>
(4)運行結(jié)果
- 由于 1 / 0 出現(xiàn)了異常,因此沒有轉(zhuǎn)發(fā)到 success.html 頁面,而是來到了 error666.html 頁面。
3、顯示域?qū)ο蟮臄?shù)據(jù)
剛才的示例中,只有 View 的功能,現(xiàn)在我們要添加 Model 的功能。
<property> 除了前面的?exceptionMappings,還有一個?exceptionAttribute,他們就是分別表示 View 和 Model:
- 通過 exceptionAttribute 的 value 設(shè)置一個域數(shù)據(jù)的屬性名,、即可通過這個屬性名訪問異常信息。
(1)SpringMVC 配置文件
- 添加了?<property name="exceptionAttribute" value="exceptionInfo"/>
<!-- 配置異常解析器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><prop key="java.lang.ArithmeticException">error666</prop><!--.... 其他異常--></props></property><property name="exceptionAttribute" value="exceptionInfo"/></bean>
(2)error666.html
- 獲取域?qū)ο?#xff0c;顯示異常信息
<body><h1>error666 頁面</h1><p th:text="${exceptionInfo}"></p>
</body>
(3)輸出結(jié)果
4、使用注解配置
(1)@ControllerAdvice
- @ControllerAdvice 將當前類標識為異常處理的 Controller。
(2)@ExceptionHandler
- @ExceptionHandler 有一個 value 屬性,需要繼承自 Throwable 的異常類的?Class 數(shù)組作為屬性值,其中傳入我們需要處理的異常類的 class 屬性。
(3)返回值
- 如果使用 String,那么返回值可以設(shè)置成錯誤頁面的邏輯視圖;
- 如果使用 ModelAndView,就使用 setView 方法設(shè)置邏輯視圖;
(4)Controller
- 產(chǎn)生一個 math 異常
@RequestMapping(value = "/testException")
public String testException() {int i = 1 / 0;return "success";
}
(5)ExceptionHandler
- 由于 Controller 有 math 方面的異常,因此用?ArithmeticException
@ControllerAdvice
public class ExceptionController {@ExceptionHandler(value = {ArithmeticException.class})public String handleException(Model model, Throwable exception) {model.addAttribute("exceptionInfo", exception);return "error666";}
}
(6)運行結(jié)果