Http Post avec demande Type de contenu formulaire ne fonctionne pas au printemps MVC 3

extrait de code:

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

après réception de la requête, ce que j'ai obtenu est le code de statut Http 415: Le serveur a refusé cette requête parce que l'entité de requête est dans un format non supporté par la ressource demandée pour la méthode demandée ().

Si je change le code:

extrait de code:

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

je vais obtenir 405 method not allowed. Chose drôle est dans l'en-tête allow de response, il Liste GET et POST comme méthodes autorisées.

j'ai une classe qui ne JOSN mapping:

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

}

copié à partir d'exemples de printemps. fonctionne très bien avec json content-type.

une question plus générale est de savoir comment faire fonctionner les MVC request handlers avec différents types de contenu de requête. Tout conseil serait grandement apprécié.

35
demandé sur marathon 2010-12-02 22:52:21

7 réponses

Malheureusement FormHttpMessageConverter (qui est utilisé pour le @RequestBody - paramètres annotés lorsque le type de contenu est application/x-www-form-urlencoded) ne peut pas lier les classes cibles (comme @ModelAttribute).

vous avez donc besoin de @ModelAttribute au lieu de @RequestBody. Si vous n'avez pas besoin de passer les différents types de contenu à cette méthode, vous pouvez simplement remplacer l'annotation:

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

sinon je suppose que vous pouvez créer un formulaire de méthode séparé traiter les données du formulaire avec leheaders attribut:

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

EDIT: une autre option possible est d'implémenter votre propre HttpMessageConverter en combinant FormHttpMessageConverter (pour convertir le message d'entrée à la carte des paramètres) et WebDataBinder (pour convertir la carte des paramètres de l'objet cible).

54
répondu axtavt 2010-12-02 20:34:51

j'ai eu la réponse HTTP du code de 415

mes problèmes ont été résolus quand j'ai ajouté le type de contenu à l'en-tête de la requête

e.g

"Content-Type: application / json"

22
répondu user1306828 2012-07-20 19:43:17

au cœur du problème, nous souhaitons accepter à la fois les types application/json et application/x-www-form-urlencoded Content avec le même gestionnaire de requêtes.

pour ce faire, j'utilise le @RequestBody, qui travaillait déjà pour application/json pour moi (et généralement d'autres des threads que j'ai trouvés, mais il y a du travail supplémentaire pour que application/x-www-form-urlencoded puisse être utilisé avec @RequestBody.

tout d'abord, créer un nouveau convertisseur Httpmessagecapable de changer la demande de saisie d'un objet. Je le fais en réutilisant le FormHttpMessageConverter, qui est déjà capable de changer l'entrée en MultiValueMap. Je change alors le MultiValueMap en une carte régulière, et utilise Jackson pour tourner la carte à l'objet désiré.

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

il y a beaucoup de différentes façons dont une application de printemps pourrait prendre ce convertisseur de message. Pour moi, cela a été accompli dans un fichier XML:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>
3
répondu jswidler 2015-06-27 03:17:41

utiliser @ModelAttribute est en effet le moyen préféré pour traiter les paramètres de forme.

2
répondu Rossen 2011-06-08 10:37:35

L'utilisation de JSON a fonctionné pour moi aussi, je suppose que cela fait l'interprète JSON obtenir les données du corps. J'essayais D'utiliser PUT, ce qui est un peu plus difficile. Vous pouvez lire mon post à ce sujet ici.

0
répondu TheZuck 2012-09-28 07:48:16

ci-Dessous a fonctionné pour moi

côté serveur:

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

côté client:

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
répondu Deepti Kohli 2013-10-01 06:40:50

j'utilise ce code pour convertir la forme html en json .

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

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

                        return json;
                    }

et utiliser des citations simples était erroné . J'ai changé en "" et le problème est résolu.

0
répondu amin arab 2016-05-27 06:45:27