Недавно я обнаружил, что если мы добавим некоторые параметры в заголовок accept, для Spring будет трудно получить правильный заголовок ответа из-за его метода isCompatibleWith()
в org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
Например,если у нас есть конечная точка, подобная этой:
@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
=> он вернет Content-Введите "application/json; profile='http://profiles/v1'"
, как и ожидалось. Accept = "application/json; profile='http://profiles/v3'"
=> он вернет Content-Type как то же самое, поскольку в producibleMediaTypes
. Accept = "application/json; profile='http://invalid/profiles/v1'"
=> в producibleMediaTypes
нет совпадений, как и ожидалось, он должен вернуть тип содержимого по умолчанию, который является первым в producibleMediaTypes
.Но теперь он вернет точно то же значение, которое мы указали в заголовке запроса, для вышеупомянутого я получу "application/json; profile='http://invalid/profiles/v1'"
После отладки я обнаружил, что:
- Для первого сценария Spring автоматически установит заголовок принятия как
"*/*"
, прежде чем он перейдет в writeWithMessageConverters()
, что означает после сопоставления сproducibleMediaTypes
, он будет передавать тип контента по умолчанию на основе своих стратегий сравнения. - Для второго сценария, поскольку он имеет идеальное совпадение в
producibleMediaTypes
, он вернет правильный тип контента. - Для сценария Ошибка , вот как Весна справляется с
@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 * выберет более конкретный в качествеselectedMediaType
, который будет возвращен как заголовок ответа.Тем не менее, недопустимый всегда выбирался как более конкретный.
Вот некоторые обсуждения на Github:
Используйте параметры, объявленные в потребителях, или создайте условие для суженияотображение запросов [SPR-17133]
Согласование содержимого игнорирует параметры типа носителя [SPR-10903]
, кажется, что они хотят улучшить совместимую стратегию или каким-то образомпозволить разработчикам реализовать свое собственное сопоставление параметров, вместо того, чтобы ждать этого, есть ли другой способ обработать недопустимый параметр типа контента?
Например, есть ли способ получить producibleMediaTypes
перед сопоставлением?Если это так, мы можем пометить недопустимый тип контента как "*/*
, тогда мы можем получить тип по умолчанию, как и ожидалось.Или мы можем еще раз проверить тип содержимого, прежде чем передать его заголовку ответа?
Вы когда-нибудь сталкивались с такой ситуацией раньше?Любой совет или опыт помогут!