背景:使用protobuf2做與前端的數據交互,自己用junit測試的時候沒問題,但是前端人員訪問接口,總是報錯:
com.google.protobuf.InvalidProtocolBufferException: Protocol message end-group tag did not match expected tag.
剛開始以爲是protobuf jar包版本不一致導致的問題,後來自己用不同版本的protobuf訪問接口也沒問題。找啊找都沒找到問題出在哪兒,後來前端的同學說正常應該返回二進制數據,但是我們返回的是個string,根據這個點找到了問題。
結果就是:接口沒指定返回的數據類型,
RequestMapping("api/getC")改成 @RequestMapping(value = "api/getC", produces = "application/x-protobuf").
問題解決後,回去看爲什麼自己寫的junitTest沒問題,然後發現有指定accept,如圖153行代碼:
,當我刪掉produces配置再把153行刪掉,果然報錯了。
後記1:
produces :指定返回值類型,還設定返回值的字符編碼;對應header裏面的Accept;
consumes:指定處理請求的提交內容類型;對應header裏面的Content-Type);
後記2: 查詢資料過程中,又在思考,框架是怎麼根據這些參數去返回數據的呢。這裏是spring的HttpMessageConverter這個類起作用的,springmvc默認會加載StringHttpMessageConverter、ByteArrayHttpMessageConverter等,配置如圖。
@Configuration
public class SpringMVCConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setDefaultCharset(Charsets.UTF_8);
stringConverter.setSupportedMediaTypes(Arrays.asList(MediaType.parseMediaType("text/plain;charset=UTF-8")));
converters.add(stringConverter);
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
fastConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8));
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
converters.add(new ProtobufHttpMessageConverter());
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new HeaderArgumentResolver());
}
}
mvc會根據request中的content-type,遍歷messageConverters中的converter,如果(targetClass != null && converter.canRead(targetClass, contentType))==true 說明找到了對應的轉換器,如果debug模式可以看到如下日誌:
Read [class com.cc.apply.QueryRequest] as "application/x-protobuf;charset=UTF-8" with [org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter@20413045]
canRead方法調用的其實是 AbstractHttpMessageConverter的canRead方法:
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return this.supports(clazz) && this.canRead(mediaType);
}
返回的時候調用AbstractHttpMessageConverter的canWrite方法,確定適用的轉換器爲protobuf,writeIternal()
找到一篇把整個流程寫的比較清楚的:https://blog.csdn.net/everyok/article/details/81350894 留看