it-swarm.com.de

HTTP-Post mit Anforderungsinhaltstyp-Formular funktioniert in Spring MVC 3 nicht

code-Auszug:

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

Nachdem ich die Anfrage erhalten habe, erhalte ich den HTTP-Statuscode 415: Der Server hat diese Anfrage abgelehnt, weil die Anfrage-Entität in einem Format vorliegt, das von der angeforderten Ressource für die angeforderte Methode () nicht unterstützt wird.

Wenn ich den Code in diesen ändere:

code-Auszug:

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

Ich werde 405 Methode nicht erlaubt bekommen. Das Komische ist, dass im Header "allow" der Antwort "GET" und "POST" als zulässige Methoden aufgeführt sind.

Ich habe eine Klasse, die JOSN-Mapping macht:

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

}

Aus Frühlingsbeispielen kopiert. Funktioniert hervorragend mit JSON-Inhaltstypen.

Eine allgemeinere Frage ist, wie Spring MVC-Request-Handler mit verschiedenen Request-Content-Typen arbeiten sollen. Jeder Rat wäre sehr dankbar.

35
Bobo

Leider kann FormHttpMessageConverter (das für @RequestBody - mit Anmerkungen versehene Parameter verwendet wird, wenn der Inhaltstyp application/x-www-form-urlencoded Ist) keine Zielklassen binden (wie @ModelAttribute Kann).

Daher benötigen Sie @ModelAttribute Anstelle von @RequestBody. Wenn Sie dieser Methode keine unterschiedlichen Inhaltstypen übergeben müssen, können Sie die Anmerkung einfach ersetzen:

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

Andernfalls können Sie ein separates Methodenformular für die Verarbeitung von Formulardaten mit dem entsprechenden headers -Attribut erstellen:

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

BEARBEITEN: Eine andere mögliche Option besteht darin, Ihr eigenes HttpMessageConverter zu implementieren, indem Sie FormHttpMessageConverter kombinieren (um die Eingangsnachricht in die Karte der Parameter umzuwandeln) und WebDataBinder ( um die Parameterkarte in das Zielobjekt umzuwandeln).

55
axtavt

Ich hatte HTTP-Antwortcode 415

Meine Probleme wurden behoben, als ich den Inhaltstyp zum Anforderungsheader hinzufügte

z.B

"Inhaltstyp: application/json"

22
user1306828

Im Kern des Problems möchten wir sowohl application/json als auch application/x-www-form-urlencoded Content-Types mit demselben Request-Handler akzeptieren.

Dazu verwende ich den @RequestBody, der bereits für application/json funktioniert hat (und im Allgemeinen auch für andere Threads, die ich gefunden habe), aber es gibt zusätzliche Arbeit, sodass application/x-www-form-urlencoded verwendet werden kann mit @RequestBody.

Erstellen Sie zunächst einen neuen HttpMessageConverter, der die Anforderungseingabe für ein Objekt ändern kann. Ich verwende dazu den FormHttpMessageConverter, der bereits in der Lage ist, die Eingabe in eine MultiValueMap zu ändern. Ich ändere dann die MultiValueMap in eine normale Map und verwandle die Map mit Jackson in das gewünschte Objekt.

Hier ist der Code für den 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("");
    }
}

Es gibt viele verschiedene Möglichkeiten, wie eine Spring-App diesen Nachrichtenkonverter aufnimmt. Für mich wurde es in einer XML-Datei durchgeführt:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>
4
jswidler

Die Verwendung von @ModelAttribute ist in der Tat die bevorzugte Methode, um mit Formularparametern umzugehen.

2
Rossen

Die Verwendung von JSON hat auch für mich funktioniert. Ich nehme an, dass der JSON-Interpreter die Daten aus dem Body abruft. Ich habe versucht, PUT zu verwenden, was etwas schwieriger ist. Sie können meinen Beitrag darüber lesen hier .

0
TheZuck

Ich verwende diesen Code für die Konvertierung von HTML-Formularen in JSON.

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

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

                        return json;
                    }

und einfache Anführungszeichen war falsch. Ich habe zu "" gewechselt und das Problem wurde behoben.

0
Amin Arab

Unten hat bei mir gearbeitet

Auf der Serverseite:

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

Auf Kundenseite:

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
Deepti Kohli