首頁 > 軟體

SpringBoot自定義MessageConverter與內容協商管理器contentNegotiationManager詳解

2022-10-10 14:01:39

1、自定義訊息轉換器MessageConverter

在WebMvcAutoConfiguration類中有一個方法configureMessageConverters(),它會設定預設的MessageConverter

public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	this.messageConvertersProvider.ifAvailable((customConverters) -> {
		converters.addAll(customConverters.getConverters());
	});
}

假設我們現在有一個新的需求

想要後端返回我們自己定義的格式的資料,就叫x-decade,格式為使用分號拼接Person物件屬性值

那麼就要新建一個MessageConverter了

package com.decade.converters;
import com.decade.pojo.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class DecadeConverter implements HttpMessageConverter<Person> {
	// 只考慮寫出,所以這裡預設寫false
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);
    }
    /**
     * 統計當前converter能支援哪些型別
     * @return 返回支援的媒體型別
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-decade");
    }
	// 只考慮寫出,所以這裡預設寫null
    @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 {
        // 自定義想要寫出的資料格式
        String result = person.getName() + ";" + person.getAge() + ";" + person.getBirth();
        // 寫出
        final OutputStream body = outputMessage.getBody();
        body.write(result.getBytes());
    }
}

我們發現,WebMvcConfigurer介面類下面有2個關於設定MessageConverter的方法

// 會覆蓋預設的MessageConverter
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
// 不會覆蓋,只會在預設的MessageConverter後面追加我們自定義的
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}

所以,我們選擇在自定義設定類中重寫extendMessageConverters()方法

package com.decade.config;
import com.decade.converters.DecadeConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration(proxyBeanMethods = false)
public class MyMvcConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer createConvert() {
        return new WebMvcConfigurer() {
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new DecadeConverter());
            }
        };
    }
}

我們在請求頭中設定Accept為我們自定義的格式application/x-decade

驗證結果如下

2、自定義內容協商管理器contentNegotiationManager

問題:我們新建的x-decade格式是否只能通過postman呼叫才會生效呢?如果我們要使用瀏覽器引數方式,怎麼才能生效呢?

因為我們之前的【Spring Boot】響應處理

它預設只能處理xml和json格式,所以為了解決這個問題,我們就要自定義內容協商管理器了

首先我們還是要在自定義設定類中重寫相關方法

package com.decade.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration(proxyBeanMethods = false)
public class MyMvcConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer createConvert() {
        return new WebMvcConfigurer() {
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                // 設定支援的瀏覽器引數型別
                Map<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json", MediaType.APPLICATION_JSON);
                mediaTypes.put("xml", MediaType.APPLICATION_XML);
                mediaTypes.put("decade", MediaType.parseMediaType("application/x-decade"));
                final ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
                // 為了繼續支援請求頭引數型別,還需要往裡面塞請求頭內容協商管理器
                final HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
                configurer.strategies(Arrays.asList(strategy, headerContentNegotiationStrategy));
            }
        };
    }
}

可以看到,系統中的內容協商管理器下面還是原來的2種:獲取請求頭中的Accept和獲取請求引數中的format

但是獲取請求引數format,除了能識別原來的json和xml,還能識別我們自定義的application/x-decade,它使用decade與之對應

可以看到,我們自定義的媒體型別成功加入伺服器能解析出來的型別

我們寫一個介面進行驗證

@GetMapping(value = "/testPerson")
@ResponseBody
public Person testPerson() {
	Person person = new Person();
	person.setName("decade");
	person.setAge(24);
	person.setBirth(new Date());
	return person;
}

由驗證結果可以知道,我們從postman和瀏覽器都可以獲得我們指定格式的資料

到此這篇關於SpringBoot自定義MessageConverter與內容協商管理器contentNegotiationManager詳解的文章就介紹到這了,更多相關SpringBoot MessageConverter內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com