Http Post с формой типа содержимого запроса, не работающей в Spring MVC 3
фрагмент кода:
@RequestMapping(method = RequestMethod.POST)//, headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(@RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
После получения запроса, у меня есть код состояния Http 415:
Сервер отклонил этот запрос, поскольку объект запроса находится в формате, не поддерживаемом запрошенным ресурсом для запрошенного метода().
Если я изменю код на это:
фрагмент кода:
@RequestMapping(method = RequestMethod.POST,headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(@RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
Я получу 405 Метод не допускается. Забавно, что в заголовке ответа есть ответ, он перечисляет GET и POST как разрешенные методы.
У меня есть класс, который отображает JOSN:
@Component
public class JacksonConversionServiceConfigurer implements BeanPostProcessor {
private final ConversionService conversionService;
@Autowired
public JacksonConversionServiceConfigurer(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
HttpMessageConverter<?>[] converters = adapter.getMessageConverters();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ConversionServiceAwareObjectMapper(this.conversionService));
}
}
}
return bean;
}
}
Скопировано из примеров Spring. отлично работает с контентом типа JSON.
Более общий вопрос заключается в том, как заставить обработчики запросов Spring mvc работать с разными типами содержимого запроса.
Любые советы будут очень признательны.
Ответы
Ответ 1
К сожалению FormHttpMessageConverter
(который используется для @RequestBody
-аннотированных параметров, когда тип содержимого application/x-www-form-urlencoded
) не может связывать целевые классы (как @ModelAttribute
can).
Поэтому вам нужно @ModelAttribute
вместо @RequestBody
. Если вам не нужно передавать разные типы контента этому методу, вы можете просто заменить аннотацию:
@RequestMapping(method = RequestMethod.POST)
public ModelAndView create(@ModelAttribute UserAccountBean account) { ... }
В противном случае, я думаю, вы можете создать отдельную форму обработки формы формы с соответствующим атрибутом headers
:
@RequestMapping(method = RequestMethod.POST,
headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView createFromForm(@ModelAttribute UserAccountBean account) { ... }
EDIT: Еще один возможный вариант - реализовать собственный HttpMessageConverter
, объединив FormHttpMessageConverter
(чтобы преобразовать входное сообщение в карту параметров) и WebDataBinder
(чтобы преобразовать карту параметров в целевой объект).
Ответ 2
У меня был HTTP-код ответа 415
Мои проблемы были устранены, когда я добавил Content Type для запроса заголовка
например
"Content-Type: application/json"
Ответ 3
В основе проблемы мы хотим принять и Content-типы application/json и application/x-www-form-urlencoded с тем же обработчиком запросов.
Для этого я использую @RequestBody, который уже работал для приложения /json для меня (и, как правило, другие из тем, которые я нашел, но есть дополнительная работа, поэтому application/x-www-form-urlencoded может использоваться с @RequestBody.
Сначала создайте новый HttpMessageConverter, способный изменять ввод запроса на объект. Я делаю это, повторно используя FormHttpMessageConverter, который уже способен изменять ввод на MultiValueMap. Затем я изменяю MultiValueMap на обычную карту и использую Jackson для перевода карты на нужный объект.
Вот код для HttpMessageConverter:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* <p>Converts HTTP requests with bodies that are application/x-www-form-urlencoded or multipart/form-data to an Object
* annotated with {@link org.springframework.web.bind.annotation.RequestBody} in the the handler method.
*
* @author Jesse Swidler
*/
public class ObjectHttpMessageConverter implements HttpMessageConverter<Object> {
private final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
private final ObjectMapper objectMapper = new ObjectMapper();
private static final LinkedMultiValueMap<String, String> LINKED_MULTI_VALUE_MAP = new LinkedMultiValueMap<>();
private static final Class<? extends MultiValueMap<String, ?>> LINKED_MULTI_VALUE_MAP_CLASS
= (Class<? extends MultiValueMap<String, ?>>) LINKED_MULTI_VALUE_MAP.getClass();
@Override
public boolean canRead(Class clazz, MediaType mediaType) {
return objectMapper.canSerialize(clazz) && formHttpMessageConverter.canRead(MultiValueMap.class, mediaType);
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType) {
return false;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return formHttpMessageConverter.getSupportedMediaTypes();
}
@Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
Map<String, String> input = formHttpMessageConverter.read(LINKED_MULTI_VALUE_MAP_CLASS, inputMessage).toSingleValueMap();
return objectMapper.convertValue(input, clazz);
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws UnsupportedOperationException {
throw new UnsupportedOperationException("");
}
}
Существует множество разных способов, по которым приложение Spring может забрать этот конвертер сообщений. Для меня это было выполнено в XML файле:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
Ответ 4
Использование @ModelAttribute действительно является предпочтительным способом обработки параметров формы.
Ответ 5
Использование JSON также работало для меня, я полагаю, это делает интерпретатор JSON для получения данных из тела.
Я пытался использовать PUT, хотя это немного сложнее.
Вы можете прочитать мое сообщение об этом здесь.
Ответ 6
Ниже работал у меня
На стороне сервера:
@RequestMapping(value = "test", method = RequestMethod.POST, consumes = {"application/xml", "application/json"})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody
String methodName(@RequestBody EntityClassName entity) {
На стороне клиента:
String json = new JSONStringer().object()
.key("key").value("value")
.endObject()
.toString();
StringEntity se = new StringEntity(json);
se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
request.setEntity(se);
HttpResponse response = client.execute(request);
Ответ 7
Я использую этот код для преобразования html-формы в json.
function ConvertFormToJSON(form) {
var array = $(form).serializeArray();
var json = {};
$.each(array, function() {
json[this.name] = this.value || '';
});
return json;
}
и использование одиночных котировок было неправильным. Я изменил "на" и "решить проблему".