Spring?Boot數(shù)據(jù)響應(yīng)問題實(shí)例詳解
前言
響應(yīng)頁面指的是我們?nèi)绾伟l(fā)送一個(gè)請(qǐng)求,跳轉(zhuǎn)到指定頁面。將會(huì)在后面的視圖解析中說明。 響應(yīng)頁面常見于開發(fā)單體應(yīng)用。 響應(yīng)數(shù)據(jù)常見于開發(fā)前后端分離的應(yīng)用。后端代碼主要用來接收請(qǐng)求。前端頁面給我們發(fā)送過來請(qǐng)求,給前端響應(yīng)json數(shù)據(jù)?;蛘呓o前端響應(yīng)xml、圖片、音視頻數(shù)據(jù)。
在前后端分離開發(fā)過程中,后端一般會(huì)將數(shù)據(jù)集封裝成一個(gè)JSON對(duì)象響應(yīng)給前端 ,一般只需要標(biāo)準(zhǔn)ResponseBody即可給前端返回?cái)?shù)據(jù)
1、響應(yīng)Json數(shù)據(jù):Jackson.jar+@ResponseBody
假設(shè)給前端自動(dòng)返回json數(shù)據(jù),需要引入相關(guān)的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- web場景自動(dòng)引入了json場景 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
控制層代碼如下:引入了依賴后,給方法上標(biāo)注@ResponseBody,就可以給前端自動(dòng)返回JSON數(shù)據(jù)。
@Controller public class ResponseTestController { @ResponseBody //原理就是利用返回值處理器里面消息轉(zhuǎn)換器進(jìn)行處理 @GetMapping("/test/person") public Person getPerson(){ Person person = new Person(); person.setAge(28); person.setBirth(new Date()); person.setUserName("zhangsan"); return person; } }
測(cè)試:
2、原理解析
- 返回值處理器判斷是否支持這種類型返回值supportsReturnType
- 返回值處理器調(diào)用handleReturnValue進(jìn)行處理
- RequestResponseBodyMethodProcessor可以處理返回值標(biāo)了@ResponseBody注解的。
- 利用MessageConverters進(jìn)行處理將數(shù)據(jù)寫為json
- 內(nèi)容協(xié)商(瀏覽器默認(rèn)會(huì)以請(qǐng)求頭的方式告訴服務(wù)器他能接受什么樣的內(nèi)容類型)
- 服務(wù)器最終根據(jù)自己自身的能力,決定服務(wù)器能生產(chǎn)出什么樣內(nèi)容類型的數(shù)據(jù),
- SpringMVC會(huì)挨個(gè)遍歷所有容器底層的HttpMessageConverter,看誰能處理?(也就是把對(duì)象轉(zhuǎn)換成為json數(shù)據(jù))
- 得到MappingJackson2HttpMessageConverter消息轉(zhuǎn)換器可以將對(duì)象寫為json
- 利用MappingJackson2HttpMessageConverter將對(duì)象轉(zhuǎn)為json再寫出去。
- 利用MessageConverters進(jìn)行處理將數(shù)據(jù)寫為json
SpringMVC到底支持哪些返回值
- ModelAndView //包含數(shù)據(jù)和頁面
- Model
- View
- ResponseEntity
- ResponseBodyEmitter
- StreamingResponseBody
- HttpEntity
- HttpHeaders
- Callable //異步
- DeferredResult
- ListenableFuture
- CompletionStage
- WebAsyncTask
- 有 @ModelAttribute 且為對(duì)象類型的
- @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;處理器//即在方法上或者類上是否標(biāo)注@ResponseBody
HTTPMessageConverter原理
MessageConverter規(guī)范
HttpMessageConverter:看是否支持將 此 Class類型的對(duì)象,轉(zhuǎn)為MediaType類型的數(shù)據(jù)。 例子:CanWrite將Person對(duì)象轉(zhuǎn)為JSON。canRead或者 JSON轉(zhuǎn)為Person
默認(rèn)的MessageConverter
- 0 - 只支持Byte類型的
- 1 - String
- 2 - String
- 3 - Resource
- 4 - ResourceRegion
- 5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
- 6 - MultiValueMap
- 7 - true //支持將任意對(duì)象轉(zhuǎn)為指定的,不管是什么都支持
- 8 - true
- 9 - 支持注解方式xml處理的。
最終 MappingJackson2HttpMessageConverter 把對(duì)象轉(zhuǎn)為JSON(利用底層的jackson的objectMapper轉(zhuǎn)換的)
3、內(nèi)容協(xié)商
3.1、概述
根據(jù)客戶端接收能力不同【有的只接收xml,有的只接收json】,返回不同媒體類型的數(shù)據(jù)。比如返回xml數(shù)據(jù)給前
引入支持XML依賴:
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
重新編譯該項(xiàng)目運(yùn)行 ,返回了xml數(shù)據(jù)
3.2、postman分別測(cè)試返回json和xml
在上面的測(cè)試中,此時(shí)如果我用postman發(fā)送相同的請(qǐng)求,則得到的是json數(shù)據(jù),為啥同樣的請(qǐng)求,方式不一樣,返回的數(shù)據(jù)不一樣呢。原因就是請(qǐng)求頭中規(guī)定的數(shù)據(jù)響應(yīng)先后順序
查看請(qǐng)求頭
內(nèi)容協(xié)商Accept中,瀏覽器具備什么類型數(shù)據(jù)的接收能力,可以看到xml數(shù)據(jù)是優(yōu)先被接收的。
可用Postman軟件分別測(cè)試返回json和xml:只需要改變請(qǐng)求頭中Accept字段(application/json、application/xml)。Http協(xié)議中規(guī)定的,告訴服務(wù)器本客戶端可以接收的數(shù)據(jù)類型。
3.3、開啟瀏覽器參數(shù)方式內(nèi)容協(xié)商功能
為了方便內(nèi)容協(xié)商,開啟基于請(qǐng)求參數(shù)的內(nèi)容協(xié)商功能。
spring: contentnegotiation: favor-parameter: true #開啟請(qǐng)求參數(shù)內(nèi)容協(xié)商模式
發(fā)請(qǐng)求:
- json類型: http://localhost:8080/test/person?format=json
- xml類型:http://localhost:8080/test/person?format=xml
確定客戶端接收什么樣的內(nèi)容類型;
1、Parameter策略優(yōu)先確定是要返回json數(shù)據(jù)(獲取請(qǐng)求頭中的format的值) 2、最終進(jìn)行內(nèi)容協(xié)商返回給客戶端json即可。
4、內(nèi)容協(xié)商原理
- 判斷當(dāng)前響應(yīng)頭中是否已經(jīng)有確定的媒體類型。MediaType
- 獲取客戶端(PostMan、瀏覽器)支持接收的內(nèi)容類型。(獲取客戶端Accept請(qǐng)求頭字段)【application/xml】
- contentNegotiationManager 內(nèi)容協(xié)商管理器 默認(rèn)使用基于請(qǐng)求頭的策略
- HeaderContentNegotiationStrategy 確定客戶端可以接收的內(nèi)容類型
- 遍歷循環(huán)所有當(dāng)前系統(tǒng)的 MessageConverter,看誰支持操作這個(gè)對(duì)象(Person)
- 找到支持操作Person的converter,把converter支持的媒體類型統(tǒng)計(jì)出來。
- 客戶端需要【application/xml】。服務(wù)端能力【10種、json、xml】
- 進(jìn)行內(nèi)容協(xié)商的最佳匹配媒體類型
- 用 支持 將對(duì)象轉(zhuǎn)為 最佳匹配媒體類型 的converter。調(diào)用它進(jìn)行轉(zhuǎn)化 。
導(dǎo)入了jackson處理xml的包,xml的converter就會(huì)自動(dòng)進(jìn)來
WebMvcConfigurationSupport jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); if (jackson2XmlPresent) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build())); }
5、自定義消息轉(zhuǎn)換器MessageConverter
5.1、概述
實(shí)現(xiàn)多協(xié)議數(shù)據(jù)兼容。json、xml、x-guigu
- @ResponseBody 響應(yīng)數(shù)據(jù)出去 調(diào)用 RequestResponseBodyMethodProcessor 處理
- Processor 處理方法返回值。通過 MessageConverter 處理
- 所有 MessageConverter 合起來可以支持各種媒體類型數(shù)據(jù)的操作(讀、寫)
- 內(nèi)容協(xié)商找到最終的 messageConverter;
要自定義SpringMVC的什么功能,即通過一個(gè)入口給容器中添加一個(gè) WebMvcConfigurer
假設(shè)你想基于自定義請(qǐng)求參數(shù)的自定義內(nèi)容協(xié)商功能。換句話,在地址欄輸入http://localhost:8080/test/person?format=gg返回?cái)?shù)據(jù),跟http://localhost:8080/test/person且請(qǐng)求頭參數(shù)`Accept:application/x-guigu`的返回自定義協(xié)議數(shù)據(jù)的一致。
演示
通過上文分析,我們只需要實(shí)現(xiàn)WebMvcConfigurer接口,并實(shí)現(xiàn)了configureMessageConverters方法,就可以達(dá)到自定義消息轉(zhuǎn)換器的目的。例如,我不想用jackson了,想用fastjson的消息轉(zhuǎn)換器,我們可以添加fastjson相關(guān)的MessageConverter就可以了
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); List<MediaType> fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.TEXT_HTML); fastMediaTypes.add(MediaType.APPLICATION_JSON); fastConverter.setSupportedMediaTypes(fastMediaTypes); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteDateUseDateFormat); SerializeConfig serializeConfig = SerializeConfig.globalInstance; serializeConfig.put(BigInteger.class, ToStringSerializer.instance); serializeConfig.put(Long.class, ToStringSerializer.instance); serializeConfig.put(Long.TYPE, ToStringSerializer.instance); fastJsonConfig.setSerializeConfig(serializeConfig); fastConverter.setFastJsonConfig(fastJsonConfig); converters.add(fastConverter); } }
測(cè)試
@Data public class Person { private String userName; private Integer age; //使用fastjson的注解進(jìn)行轉(zhuǎn)換 @JSONField(format = "yyyy-MM-dd") private Date birth; private Pet pet; }
5.2、自定義的Converter
除此之外,這些都是默認(rèn)的,我們可以進(jìn)行擴(kuò)展,如下實(shí)現(xiàn)自定義的設(shè)置轉(zhuǎn)化,如下,利用這個(gè)代碼:
@Bean public WebMvcConfigurer webMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { } } }
測(cè)試
@Configuration(proxyBeanMethods = false) public class WebConfig { @Bean public WebMvcConfigurer webMvcConfigurer(){ return new WebMvcConfigurer() { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new GuiguMessageConverter()); } } } }
/** * 自定義的Converter */ public class GuiguMessageConverter implements HttpMessageConverter<Person> { @Override public boolean canRead(Class<?> clazz, MediaType mediaType) { return false; } @Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return clazz.isAssignableFrom(Person.class); } /** * 服務(wù)器要統(tǒng)計(jì)所有MessageConverter都能寫出哪些內(nèi)容類型 * * application/x-guigu * @return */ @Override public List<MediaType> getSupportedMediaTypes() { return MediaType.parseMediaTypes("application/x-guigu"); } @Override public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return null; } @Override public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //自定義協(xié)議數(shù)據(jù)的寫出 String data = person.getUserName()+";"+person.getAge()+";"+person.getBirth(); //寫出去 OutputStream body = outputMessage.getBody(); body.write(data.getBytes()); } }
測(cè)試:
import java.util.Date; @Controller public class ResponseTestController { /** * 1、瀏覽器發(fā)請(qǐng)求直接返回 xml [application/xml] jacksonXmlConverter * 2、如果是ajax請(qǐng)求 返回 json [application/json] jacksonJsonConverter * 3、如果硅谷app發(fā)請(qǐng)求,返回自定義協(xié)議數(shù)據(jù) [appliaction/x-guigu] xxxxConverter * 屬性值1;屬性值2; * * 步驟: * 1、添加自定義的MessageConverter進(jìn)系統(tǒng)底層 * 2、系統(tǒng)底層就會(huì)統(tǒng)計(jì)出所有MessageConverter能操作哪些類型 * 3、客戶端內(nèi)容協(xié)商 [guigu--->guigu] * * 作業(yè):如何以參數(shù)的方式進(jìn)行內(nèi)容協(xié)商 * @return */ @ResponseBody //利用返回值處理器里面的消息轉(zhuǎn)換器進(jìn)行處理 @GetMapping(value = "/test/person") public Person getPerson(){ Person person = new Person(); person.setAge(28); person.setBirth(new Date()); person.setUserName("zhangsan"); return person; } }
日后開發(fā)要注意,有可能我們添加的自定義的功能會(huì)覆蓋默認(rèn)很多功能,導(dǎo)致一些默認(rèn)的功能失效。
總結(jié)
到此這篇關(guān)于Spring Boot數(shù)據(jù)響應(yīng)問題的文章就介紹到這了,更多相關(guān)SpringBoot數(shù)據(jù)響應(yīng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用Jackson序列化實(shí)現(xiàn)數(shù)據(jù)脫敏詳解
在項(xiàng)目中有些敏感信息不能直接展示,比如客戶手機(jī)號(hào)、身份證、車牌號(hào)等信息,展示時(shí)均需要進(jìn)行數(shù)據(jù)脫敏,防止泄露客戶隱私。本文將利用Jackson序列化實(shí)現(xiàn)數(shù)據(jù)脫敏,需要的可以參考一下2023-03-03探究springboot中的TomcatMetricsBinder
springboot的TomcatMetricsBinder主要是接收ApplicationStartedEvent然后創(chuàng)建TomcatMetrics執(zhí)行bindTo進(jìn)行注冊(cè),TomcatMetrics主要注冊(cè)了globalRequest、servlet、cache、threadPool、session相關(guān)的指標(biāo),本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11基于Spring Boot不同的環(huán)境使用不同的配置方法
下面小編就為大家分享一篇基于Spring Boot不同的環(huán)境使用不同的配置方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01理解Java注解及Spring的@Autowired是如何實(shí)現(xiàn)的
今天通過本文帶領(lǐng)大家學(xué)習(xí)注解的基礎(chǔ)知識(shí),學(xué)習(xí)Spring的@Autowired是怎么實(shí)現(xiàn)的,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07Jexcel實(shí)現(xiàn)按一定規(guī)則分割excel文件的方法
這篇文章主要介紹了Jexcel實(shí)現(xiàn)按一定規(guī)則分割excel文件的方法,涉及java操作Excel文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)
這篇文章主要介紹了SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03Java--SSH,SSM和Spring?Boot框架區(qū)別及優(yōu)缺點(diǎn)說明
這篇文章主要介紹了Java--SSH,SSM和Spring?Boot框架區(qū)別及優(yōu)缺點(diǎn)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12