Post Http z formularzem typu request content nie działa w Spring MVC 3

Fragment kodu:

@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");
}

Po otrzymaniu żądania otrzymałem kod statusu Http 415: Serwer odmówił tego żądania, ponieważ jednostka żądania jest w formacie, który nie jest obsługiwany przez żądany zasób dla żądanej metody ().

Jeśli zmienię kod na ten:

Fragment kodu:

@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");
}

Dostanę metodę 405 niedozwoloną. Zabawne jest to, że w nagłówku allow odpowiedzi znajduje się lista GET I POST jako dozwolonych metod.

Mam klasę, która Odwzorowanie 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;
}

}

Skopiowane z wiosennych przykładów. działa świetnie z typem zawartości JSON.

Bardziej ogólnym pytaniem jest to, jak sprawić, by programy obsługi żądań MVC spring działały z różnymi typami żądań. Każda rada będzie bardzo mile widziana.

Author: marathon, 2010-12-02

7 answers

Niestety FormHttpMessageConverter (który jest używany dla @RequestBody - parametry z adnotacją, gdy typem zawartości jest application/x-www-form-urlencoded) nie mogą wiązać klas docelowych (jak @ModelAttribute może).

Dlatego potrzebujesz @ModelAttribute zamiast @RequestBody. Jeśli nie musisz przekazywać różnych typów zawartości do tej metody, możesz po prostu zastąpić adnotację:

@RequestMapping(method = RequestMethod.POST)
public ModelAndView create(@ModelAttribute UserAccountBean account) { ... }

W przeciwnym razie można utworzyć osobną metodę przetwarzania danych formularza z odpowiednim atrybutem headers:

@RequestMapping(method = RequestMethod.POST, 
    headers = "content-type=application/x-www-form-urlencoded") 
public ModelAndView createFromForm(@ModelAttribute UserAccountBean account) { ... }

EDIT: inny możliwy opcja jest zaimplementowanie własnej HttpMessageConverter przez połączenie FormHttpMessageConverter (do konwersji wiadomości wejściowej na mapę parametrów) i WebDataBinder (do konwersji mapy parametrów na obiekt docelowy).

 54
Author: axtavt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 20:34:51

Miałem kod odpowiedzi HTTP 415

Moje problemy zostały rozwiązane, gdy dodałem Content Type do nagłówka żądania

E. g

"Content-Type: application / json"

 22
Author: user1306828,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-20 19:43:17

W samym sercu problemu, chcemy zaakceptować zarówno application / json, jak i application / x-www-form-urlencoded Content-types z tym samym handlerem żądań.

Aby to zrobić, używam @RequestBody, który już działał dla application / json dla mnie (i ogólnie innych z wątków, które znalazłem, ale jest dodatkowa praca, więc application/x-www-form-urlencoded może być używany z @RequestBody.

Najpierw Utwórz nowy HttpMessageConverter, który może zmienić wejście żądania na obiekt. Robię to przez ponowne użycie FormHttpMessageConverter, który jest już w stanie zmienić wejście na wielowartościową mapę. Następnie zmieniam mapę wielowartościową na zwykłą mapę i używam Jacksona, aby obrócić mapę na żądany obiekt.

Oto kod dla 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("");
    }
}

Istnieje wiele różnych sposobów, w jaki aplikacja Spring może odebrać ten konwerter wiadomości. Dla mnie zostało to zrealizowane w pliku XML:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>
 3
Author: jswidler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 03:17:41

Używanie @ ModelAttribute jest rzeczywiście preferowanym sposobem radzenia sobie z parametrami formularza.

 2
Author: Rossen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-08 10:37:35

Uĺźycie JSONA rĂłwnieĹź mi dziaĹ ' aĹ 'o, przypuszczam, Ĺźe sprawia, Ĺźe interpreter JSON pobiera dane z ciaĹ' a. Próbowałem użyć PUT, co jest nieco trudniejsze. Możesz przeczytać mój post o tym tutaj.

 0
Author: TheZuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-28 07:48:16

Poniżej działa dla mnie

Po stronie serwera:

 @RequestMapping(value = "test", method = RequestMethod.POST, consumes = {"application/xml", "application/json"})
        @ResponseStatus(HttpStatus.OK)
        public @ResponseBody
        String methodName(@RequestBody EntityClassName entity) {

Po stronie klienta:

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
Author: Deepti Kohli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-01 06:40:50

Używam tego kodu do konwersji formularza html na json .

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

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

                        return json;
                    }

I użycie pojedynczych cytatów było błędne . Zmieniłem na "" i problem rozwiązany.

 0
Author: amin arab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-27 06:45:27