Http Post с формой запроса контента не работает в Spring MVC 3 - PullRequest
35 голосов
/ 02 декабря 2010

фрагмент кода:

@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 Метод не разрешен. Забавно, что в заголовке ответа allow указаны 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 работать с различными типами содержимого запросов. Любой совет будет принята с благодарностью.

Ответы [ 7 ]

55 голосов
/ 02 декабря 2010

К сожалению, FormHttpMessageConverter (который используется для @RequestBody -аннотированных параметров, когда тип контента application/x-www-form-urlencoded) не может связать целевые классы (как может @ModelAttribute).

Поэтому вам нужно @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) { ... }

РЕДАКТИРОВАТЬ: Другой возможный вариант заключается в реализации собственного HttpMessageConverter путем объединения FormHttpMessageConverter (для преобразования входного сообщения в карту параметров) и WebDataBinder (преобразовать карту параметров в целевой объект).

22 голосов
/ 20 июля 2012

У меня был HTTP-код ответа 415

Мои проблемы были решены, когда я добавил Тип контента в заголовок запроса

* 1005 например *

"Тип контента: application / json"

4 голосов
/ 27 июня 2015

Суть проблемы заключается в том, что мы хотим принять и Content-типы application / json и application / x-www-form-urlencoded с одним и тем же обработчиком запросов.

Для этого я использую@RequestBody, которая уже работала для application / json для меня (и вообще для других из найденных мной потоков, но есть дополнительная работа, поэтому application / x-www-form-urlencoded можно использовать с @ RequestBody.

Сначала создайте новый HttpMessageConverter, способный изменять входные данные запроса для объекта. Я делаю это путем повторного использования FormHttpMessageConverter, который уже способен изменять входные данные в MultiValueMap. Затем я изменяю MultiValueMap на обычную карту ииспользуйте Джексона, чтобы превратить карту в нужный объект.

Вот код для 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>
2 голосов
/ 08 июня 2011

Использование @ModelAttribute действительно предпочтительный способ работы с параметрами формы.

0 голосов
/ 27 мая 2016

Я использую этот код для преобразования HTML-формы в JSON.

function ConvertFormToJSON(form) {
                        var array = $(form).serializeArray();
                        var json = {};

                        $.each(array, function() {
                            json[this.name] = this.value || '';
                        });

                        return json;
                    }

и использовать одинарные кавычки было неправильно. Я изменил «» на «», и проблема решена.

0 голосов
/ 01 октября 2013

ниже работал у меня

На стороне сервера:

 @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);
0 голосов
/ 28 сентября 2012

Использование JSON также помогло мне, я полагаю, это заставляет интерпретатор JSON получать данные из тела.Я пытался использовать PUT, хотя это немного сложнее.Вы можете прочитать мой пост об этом здесь .

...