最近我发现,如果我们在accept-header中添加一些参数,由于isCompatibleWith()
中的org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
方法,Spring很难获得正确的响应标头
例如,如果我们有一个这样的端点:
@ApiResponse(code = ...)
@RequestMapping(value = {/Id}, method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v1'",
MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v2'",
MediaType.APPLICATION_JSON_VALUE + "; profile='http://profiles/v3'",})
@ResponseBody
public HttpEntity<? ..> getXXX(@PathVariable(value = "Id") String Id) throws Exception {
// business logic
}
现在,如果在请求标头中,我给出:
Accept = " "
或No Accept header
=>它将按预期返回内容类型为"application/json; profile='http://profiles/v1'"
。Accept = "application/json; profile='http://profiles/v3'"
=>由于producibleMediaTypes
中存在匹配项,它将返回相同的Content-Type。Accept = "application/json; profile='http://invalid/profiles/v1'"
=> producibleMediaTypes
中没有匹配项,如预期的那样,它应该返回默认的Content-Type,它是producibleMediaTypes
中的第一个。但是现在,它将返回与我们在Request-Header中提供的值完全相同的值
,对于上述内容,我将获得"application/json; profile='http://invalid/profiles/v1'"
调试后,我发现:
"*/*"
之前自动将Accept-Header设置为writeWithMessageConverters()
,这意味着与producibleMediaTypes
匹配,它将根据其比较策略传递默认的Content-Type。producibleMediaTypes
中具有完美匹配,因此它将返回正确的Content-Type。@ResponseBody
的方式:protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
Type returnValueType = getGenericType(returnType);
HttpServletRequest servletRequest = inputMessage.getServletRequest();
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType);
if (returnValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass);
}
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (compatibleMediaTypes.isEmpty()) {
if (returnValue != null) {
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
}
return;
}
requestedMediaTypes
是我们在请求标头中传递的内容,producibleMediaTypes
是我们在@ReqeustMapping
中提供的内容,经过比较,我理解的是 *** Spring * < / em>将选择更具体的一个作为selectedMediaType
,它将作为Response-Header返回。但是,总是选择无效的作为更具体的一个。
以下是有关Github的讨论:
Use parameters declared in consumes or produces condition to narrow the request mapping [SPR-17133]
Content negotiation ignores media type parameters [SPR-10903]
似乎他们想改进兼容策略,或者以某种方式允许开发人员实现自己的参数匹配,而不是等待它,还有其他方法来处理无效的content-type参数吗?
是否有任何方法可以在匹配之前获取producibleMediaTypes
?如果是这样,我们可以将无效的内容类型标记为"*/*
,则可以按预期获取默认内容类型。还是可以在将内容类型传递给Response-Header之前再次检查它的内容类型?
您以前遇到过这种情况吗?任何建议或经验都会有所帮助!