SpringMVC將請求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為JSON格式的幾種方式
RESTful
概述
REST:Representational State Transfer,表現(xiàn)層資源狀態(tài)轉(zhuǎn)移。
表現(xiàn)層:前端的視圖頁面和后端的控制層就是表現(xiàn)層
資源
資源是一種看待服務(wù)器的方式,即,將服務(wù)器看作是由很多離散的資源組成。每個資源是服務(wù)器上一個可命名的抽象概念。因為資源是一個抽象的概念,所以它不僅僅能代表服務(wù)器文件系統(tǒng)中的一個文件、數(shù)據(jù)庫中的一張表等等具體的東西,可以將資源設(shè)計的要多抽象有多抽象,只要想象力允許而且客戶端應(yīng)用開發(fā)者能夠理解。與面向?qū)ο笤O(shè)計類似,資源是以名詞為核心來組織的,首先關(guān)注的是名詞。一個資源可以由一個或多個URI來標識。URI既是資源的名稱,也是資源在Web上的地址。對某個資源感興趣的客戶端應(yīng)用,可以通過資源的URI與其進行交互。
URI :統(tǒng)一資源標識符(Uniform Resource Identifier,URI)是一個用于標識某一互聯(lián)網(wǎng)資源名稱的字符串。
統(tǒng)一資源定位符(Uniform Resource Locator,URL),統(tǒng)一資源名稱(Uniform Resource Name,URN)是URI的子集。
資源的表述
資源的表述是一段對于資源在某個特定時刻的狀態(tài)的描述??梢栽诳蛻舳?服務(wù)器端之間轉(zhuǎn)移(交換)。資源的表述可以有多種格式,例如 HTML/XML/JSON/純文本/圖片/視頻/音頻等等。資源的表述格式可以通過協(xié)商機制來確定。請求-響應(yīng)方向的表述通常使用不同的格式。
狀態(tài)轉(zhuǎn)移
狀態(tài)轉(zhuǎn)移說的是:在客戶端和服務(wù)器端之間轉(zhuǎn)移(transfer)代表資源狀態(tài)的表述。通過轉(zhuǎn)移和操作資源的表述,來間接實現(xiàn)操作資源的目的。
RESTful的實現(xiàn)
具體說,就是 HTTP 協(xié)議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。
它們分別對應(yīng)四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。
REST 風(fēng)格提倡 URL 地址使用統(tǒng)一的風(fēng)格設(shè)計,從前到后各個單詞使用斜杠分開,不使用問號鍵值對方式攜帶請求參數(shù),而是將要發(fā)送給服務(wù)器的數(shù)據(jù)作為 URL 地址的一部分,以保證整體風(fēng)格的一致性。
操作傳統(tǒng)方式REST風(fēng)格查詢操作
操作 | 傳統(tǒng)方式 | REST風(fēng)格 |
---|---|---|
查詢操作 | getUserById?id=1 | user/1–>get請求方式 |
保存操作 | saveUser | user–>post請求方式 |
刪除操作 | deleteUser?id=1 | user/1–>delete請求方式 |
更新操作 | updateUser | user–>put請求方式 |
該案例我寫在 demo3 中(也可創(chuàng)建一個新的項目),大致需要的文件如下:
創(chuàng)建 user.html 并編寫用來測試的表單和超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>使用RESTFul模擬操作用戶資源(增刪改查)</title> </head> <body> <h1>使用RESTFul模擬操作用戶資源(增刪改查)</h1> <a th:href="@{/user}" rel="external nofollow" >查詢所有用戶信息</a><br/> <a th:href="@{/user/1}" rel="external nofollow" >根據(jù)用戶ID查詢用戶信息</a><br/> <form method="post" th:action="@{/user}"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="text" name="password"><br/> <input type="submit" value="添加用戶信息"> </form> <form method="put" th:action="@{/user}"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="text" name="password"><br/> <input type="submit" value="根據(jù)用戶ID修改用戶信息"> </form> <form method="delete" th:action="@{/user}"> 用戶ID:<input type="text" name="id"><br/> <input type="submit" value="根據(jù)用戶ID刪除對應(yīng)用戶信息"> </form> </body> </html>
編寫對應(yīng)的控制器方法
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @ClassName UserController * @Description: 使用RESTFul模擬操作用戶資源(增刪改查) * @Author Laoyang * @Date 2022/1/13 15:53 */ @Controller public class UserController { /** * 查詢所有用戶信息 * /user ---> GET */ @RequestMapping(value = "/user", method = RequestMethod.GET) public String findAllUser() { System.out.println("查詢所有用戶信息"); return "success"; } /** * 根據(jù)用戶ID查詢用戶信息 * /user/1 ---> GET */ @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public String findByUserId(@PathVariable("id") Integer id) { System.out.println("id=" + id); System.out.println("根據(jù)用戶ID查詢用戶信息"); return "success"; } /** * 添加用戶信息 * /user ---> POST */ @RequestMapping(value = "/user", method = RequestMethod.POST) public String saveUser(String username, String password) { System.out.println("username=" + username + ";password=" + password); System.out.println("添加用戶信息"); return "success"; } /** * 根據(jù)用戶ID修改用戶信息 * /user ---> PUT */ @RequestMapping(value = "/user", method = RequestMethod.PUT) public String updateUser(String username, String password) { System.out.println("username=" + username + ";password=" + password); System.out.println("根據(jù)用戶ID修改用戶信息"); return "success"; } /** * 根據(jù)用戶ID刪除對應(yīng)用戶信息 * /user/1 ---> DELETE */ @RequestMapping(value = "/user", method = RequestMethod.DELETE) public String deleteUser(Integer id) { System.out.println("id=" + id); System.out.println("根據(jù)用戶ID刪除對應(yīng)用戶信息"); return "success"; } }
在 spring-mvc.xml 文件中配置頁面跳轉(zhuǎn)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 掃描組件 --> <context:component-scan base-package="com.laoyang.mvc" /> <!-- 配置 Thymeleaf 視圖解析器 --> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <!-- 優(yōu)先級 --> <property name="order" value="1"/> <!-- 字符編碼 --> <property name="characterEncoding" value="UTF-8"/> <!-- 模板 --> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <!-- 視圖前綴 --> <property name="prefix" value="/WEB-INF/templates/"/> <!-- 視圖后綴 --> <property name="suffix" value=".html"/> <!-- 模板模型 --> <property name="templateMode" value="HTML5"/> <!-- 頁面編碼格式 --> <property name="characterEncoding" value="UTF-8"/> </bean> </property> </bean> </property> </bean> <!-- path:設(shè)置處理的請求地址,該路徑是和 @RequestMapping 注解中的地址是一樣的 簡單理解:就是把 controller 中用來跳轉(zhuǎn)頁面的控制器方法寫在了配置文件中 view-controller:設(shè)置請求地址所對應(yīng)的視圖名稱 --> <!-- 訪問方式:http://localhost:8080/springmvc/ --> <mvc:view-controller path="/" view-name="index"/> <!-- 訪問方式:http://localhost:8080/springmvc/doView --> <mvc:view-controller path="/doView" view-name="view" /> <!-- 訪問方式:localhost:8080/springmvc/doUser,這一步是新增的代碼 --> <mvc:view-controller path="/doUser" view-name="user"/> <!-- 開啟 mvc 的注解驅(qū)動 --> <mvc:annotation-driven /> </beans>
啟動Tomcat查看效果(訪問localhost:8080/springmvc/doUser)
- 查詢所有用戶
- 根據(jù)用戶ID查詢對應(yīng)的用戶信息
- 添加用戶信息
以上三個都是可以正常訪問的,但是瀏覽器效果都是跳轉(zhuǎn)到 success 頁面,所以大家看控制臺打印就可以看那個方法被執(zhí)行了。
- 根據(jù)用戶ID修改用戶信息
- 根據(jù)用戶ID刪除對應(yīng)用戶信息
這里截的圖是修改的效果,目前是不滿足我們的需求的,刪除效果和這個差不多,就不另做演示了
- 問題說明: 因為修改和刪除的表單提交的請求方式是 put 和 delete,而瀏覽器基本上都只支持 get 和 post 請求,所以會導(dǎo)致無法通過 put 或 delete 方式進行處理,所以遇到這種情況的時候,瀏覽器默認會以 get 請求方式進行處理,所以修改和刪除所映射到的控制器方法是 GET 類型的(查詢所有用戶,可以觀察控制臺打印的語句)
- 解決方案: 可注冊 HiddenHttpMethodFilter 過濾器處理除 get 和 post 之外的幾種請求方式,通過這個過濾器就可以解決剛才的問題
這里我先簡單說明一下,如果想要在了解一些東西可以看后面的 HiddenHttpMethodFilter 解析
在 web.xml 中配置 HiddenhttpMethodFilter
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置HiddenHttpMethodFilter過濾器 --> <filter> <filter-name>hidden</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <!-- 注冊前端控制器 DispatcherServlet --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 初始化參數(shù) --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!-- 初始化前端控制器,將前端控制器 DispatcherServlet 的初始化時間提前到服務(wù)器啟動時 --> <load-on-startup>1</load-on-startup> </servlet> <!-- 設(shè)置字符編碼格式 注意:設(shè)置編碼格式之前不可以獲取任何請求參數(shù),如果獲取了,就會導(dǎo)致設(shè)置的字符編碼失效!從而導(dǎo)致拿到的數(shù)據(jù)是亂碼。 > 所以字符編碼的 filter-mapping 標簽一定要放在其它 filter-mapping 標簽前面?。。? --> <filter> <filter-name>encoding-filter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!-- 指定要使用的編碼格式 --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <!-- 強制使用我們設(shè)置的編碼格式,默認是false(不強制) --> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 字符編碼的處理規(guī)則,必須在其他處理規(guī)則之前 --> <filter-mapping> <filter-name>encoding-filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 前端控制器請求處理規(guī)則 --> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 請求方式的處理規(guī)則 --> <filter-mapping> <filter-name>hidden</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
注意: 編寫 web.xml 文件的時候一定要注意,字符編碼的處理規(guī)則一定要放在最上面!因為設(shè)置字符編碼之前不可以獲取到任何一個參數(shù)!如果你先讓其它的處理規(guī)則先執(zhí)行,那么這些請求規(guī)則就可能會帶有參數(shù),一旦帶有參數(shù),就會導(dǎo)致我們所設(shè)置的字符編碼失效(簡單來說,就是即使我們設(shè)置了字符編碼,拿到的數(shù)據(jù)也還是亂碼?。?,所以千萬要注意!??!
Ps:web.xml 文件基本上的配置就是這些,兩個過濾器,一個 Servlet,只要是使用 SpringMVC,這幾個配置就不能少!
配置完之后還需要將提交方式修改為 POST,并且傳一個參數(shù) _method
,這個參數(shù)的值就是我們最終的提交方式,HiddenHttpMethodFilter 會根據(jù)這個參數(shù)進行處理
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>使用RESTFul模擬操作用戶資源(增刪改查)</title> </head> <body> <h1>使用RESTFul模擬操作用戶資源(增刪改查)</h1> <form method="post" th:action="@{/user}"> <!-- 因為這個參數(shù)只是給過濾器進行處理的,所以我們使用隱藏域來傳,這樣就不會影響到用戶使用了 --> <input type="hidden" name="_method" value="PUT"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="text" name="password"><br/> <input type="submit" value="根據(jù)用戶ID修改用戶信息"> </form> <form method="post" th:action="@{/user}"> <input type="hidden" name="_method" value="DELETE"> 用戶ID:<input type="text" name="id"><br/> <input type="submit" value="根據(jù)用戶ID刪除對應(yīng)用戶信息"> </form> </body> </html>
再次測試
這個時候在看控制臺,就是打印我們修改方法和刪除方法中的數(shù)據(jù)了
HiddenHttpMethodFilter 解析
為什么要使用 HiddenHttpMethodFilter
由于瀏覽器只支持發(fā)送 get 和 post 方式的請求,所以我們需要配置這個過濾器來讓瀏覽器能夠使用我們指定的方式進行處理,那么該如何發(fā)送 put 和 delete 請求呢?
- SpringMVC 提供了 HiddenHttpMethodFilter 幫助我們將 POST 請求轉(zhuǎn)換為 DELETE 或 PUT 請求 HiddenHttpMethodFilter 處理put和delete請求的條件:
- 當前請求的請求方式必須為post
- 當前請求必須傳輸請求參數(shù)
_method
,并且該參數(shù)的值必須是可兼容的(PUT、DELETE、PATCH,如有改動可自行查看源碼)
滿足以上條件,HiddenHttpMethodFilter 過濾器就會將當前請求的請求方式轉(zhuǎn)換為請求參數(shù)
_method
的值,因此請求參數(shù)_method
的值才是最終的請求方式
部分源碼 大家可根據(jù)我在源碼上標注的注釋進行理解
public class HiddenHttpMethodFilter extends OncePerRequestFilter { /** * request 和 response 是我們攔截的請求和響應(yīng) */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; /* request.getMethod():獲取當前所攔截的請求的請求方式 理解:如果當前的請求方式為 POST,并且沒有任何錯誤信息,則進行相關(guān)的操作 */ if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { // 獲取當前請求的請求參數(shù)(默認為:_method) String paramValue = request.getParameter(this.methodParam); // 如果該值的長度不為0,則進行相關(guān)的操作 if (StringUtils.hasLength(paramValue)) { // 將請求參數(shù)的值轉(zhuǎn)換為大寫 String method = paramValue.toUpperCase(Locale.ENGLISH); /* 如果轉(zhuǎn)換為大寫之后的請求參數(shù)是ALLOWED_METHODS中的某一個,那么就可以進行使用(PUT、DELETE、PATCH) 如果不是ALLOWED_METHODS中的值,則無法實現(xiàn)我們想要的效果 > 比如我們提交方式為QWE,因為ALLOWED_METHODS中并沒有QWE,所以就無法以QWE方式發(fā)送請求 */ if (ALLOWED_METHODS.contains(method)) { // 將當前請求的請求方式替換成我們需要的那種(比如把POST替換成PUT,然后在以PUT方式發(fā)送請求) requestToUse = new HttpMethodRequestWrapper(request, method); } } } // 給當前請求放行 filterChain.doFilter(requestToUse, response); } }
SpringMVC 過濾器說明
- 目前為止,SpringMVC中提供了兩個過濾器:
CharacterEncodingFilter
和HiddenHttpMethodFilter
- 在 web.xml 中注冊時,必須先注冊 CharacterEncodingFilter,再注冊HiddenHttpMethodFilter
原因:
- 在 CharacterEncodingFilter 中是通過
request.setCharacterEncoding(encoding)
方法設(shè)置字符集的; request.setCharacterEncoding(encoding) 方法要求前面不能有任何獲取請求參數(shù)的操作 - 而 HiddenHttpMethodFilter 恰恰有一個獲取請求參數(shù)的操作:
String paramValue = request.getParameter(this.methodParam);
HttpMessageConverter
- 學(xué)習(xí)前可先創(chuàng)建一個新的項目工程,用來測試對應(yīng)的案例代碼,我這里的工程名為:
springmvc-demo4
web.xml 和 pom.xml 文件里面的內(nèi)容復(fù)制過來就行
說明
- HttpMessageConverter:
報文信息轉(zhuǎn)換器
,將請求報文轉(zhuǎn)換為 Java 對象,或?qū)?Java 對象轉(zhuǎn)換為響應(yīng)報文。 - HttpMessageConverter 提供了兩個注解和兩個類型:
@RequestBody
,@ResponseBody
,RequestEntity
,ResponseEntity
@RequestBoyd
@RequestBody 可以獲取請求體,需要在控制器方法設(shè)置一個形參,使用 @RequestBody 進行標識,當前請求的請求體就會為當前注解所標識的形參賦值。
案例
編寫web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置編碼過濾器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置 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> <url-pattern>/*</url-pattern> </filter-mapping> <!--注冊SpringMVC前端控制器--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
編寫spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 開啟組件掃描 --> <context:component-scan base-package="com.laoyang.mvc" /> <!-- 配置 Thymeleaf 視圖解析器 --> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <!-- 優(yōu)先級 --> <property name="order" value="1"/> <!-- 字符編碼 --> <property name="characterEncoding" value="UTF-8"/> <!-- 模板 --> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <!-- 視圖前綴 --> <property name="prefix" value="/WEB-INF/templates/" /> <!-- 視圖后綴 --> <property name="suffix" value=".html"/> <!-- 模板模型 --> <property name="templateMode" value="HTML5"/> <!-- 頁面編碼格式 --> <property name="characterEncoding" value="UTF-8"/> </bean> </property> </bean> </property> </bean> <!-- 配置視圖控制器 --> <mvc:view-controller path="/" view-name="index" /> <!-- 開放對靜態(tài)資源的訪問 --> <mvc:default-servlet-handler /> <!-- 開啟mvc注解驅(qū)動 --> <mvc:annotation-driven /> </beans>
編寫 index.html 頁面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1> <form method="post" th:action="@{/testRequestBody}"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="text" name="password"><br/> <input type="submit" value="測試@RequestBody注解"> </form> </body> </html>
編寫 success.html 頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>主頁面</title> </head> <body> <h1>主頁面</h1> </body> </html>
編寫對應(yīng)的控制器方法
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; /** * @ClassName HttpController * @Description: 測試 @RequestBody 注解和 RequestEntity 類型 * @Author Laoyang * @Date 2022/1/15 15:52 */ @Controller public class RequestController { /** * 測試 @RequestBody 注解 */ @RequestMapping(value = "/testRequestBody") public String testRequestBody(@RequestBody String requestBody) { // 因為使用了 @RequestBody 注解,所以這里的 requestBody 就可以獲取到頁面兩個文本框的值 System.out.println("------>" + requestBody); return "success"; } }
啟動Tomcat進行測試
- 頁面效果:成功跳轉(zhuǎn)到了 success.html 頁面
- 控制臺效果:成功獲取到了我們在瀏覽器文本框中輸入的值 RequestEntity
RequestEntity
封裝請求報文的一種類型,需要在控制器方法的形參中設(shè)置該類型的形參,當前請求的請求報文就會賦值給該形參,可以通過 getHeaders()
獲取請求頭信息,通過 getBody()
獲取請求體信息。
案例
在 index.html 中編寫表單
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1> <form method="post" th:action="@{/testRequestEntity}"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="text" name="password"><br/> <input type="submit" value="測試RequestEntity類型"> </form> </body> </html>
編寫對應(yīng)的控制器方法
package com.laoyang.mvc.controller; import org.springframework.http.RequestEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; /** * @ClassName HttpController * @Description: 測試 @RequestBody 注解和 RequestEntity 類型 * @Author Laoyang * @Date 2022/1/15 15:52 */ @Controller public class RequestController { /** * 測試 RequestEntity 類型 */ @RequestMapping(value = "/testRequestEntity") public String testRequestEntity(RequestEntity<String> requestEntity) { // 請求頭獲取的就是瀏覽器(F12)中看到的 System.out.println("請求頭 --->" + requestEntity.getHeaders()); // 請求體獲取的就是瀏覽器發(fā)送請求時帶的參數(shù) System.out.println("請求體 --->" + requestEntity.getBody()); return "success"; } }
啟動Tomcat進行測試
- 請求頭獲取的就是瀏覽器(F12)中看到的,具體大家可看瀏覽器,或控制臺打印的數(shù)據(jù)
@ResponseBody
@ResponseBody 用于標識一個控制器方法,可以將該方法的返回值直接作為響應(yīng)報文的響應(yīng)體響應(yīng)到瀏覽器。
使用ServletAPI的response對象響應(yīng)瀏覽器數(shù)據(jù)
在 index.html 中編寫超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1> <a th:href="@{/testResponse}" rel="external nofollow" >通過ServletAPI的response對象響應(yīng)瀏覽器數(shù)據(jù)</a><br/> </body> </html>
編寫對應(yīng)控制器方法
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 通過ServletAPI的response對象響應(yīng)瀏覽器數(shù)據(jù) */ @RequestMapping("/testResponse") public void testResponse(HttpServletResponse response) { try { // 設(shè)置編碼格式 response.setContentType("text/html;charset=utf-8"); // 響應(yīng)給瀏覽器的數(shù)據(jù) response.getWriter().print("Hello 原生ServletAPI的response"); } catch (IOException e) { e.printStackTrace(); } } }
啟動Tomcat查看效果
通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù)(響應(yīng)String類型的數(shù)據(jù))
在 index.html 中編寫超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1> <a th:href="@{/testResponseBody}" rel="external nofollow" >通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù)(String)</a><br/> </body> </html>
編寫對應(yīng)控制器方法
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù) - String數(shù)據(jù) */ @RequestMapping("/testResponseBody") @ResponseBody public String testResponseBody() { /* 不加 @ResponseBody 注解就表示跳轉(zhuǎn)到 success 頁面 加上 @ResponseBody 注解則表示將該返回值響應(yīng)給瀏覽器 */ return "success"; } }
啟動Tomcat查看效果
使用 @ResponseBody 注解處理 json 數(shù)據(jù)
在 index.html 中編寫超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1 <a th:href="@{/testResponseUser}" rel="external nofollow" >通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù)(User)</a><br/> </body> </html>
編寫一個實體類用來配合測試
package com.laoyang.mvc.pojo; public class User { private Integer id; private String username; private String password; private Integer age; private String sex; // 創(chuàng)建對應(yīng)的get/set 方法、有參構(gòu)造器、全參構(gòu)造器 }
編寫對應(yīng)控制器方法
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù) - User對象數(shù)據(jù) */ @RequestMapping("/testResponseUser") @ResponseBody public User testResponseUser() { return new User(1001, "admin", "12345", 18, "男"); } }
啟動Tomcat查看效果
報錯原因:瀏覽器只能處理字符串類型的數(shù)據(jù)
解決方案:引入 json 相關(guān)依賴,并進行相關(guān)配置
解決方案的實現(xiàn)步驟
導(dǎo)入jackson
的依賴
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.5</version> </dependency>
在 SpringMVC 的核心配置文件(spring-mvc.xml)中開啟 mvc 的注解驅(qū)動,此時在HandlerAdaptor 中會自動裝配一個消息轉(zhuǎn)換器:MappingJackson2HttpMessageConverter
,可以將響應(yīng)到瀏覽器的 Java 對象轉(zhuǎn)換為 Json 格式的字符串
<mvc:annotation-driven />
在最開始的時候我們已經(jīng)配好了,這里就可以不配了;如果沒有配置,那么一定要加上
在處理器方法上使用 @ResponseBody 注解進行標識
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù) - User對象數(shù)據(jù) */ @RequestMapping("/testResponseUser") @ResponseBody public User testResponseUser() { return null; } }
將 Java 對象直接作為控制器方法的返回值進行返回,就會自動轉(zhuǎn)換為 Json 格式的字符串
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 通過@ResponseBody注解響應(yīng)瀏覽器數(shù)據(jù) - User對象數(shù)據(jù) */ @RequestMapping("/testResponseUser") @ResponseBody public User testResponseUser() { return new User(1001, "admin", "12345", 18, "男"); } }
啟動Tomcat查看效果
使用 @ResponseBody 注解處理 ajax 請求
在 index.html 中編寫超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首頁</title> </head> <body> <h1>首頁</h1> <div id="app"> <a th:href="@{testAxios}" rel="external nofollow" @click="testAxios">SpringMVC處理ajax</a> </div> </body> </html>
通過vue和axios處理點擊事件
需要先導(dǎo)入 vue.js
和 axios.min.js
然后使用代碼進行引用
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script> <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script> <script type="text/javascript"> var vue = new Vue({ el:"#app", methods:{ testAxios:function (event) { axios({ method:"post", url:event.target.href, params:{ username:"admin", password:"123456" } }).then(function (response) { alert(response.data); }); event.preventDefault(); } } }); </script>
編寫對應(yīng)控制器方法
package com.laoyang.mvc.controller; import com.laoyang.mvc.pojo.User; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @ClassName ResponseController * @Description: 測試 @ResponseBody 注解和 ResponseEntity 類型 * @Author Laoyang * @Date 2022/1/15 17:10 */ @Controller public class ResponseController { /** * 使用 @ResponseBody 注解處理 ajax 請求 */ @RequestMapping("/testAxios") @ResponseBody public String testAxios(String username, String password) { System.out.println(username + "--->" + password); return "Hello Axios"; } }
啟動Tomcat查看效果
點擊鏈接后就可以獲取到控制器方法響應(yīng)到瀏覽器的數(shù)據(jù)了,控制臺中也可以看到 username 和 password 的數(shù)據(jù)了。
@RestController 注解
@RestController
注解是 SpringMVC 提供的一個復(fù)合注解,標識在控制器的類上,就相當于為類添加了 @Controller 注解,并且為其中的每個方法添加了 @ResponseBody 注解。
@RestController public class ResponseController { } 效果就相當于: @Controller public class ResponseController { @ResponseBody public String testAxios(String username, String password) { System.out.println(username + "--->" + password); return "Hello"; } } 簡單理解:@RestController 就相當于是 @Controller 和 @ResponseBody 的效果的結(jié)合 > 使用這一個注解,就可以實現(xiàn)這兩個注解的效果 Ps:因為作用是一樣的,所以這里就不演示了
ResponseEntity
- ResponseEntity 用于控制器方法的返回值類型,該控制器方法的返回值就是響應(yīng)到瀏覽器的響應(yīng)報文。
- 通過 ResponseEntity 可以實現(xiàn)文件的上傳和下載
文件下載
在 static 目錄下創(chuàng)建 img 文件夾,然后導(dǎo)入一些文件
創(chuàng)建 file.html,編寫超鏈接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>文件上傳與下載</title> </head> <body> <h1>文件上傳與下載</h1> <a th:href="@{/download}" rel="external nofollow" >下載文件</a><br/> </body> </html>
編寫對應(yīng)控制器方法
package com.laoyang.mvc.controller; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import java.io.FileInputStream; import java.io.InputStream; /** * @ClassName FileAndDownController * @Description: 文件上傳和下載 * @Author Laoyang * @Date 2022/1/16 16:36 */ @Controller public class FileAndDownController { /** * 文件下載 */ @RequestMapping("/download") public ResponseEntity<byte[]> download(HttpSession session) throws Exception { //獲取ServletContext對象 ServletContext servletContext = session.getServletContext(); //獲取服務(wù)器中文件的真實路徑 String realPath = servletContext.getRealPath("/static/img/21.jpg"); //創(chuàng)建輸入流 InputStream is = new FileInputStream(realPath); //創(chuàng)建字節(jié)數(shù)組(is.available():獲取當前文件的字節(jié)數(shù)) byte[] bytes = new byte[is.available()]; //將流讀到字節(jié)數(shù)組中 is.read(bytes); //創(chuàng)建HttpHeaders對象設(shè)置響應(yīng)頭信息 MultiValueMap<String, String> headers = new HttpHeaders(); //設(shè)置要下載方式以及下載文件的名字 headers.add("Content-Disposition", "attachment;filename=Digimon.jpg"); //設(shè)置響應(yīng)狀態(tài)碼 HttpStatus statusCode = HttpStatus.OK; //創(chuàng)建ResponseEntity對象(參數(shù)分別是:請求體、請求頭、響應(yīng)狀態(tài)碼) ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode); //關(guān)閉輸入流 is.close(); return responseEntity; } }
啟動Tomcat查看效果
點擊瀏覽器頁面中的超鏈接,即可下載我們配置好的文件
文件上傳
文件上傳要求 form 表單的請求方式必須為 post,并且添加屬性 enctype="multipart/form-data"
SpringMVC 中將上傳的文件封裝到 MultipartFile 對象中,通過此對象可以獲取文件相關(guān)信息。
導(dǎo)入相關(guān)依賴
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
在 file.html 中編寫表單進行文件上傳
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>文件上傳與下載</title> </head> <body> <h1>文件上傳與下載</h1> <form th:action="@{/upload}" method="post" enctype="multipart/form-data"> <input type="file" name="photo"> <input type="submit" value="上傳文件"> </form> </body> </html>
在spring-mvc.xml 文件中配置文件上傳解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
編寫對應(yīng)的控制器方法
package com.laoyang.mvc.controller; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @ClassName FileAndDownController * @Description: 文件上傳和下載 * @Author Laoyang * @Date 2022/1/16 16:36 */ @Controller public class FileAndDownController { /** * 文件上傳 */ @RequestMapping("/upload") public String upload(MultipartFile photo, HttpSession session) throws IOException { // 獲取上傳的文件名 String fileName = photo.getOriginalFilename(); System.out.println(fileName); // 獲取當前工程的上下文路徑 ServletContext servletContext = session.getServletContext(); // 給定一個 URI,返回文件系統(tǒng)中 URI對應(yīng)的絕對路徑 String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); // 判斷 photoPath 所對應(yīng)的路徑是否存在 if (!file.exists()) { // 如果不存在,則創(chuàng)建對應(yīng)目錄 file.mkdirs(); } // 最終的上傳地址 String finalPath = photoPath + File.separator + fileName; // 將文件上傳到指定的地址下 photo.transferTo(new File(finalPath)); return "success"; } }
這里上傳的最終目錄設(shè)置在
springmvc-demo4\target\springmvc-demo4-1.0-SNAPSHOT
目錄下
啟動Tomcat查看效果
上傳成功后可在自己項目的
target\springmvc-demo4-1.0-SNAPSHOT
目錄下查看效果
文件上傳的重名問題
- 如果指定的目錄中有一個和我們即將要上傳的文件名稱一樣的文件,那么就會導(dǎo)致新上傳的文件覆蓋原來的文件,從而導(dǎo)致一些不必要的問題(詳細可了解 IO 流)。 使用 UUID 解決文件重名問題
package com.laoyang.mvc.controller; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.UUID; /** * @ClassName FileAndDownController * @Description: 文件上傳和下載 * @Author Laoyang * @Date 2022/1/16 16:36 */ @Controller public class FileAndDownController { /** * 文件上傳 */ @RequestMapping("/upload") public String upload(MultipartFile photo, HttpSession session) throws IOException { // 獲取上傳的文件名 String fileName = photo.getOriginalFilename(); // 截取文件后綴名(lastIndexOf:獲取字符串中最后一個 . 的位置) String suffix = fileName.substring(fileName.lastIndexOf(".")); // 將 UUID 作為文件名,保證唯一性 String uuid = UUID.randomUUID().toString(); // 將 UUID 和后綴名進行拼接,得到完整的文件名 String fullName = uuid + suffix; // 獲取當前工程的上下文路徑 ServletContext servletContext = session.getServletContext(); // 給定一個 URI,返回文件系統(tǒng)中 URI對應(yīng)的絕對路徑 String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); // 判斷 photoPath 所對應(yīng)的路徑是否存在 if (!file.exists()) { // 如果不存在,則創(chuàng)建對應(yīng)目錄 file.mkdirs(); } // 最終的上傳地址 String finalPath = photoPath + File.separator + fullName; // 將文件上傳到指定的目錄 photo.transferTo(new File(finalPath)); return "success"; } }
一般來說,UUID是不會重名的
以上就是SpringMVC將請求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為JSON格式的幾種方式的詳細內(nèi)容,更多關(guān)于SpringMVC數(shù)據(jù)轉(zhuǎn)換為JSON格式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis-Plus使用ID_WORKER生成主鍵id重復(fù)的解決方法
本文主要介紹了Mybatis-Plus使用ID_WORKER生成主鍵id重復(fù)的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java下載文件中文文件名亂碼的解決方案(文件名包含很多%)
Java下載文件時,文件名中文亂碼問題通常是由于編碼不正確導(dǎo)致的,使用`URLEncoder.encode(filepath, "UTF-8")`可以解決在提示下載框中正確顯示漢字文件名的問題,但在選擇直接打開時,文件名會變成亂碼,解決這個問題的方法2025-02-02引入mybatis-plus報 Invalid bound statement錯誤問題的解決方法
這篇文章主要介紹了引入mybatis-plus報 Invalid bound statement錯誤問題的解決方法,需要的朋友可以參考下2020-05-05Spring?Boot應(yīng)用打WAR包后無法注冊到Nacos的問題及解決方法
當我們將?Spring?Boot?應(yīng)用打包成?WAR?并部署到外部?Tomcat?服務(wù)器時,可能會遇到服務(wù)無法注冊到?Nacos?的情況,其原因主要是應(yīng)用獲取不到正確的服務(wù)器端口,下面給大家介紹Spring?Boot?應(yīng)用打?WAR?包后無法注冊到?Nacos的問題及解決方法,感興趣的朋友跟隨小編一起看看吧2024-06-06解決springboot集成swagger碰到的坑(報404)
這篇文章主要介紹了解決springboot集成swagger碰到的坑(報404),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作
這篇文章主要介紹了JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10在IDEA中安裝MyBatis Log Plugin插件,執(zhí)行mybatis的sql語句(推薦)
這篇文章主要介紹了在IDEA中安裝MyBatis Log Plugin插件,執(zhí)行mybatis的sql語句,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07