2010-12-02 14 views
35

fragmento de código:HTTP Post con forma de tipo contenido de la petición que no trabajan en 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"); 
} 

vez recibida la petición, Lo que tengo es código de estado HTTP 415: El servidor rechazó esta solicitud porque la entidad de solicitud es en un formato no admitido por el recurso solicitado para el método solicitado().

si cambio el código para esto:

fragmento de código:

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

voy a tener 405 Método no permitido. Lo gracioso está en el encabezado de permiso de respuesta, enumera GET y POST como métodos permitidos.

tengo una clase que hace JOSN mapeo:

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

} 

Copiado de ejemplos de primavera. funciona muy bien con el tipo de contenido JSON.

Una pregunta más general es cómo hacer que los controladores de solicitudes mvc de primavera funcionen con diferentes tipos de contenido de solicitud. Cualquier consejo sería muy apreciado.

Respuesta

53

Desafortunadamente FormHttpMessageConverter (que se utiliza para @RequestBody parámetros -annotated cuando el tipo de contenido es application/x-www-form-urlencoded) no puede unirse clases objetivo (como @ModelAttribute puede). Por favor, necesita @ModelAttribute en lugar de @RequestBody. Si usted no tiene que pasar diferentes tipos de contenido a ese método puede simplemente reemplazar la anotación:

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

De lo contrario supongo que se puede crear un formulario de datos de formularios proceso de los métodos por separado con los headers atributo apropiado:

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

EDIT: Otra opción posible es implementar su propio HttpMessageConverter combinando FormHttpMessageConverter (para convertir el mensaje de entrada al mapa de parámetros) y WebDataBinder (para convertir mapa de parámetros al obje objetivo Connecticut).

+0

Por cierto, ¿dónde lo descubriste? desde cualquier documento de referencia (si es así, ¿puedes compartirlo?) o por intento y error? – Bobo

+0

@Bobo: El hecho de que 'FormHttpMessageConverter' produzca' MultiValueMap' se puede encontrar en su javadoc: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/http/converter /FormHttpMessageConverter.html – axtavt

+0

Vaya. No revisó el javadoc. Otra cosa que no entiendo es por qué spring framework no detectará eso y me lanzará un mensaje de error en el momento del despliegue. – Bobo

2

Usar @ModelAttribute es de hecho la forma preferida de tratar con los parámetros del formulario.

22

que estaba teniendo código de respuesta HTTP de 415

Mis problemas se resolvieron cuando adición Tipo de contenido para solicitar cabecera

por ejemplo

"Content-Type: application/json"

0

Usar JSON también funcionó para mí, supongo que hace que el intérprete JSON obtenga los datos del cuerpo. Estaba tratando de usar PUT, lo que es un poco más difícil. Puedes leer mi publicación al respecto here.

0

A continuación trabajó para mí

En el lado del servidor:

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

En el lado del cliente:

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); 
3

En el corazón del problema, deseamos aceptar tanto application/json y application/x-www-form-urlencoded Content-types con el mismo manejador de solicitudes.

Para hacer esto, utilizo @RequestBody, que ya estaba trabajando para application/json para mí (y generalmente otros de los hilos que he encontrado, pero hay trabajo adicional para application/x-www-form- urlencoded se puede usar con @RequestBody.

Primero, cree un nuevo HttpMessageConverter capaz de cambiar la entrada de solicitud a un objeto. Lo hago reutilizando FormHttpMessageConverter, que ya es capaz de cambiar la entrada a MultiValueMap. cambie el MultiValueMap a un mapa normal y use Jackson para convertir el mapa en el objeto deseado.

Aquí está el código para el 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(""); 
    } 
} 

Hay muchas formas diferentes en que una aplicación de Spring puede elegir ese convertidor de mensajes. Para mí, se llevó a cabo en un archivo XML:

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

utilizo este código para el formulario de convertir HTML a JSON.

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

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

         return json; 
        } 

y utilizar comillas simples era incorrecto. Cambié a '' '' '' '' y problema resuelto.

Cuestiones relacionadas