溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

發(fā)布時間:2022-03-14 15:48:47 來源:億速云 閱讀:222 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細介紹“Spring Boot數(shù)據(jù)響應(yīng)問題實例分析”,內(nèi)容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“Spring Boot數(shù)據(jù)響應(yīng)問題實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

    前言

    響應(yīng)頁面指的是我們?nèi)绾伟l(fā)送一個請求,跳轉(zhuǎn)到指定頁面。將會在后面的視圖解析中說明。 響應(yīng)頁面常見于開發(fā)單體應(yīng)用。 響應(yīng)數(shù)據(jù)常見于開發(fā)前后端分離的應(yīng)用。后端代碼主要用來接收請求。前端頁面給我們發(fā)送過來請求,給前端響應(yīng)json數(shù)據(jù)?;蛘呓o前端響應(yīng)xml、圖片、音視頻數(shù)據(jù)。

    在前后端分離開發(fā)過程中,后端一般會將數(shù)據(jù)集封裝成一個JSON對象響應(yīng)給前端 ,一般只需要標準ResponseBody即可給前端返回數(shù)據(jù)

    1、響應(yīng)Json數(shù)據(jù):Jackson.jar+@ResponseBody

    假設(shè)給前端自動返回json數(shù)據(jù),需要引入相關(guān)的依賴

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- web場景自動引入了json場景 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
        <version>2.3.4.RELEASE</version>
        <scope>compile</scope>
    </dependency>

    控制層代碼如下:引入了依賴后,給方法上標注@ResponseBody,就可以給前端自動返回JSON數(shù)據(jù)。

    @Controller
    public class ResponseTestController {
        @ResponseBody //原理就是利用返回值處理器里面消息轉(zhuǎn)換器進行處理
        @GetMapping("/test/person")
        public Person getPerson(){
            Person person = new Person();
            person.setAge(28);
            person.setBirth(new Date());
            person.setUserName("zhangsan");
            return person;
        }
    
    }

    測試:

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    2、原理解析

    • 返回值處理器判斷是否支持這種類型返回值supportsReturnType

    • 返回值處理器調(diào)用handleReturnValue進行處理

    • RequestResponseBodyMethodProcessor可以處理返回值標了@ResponseBody注解的。

      • 內(nèi)容協(xié)商(瀏覽器默認會以請求頭的方式告訴服務(wù)器他能接受什么樣的內(nèi)容類型)

      • 服務(wù)器最終根據(jù)自己自身的能力,決定服務(wù)器能生產(chǎn)出什么樣內(nèi)容類型的數(shù)據(jù),

      • SpringMVC會挨個遍歷所有容器底層的HttpMessageConverter,看誰能處理?(也就是把對象轉(zhuǎn)換成為json數(shù)據(jù))

      • 得到MappingJackson2HttpMessageConverter消息轉(zhuǎn)換器可以將對象寫為json

      • 利用MappingJackson2HttpMessageConverter將對象轉(zhuǎn)為json再寫出去。

      • 利用MessageConverters進行處理將數(shù)據(jù)寫為json

    SpringMVC到底支持哪些返回值

    • ModelAndView //包含數(shù)據(jù)和頁面

    • Model

    • View

    • ResponseEntity

    • ResponseBodyEmitter

    • StreamingResponseBody

    • HttpEntity

    • HttpHeaders

    • Callable //異步

    • DeferredResult

    • ListenableFuture

    • CompletionStage

    • WebAsyncTask

    • 有 @ModelAttribute 且為對象類型的

    • @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;處理器//即在方法上或者類上是否標注@ResponseBody

    HTTPMessageConverter原理

    MessageConverter規(guī)范

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    HttpMessageConverter:看是否支持將 此 Class類型的對象,轉(zhuǎn)為MediaType類型的數(shù)據(jù)。 例子:CanWrite將Person對象轉(zhuǎn)為JSON。canRead或者 JSON轉(zhuǎn)為Person

    默認的MessageConverter

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    • 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 //支持將任意對象轉(zhuǎn)為指定的,不管是什么都支持

    • 8 - true

    • 9 - 支持注解方式xml處理的。

    最終 MappingJackson2HttpMessageConverter 把對象轉(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>

    重新編譯該項目運行 ,返回了xml數(shù)據(jù)

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    3.2、postman分別測試返回json和xml

    在上面的測試中,此時如果我用postman發(fā)送相同的請求,則得到的是json數(shù)據(jù),為啥同樣的請求,方式不一樣,返回的數(shù)據(jù)不一樣呢。原因就是請求頭中規(guī)定的數(shù)據(jù)響應(yīng)先后順序

    查看請求頭

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    內(nèi)容協(xié)商Accept中,瀏覽器具備什么類型數(shù)據(jù)的接收能力,可以看到xml數(shù)據(jù)是優(yōu)先被接收的。

    可用Postman軟件分別測試返回json和xml:只需要改變請求頭中Accept字段(application/json、application/xml)。Http協(xié)議中規(guī)定的,告訴服務(wù)器本客戶端可以接收的數(shù)據(jù)類型

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    3.3、開啟瀏覽器參數(shù)方式內(nèi)容協(xié)商功能

    為了方便內(nèi)容協(xié)商,開啟基于請求參數(shù)的內(nèi)容協(xié)商功能。

    spring:
        contentnegotiation:
          favor-parameter: true  #開啟請求參數(shù)內(nèi)容協(xié)商模式

    發(fā)請求:

    • 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ù)(獲取請求頭中的format的值) 2、最終進行內(nèi)容協(xié)商返回給客戶端json即可。

    4、內(nèi)容協(xié)商原理

    • 判斷當前響應(yīng)頭中是否已經(jīng)有確定的媒體類型。MediaType

    • 獲取客戶端(PostMan、瀏覽器)支持接收的內(nèi)容類型。(獲取客戶端Accept請求頭字段)【application/xml】

    • contentNegotiationManager 內(nèi)容協(xié)商管理器 默認使用基于請求頭的策略

    • HeaderContentNegotiationStrategy 確定客戶端可以接收的內(nèi)容類型

    • 遍歷循環(huán)所有當前系統(tǒng)的 MessageConverter,看誰支持操作這個對象(Person)

    • 找到支持操作Person的converter,把converter支持的媒體類型統(tǒng)計出來。

    • 客戶端需要【application/xml】。服務(wù)端能力【10種、json、xml】

    • 進行內(nèi)容協(xié)商的最佳匹配媒體類型

    • 用 支持 將對象轉(zhuǎn)為 最佳匹配媒體類型 的converter。調(diào)用它進行轉(zhuǎn)化 。

    導(dǎo)入了jackson處理xml的包,xml的converter就會自動進來

    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、概述

    實現(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的什么功能,即通過一個入口給容器中添加一個 WebMvcConfigurer

    假設(shè)你想基于自定義請求參數(shù)的自定義內(nèi)容協(xié)商功能。換句話,在地址欄輸入http://localhost:8080/test/person?format=gg返回數(shù)據(jù),跟http://localhost:8080/test/person且請求頭參數(shù)`Accept:application/x-guigu`的返回自定義協(xié)議數(shù)據(jù)的一致。

    演示

    通過上文分析,我們只需要實現(xiàn)WebMvcConfigurer接口,并實現(xiàn)了configureMessageConverters方法,就可以達到自定義消息轉(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);
        }
    }

    測試

    @Data
    public class Person {
        private String userName;
        private Integer age;
        //使用fastjson的注解進行轉(zhuǎn)換
        @JSONField(format = "yyyy-MM-dd")
        private Date birth;
        private Pet pet;
    }

    Spring?Boot數(shù)據(jù)響應(yīng)問題實例分析

    5.2、自定義的Converter

    除此之外,這些都是默認的,我們可以進行擴展,如下實現(xiàn)自定義的設(shè)置轉(zhuǎn)化,如下,利用這個代碼:

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
    
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    
            }
        }
    }

    測試

    @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)計所有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());
        }
    }

    測試:

    import java.util.Date;
    
    @Controller
    public class ResponseTestController {
    
        /**
         * 1、瀏覽器發(fā)請求直接返回 xml    [application/xml]        jacksonXmlConverter
         * 2、如果是ajax請求 返回 json   [application/json]      jacksonJsonConverter
         * 3、如果硅谷app發(fā)請求,返回自定義協(xié)議數(shù)據(jù)  [appliaction/x-guigu]   xxxxConverter
         *          屬性值1;屬性值2;
         *
         * 步驟:
         * 1、添加自定義的MessageConverter進系統(tǒng)底層
         * 2、系統(tǒng)底層就會統(tǒng)計出所有MessageConverter能操作哪些類型
         * 3、客戶端內(nèi)容協(xié)商 [guigu--->guigu]
         *
         * 作業(yè):如何以參數(shù)的方式進行內(nèi)容協(xié)商
         * @return
         */
        @ResponseBody  //利用返回值處理器里面的消息轉(zhuǎ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ā)要注意,有可能我們添加的自定義的功能會覆蓋默認很多功能,導(dǎo)致一些默認的功能失效。

    讀到這里,這篇“Spring Boot數(shù)據(jù)響應(yīng)問題實例分析”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI