POST JSON falla con 415 Tipo de medio no compatible, Spring 3 mvc

171

Estoy tratando de enviar una solicitud POST a un servlet. La solicitud se envía a través de jQuery de esta manera:

var productCategory = new Object();
productCategory.idProductCategory = 1;
productCategory.description = "Descrizione2";
newCategory(productCategory);

donde newCategory es

function newCategory(productCategory)
{
  $.postJSON("ajax/newproductcategory", productCategory, function(
      idProductCategory)
  {
    console.debug("Inserted: " + idProductCategory);
  });
}

y postJSON es

$.postJSON = function(url, data, callback) {
    return jQuery.ajax({
    'type': 'POST',
    'url': url,
    'contentType': 'application/json',
    'data': JSON.stringify(data),
    'dataType': 'json',
    'success': callback
    });
};

Con firebug veo que JSON se envía correctamente:

{"idProductCategory":1,"description":"Descrizione2"}

Pero obtengo 415 tipo de medio no compatible. El controlador Spring mvc tiene firma

    @RequestMapping(value = "/ajax/newproductcategory", method = RequestMethod.POST)
public @ResponseBody
Integer newProductCategory(HttpServletRequest request,
        @RequestBody ProductCategory productCategory)

Hace unos días funcionó, ahora no lo es. Mostraré más código si es necesario. Gracias

gc5
fuente
¿Qué has cambiado desde hace unos días? Además, ¿no var productCategory = { idProductCategory: 1, description: "Descrizione2" };sería más conciso y más fácil de leer? ¿Necesitas decirle a Spring que acepte application/jsonespecíficamente? En otras palabras, ¿se espera que los datos lleguen en forma?
Dave Newton
Muchas cosas desde que estaba trabajando en otra parte de este proyecto, y hoy encontré esta regresión. En esta parte no cambié nada. Sí, tengo que usarlo de esta manera porque recibo información de un formulario.
gc5
No, no lo estás, lo estás obteniendo de una publicación JSON Ajax, que no es lo mismo que los datos codificados en un formulario.
Dave Newton
1
¿Estás seguro de que Jackson todavía está disponible en tu CLASSPATH?
Tomasz Nurkiewicz
1
si envía texto / json en lugar de aplicación / json, obtiene el mismo error
Blacksonic

Respuestas:

250

He tenido esto antes con Spring @ResponseBody y fue porque no se envió un encabezado de aceptación con la solicitud. Aceptar encabezado puede ser difícil de configurar con jQuery, pero esto funcionó para mí fuente

$.postJSON = function(url, data, callback) {
    return jQuery.ajax({
    headers: { 
        'Accept': 'application/json',
        'Content-Type': 'application/json' 
    },
    'type': 'POST',
    'url': url,
    'data': JSON.stringify(data),
    'dataType': 'json',
    'success': callback
    });
};

@RequestBody utiliza el encabezado Content-Type para determinar qué formato tienen los datos que se envían desde el cliente en la solicitud. @ResponseBody utiliza el encabezado de aceptación para determinar qué formato debe enviar los datos al cliente en la respuesta. Es por eso que necesitas ambos encabezados.

theon
fuente
1
los encabezados: {...} y JSON.stringify (...) siempre me hacen tropezar.
Tim Perry
1
No tengo idea de por qué esto no está más documentado. Este problema me hizo perder mucho tiempo. ¡Muchas gracias!
Hugo Nava Kopp
Supuse que Spring admite datos de formulario de forma predeterminada, pero no lo hace. Entonces, gracias por la solución (ahora bastante antigua).
RiZKiT
Estaba usando un cartero para hacer una solicitud de venta, solo agregué el tipo de contenido: '' application / json ". Gracias
Janatbek Sharsheyev
21

agregando tipo de contenido en la solicitud como application/jsonresuelto el problema

Uiroshan
fuente
18

Tuve un problema similar, pero descubrí que había olvidado proporcionar un constructor predeterminado para el DTO que se anotó con @RequestBody.

John Shepherd
fuente
1
A mi me pasó lo mismo. Tenía 2 métodos con el mismo nombre y estaba obteniendo 415. ¡Gracias!
Daniel Vilas-Boas
12

Creo que me encontré exactamente con el mismo problema. Después de innumerables horas de lucha con el JSON, el JavaScript y el servidor, encontré al culpable: en mi caso tenía un objeto Date en el DTO, este objeto Date se convirtió en una cadena para que pudiéramos mostrarlo en la vista con el formato: HH: mm.

Cuando se devolvía la información JSON, este objeto de cadena de fecha tenía que convertirse de nuevo en un objeto de fecha completo, por lo tanto, también necesitamos un método para configurarlo en el DTO. El gran PERO es que no puede tener 2 métodos con el mismo nombre (Sobrecarga) en el DTO incluso si tienen un tipo diferente de parámetro (Cadena vs Fecha) porque esto también le dará el error de tipo 415 Medios no compatibles.

Este fue mi método controlador

  @RequestMapping(value = "/alarmdownload/update", produces = "application/json", method = RequestMethod.POST)
  public @ResponseBody
  StatusResponse update(@RequestBody AlarmDownloadDTO[] rowList) {
    System.out.println("hola");
    return new StatusResponse();
  }

Este fue mi ejemplo de DTO (los métodos id get / set y preAlarm get no están incluidos por falta de código):

@JsonIgnoreProperties(ignoreUnknown = true)
public class AlarmDownloadDTO implements Serializable {

  private static final SimpleDateFormat formatHHmm = new SimpleDateFormat("HH:mm");

  private String id;
  private Date preAlarm;

  public void setPreAlarm(Date date) { 
    this.preAlarm == date;
  }
  public void setPreAlarm(String date) {    
    try {
      this.preAlarm = formatHHmm.parse(date);
    } catch (ParseException e) {
      this.preAlarm = null;
    } catch (NullPointerException e){
      this.preAlarm = null;
    }
  }
}

Para que todo funcione, debe eliminar el método con el parámetro Tipo de fecha. Este error es muy frustrante. Espero que esto pueda ahorrarle a alguien horas de depuración.

will824
fuente
Gracias, o puedes cambiar el nombre de uno de los setters. Tenía tanto public void setParameters(List<Parameter> parameters)y public void setParameters(Parameter... parameters)métodos en un grano, cambiando este último a addParametersresolver el problema para mí.
Conor Svensson
¿No es el problema que el cuerpo es this.preAlarm == date en lugar de this.preAlarm = date?
Michael restaura a Monica Cellio el
12

Me enfrenté a un problema similar y así es como lo solucioné,

El problema se debe al proceso de conversión de JSON a Java, uno necesita tener las bibliotecas jackson de tiempo de ejecución adecuadas para que la conversión se realice correctamente.

Agregue los siguientes frascos (a través de la dependencia o descargando y agregando al classpath).

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>

Esto debería solucionar el problema.

Código completo:

function() {
  $.ajax({
    type: "POST",
    url: "saveUserDetails.do",
    data: JSON.stringify({
      name: "Gerry",
      ity: "Sydney"
    }),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    success: function(data) {
      if (data.status == 'OK')
        alert('Person has been added');
      else
        alert('Failed adding person: ' + data.status + ', ' + data.errorMessage);
}

y la firma del controlador se ve así:

@RequestMapping(value = "/saveUserDetails.do", method = RequestMethod.POST)
public @ResponseBody Person addPerson( @RequestBody final  Person person) {

Espero que esto ayude

vinSan
fuente
Solo jackson-databindse requiere.
Alex78191
8

Me enfrenté a este problema cuando integré el arranque de resorte con mvc de resorte. Lo resolví simplemente agregando estas dependencias.

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
profundo
fuente
5

Una pequeña nota al margen: tropecé con este mismo error al desarrollar una aplicación web. El error que encontramos al jugar con el servicio con Firefox Poster fue que tanto los campos como los valores en Json deberían estar rodeados de comillas dobles. Por ejemplo..

[ {"idProductCategory" : "1" , "description":"Descrizione1"}, 
  {"idProductCategory" : "2" , "description":"Descrizione2"} ]

En nuestro caso, llenamos el json a través de javascript, lo que puede ser un poco confuso cuando se trata de tratar con comillas simples / dobles, por lo que he escuchado.

Lo que se ha dicho antes en esta y otras publicaciones, como incluir los encabezados 'Aceptar' y 'Tipo de contenido', también se aplica.

Espero ayudar.

Carlos Romero
fuente
3

Me las arreglé para hacer que funcione. Dime en caso de que me equivoque. Solo utilicé una forma de serializar / deserializar: eliminé todas las anotaciones con respecto a esto ( @JSONSerializey @JSONDeserialize) y registré los serializadores y deserializadores en CustomObjectMapperclase. No encontré un artículo que explicara este comportamiento, pero lo resolví de esta manera. Espero que sea útil.

gc5
fuente
Me pasa lo mismo! ¿Alguna explicación de por qué sucede?
Whimusical
¿Puede explicar su método en detalle?
Dipanshu Verma
1

Yo tuve el mismo problema. Tuve que seguir estos pasos para resolver el problema:

1. Asegúrese de tener las siguientes dependencias:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${jackson-version}</version> // 2.4.3
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson-version}</version> // 2.4.3
    </dependency>

2. Cree el siguiente filtro:

    public class CORSFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {

            String origin = request.getHeader("origin");
            origin = (origin == null || origin.equals("")) ? "null" : origin;
            response.addHeader("Access-Control-Allow-Origin", origin);
            response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, UPDATE, DELETE, OPTIONS");
            response.addHeader("Access-Control-Allow-Credentials", "true");
            response.addHeader("Access-Control-Allow-Headers",
                    "Authorization, origin, content-type, accept, x-requested-with");

            filterChain.doFilter(request, response);
        }
    }

3. Aplique el filtro anterior para las solicitudes en web.xml

    <filter>
        <filter-name>corsFilter</filter-name>
        <filter-class>com.your.package.CORSFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>corsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Espero que esto sea útil para alguien.

Manoj Shrestha
fuente
jackson-corees una dependencia de jackson-databind, por lo que no es necesario agregarlo directamente.
Alex78191
1
¿Por qué es necesario agregar el filtro CORS?
Alex78191
1

Spring boot + spring mvn

con problema

@PostMapping("/addDonation")
public String addDonation(@RequestBody DonatorDTO donatorDTO) {

con solución

@RequestMapping(value = "/addDonation", method = RequestMethod.POST)
@ResponseBody
public GenericResponse addDonation(final DonatorDTO donatorDTO, final HttpServletRequest request){
Shahid Hussain Abbasi
fuente
0

Resolví este problema agregando el enlace de datos jackson-json a mi pom.

<dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.3</version>
</dependency>
Lingasamy Bagavathi
fuente
0

En su clase de modelo, agregue una anotación de propiedad json, también tenga un constructor predeterminado

@JsonProperty("user_name")
private String userName;

@JsonProperty("first_name")
private String firstName;

@JsonProperty("last_name")
private String lastName;
deko
fuente
0

Tuve el mismo problema. agregando

<mvc:annotation-driven />
<mvc:default-servlet-handler />

al spring-xml lo resolvió

OhadR
fuente