背景:使用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> 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 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 留看