sonoerin
sonoerin

Reputation: 5185

Spring-MVC 3.1.x REST JSON 415 Unsupported Media Type

First time post here...but I cannot find the solution in the many tutorials, SO posts, and documentation.

Creating a Spring MVC REST webapp using:

   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>3.1.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>3.1.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-core-asl</artifactId>
    <version>1.9.8</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.8</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.2.6</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.7</version>
</dependency>

My Controller is as

@Controller
public class HomeController {
    @RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
@ResponseBody
public String login(@RequestBody Login login) {
        if (login != null) {
            logger.debug(login.toString());
        } else {
            logger.error("Null Login JSON received!");
        }
        return new Login();
    }

    private class Login {
        String username;
        String password;
         ..}

And I am trying to configure it all programatically via:

@Configuration
@EnableTransactionManagement
@EnableWebMvc
@ImportResource("classpath:applicationContext.xml")
@PropertySource("classpath:application.properties")
public class ApplicationContext {

@Bean
public MappingJacksonJsonView mappingJacksonJsonView() {
    MappingJacksonJsonView mappingJacksonJsonView = new MappingJacksonJsonView();
    return mappingJacksonJsonView;
}

@Bean
public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
    Map<String, String> mediaTypes = new HashMap<String, String>();
    mediaTypes.put("json", "application/json");

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
    contentNegotiatingViewResolver.setMediaTypes(mediaTypes);
    return contentNegotiatingViewResolver;
}

@Bean
public org.codehaus.jackson.map.ObjectMapper objectMapper() {
    org.codehaus.jackson.map.ObjectMapper objectMapper = new org.codehaus.jackson.map.ObjectMapper();
    return objectMapper;
}

}

But when I send the request via AJAX or Firefox RESTClient:

{"username": "aname", "password" : "ahack"}

I get the message:

"NetworkError: 415 Unsupported Media Type - http://localhost:8080/edm/login"

I would really appreciate any pointers, this one has me stumped.

Upvotes: 1

Views: 13776

Answers (3)

xianlinbox
xianlinbox

Reputation: 979

For this kind of error "NetworkError: 415 Unsupported Media Type",

you just need to investigate the following things: In your Web service:

  • what media type you web service produces?
  • what media type you web service consumes?

In the client:

  • what is the accept media type of the response?
  • what is the content-type of your request?

the mapping as follow:

  • web service produces <---> client accept media type
  • web service consumes <---> client content-type of request

For your problem , I think root cause should be you forgot to set the content type or accept type in AJAX or Firefox RESTClient request.

Upvotes: 0

Francesco
Francesco

Reputation: 21

If your configuration is ok, the problem could raise when Spring tries to serialize your parameter. If you have a problem in serialization (for example infinite recursion due to bidirectional properties in oneToMany relations), you get 415 error. Before consuming the web service, try to simulate what Spring does, for example:

try {
    ObjectMapper mapper=new ObjectMapper();
    //serialize
    String json=mapper.writeValueAsString(login);
    //deserialize
    Login login2 = mapper.readValue(json, Login.class);
} catch (Exception e) {
    e.printStackTrace();
}

//consume the ws
restTemplate.postForObject(URI,login,String.class);

if something goes wrong, you get an exception showing you the cause.

Upvotes: 1

Biju Kunjummen
Biju Kunjummen

Reputation: 49935

You don't need to register AnnotationHandlerMethodAdapter, since you are using Spring 3.1, @EnableMVC alone should be sufficient(which actually registers a RequestMappingHandlerAdapter) - same with objectMapper, contentNegotiatingViewResolver, mappingJacksonJsonView.

You can also annotate your method this way:

@RequestMapping(value = "/login", method = RequestMethod.POST, consumes="application/json", produces="application/json")

Here is a one page test using your same annotations - this test is using Spring-test-mvc:

package org.bk.webtest;

import static org.springframework.test.web.server.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.server.setup.MockMvcBuilders.*;

import org.junit.Test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

public class WebContextConfigurationTest {

    @Test
    public void testWebFlow() throws Exception {
        annotationConfigSetup(WebContextConfigurationTest.TestConfiguration.class)
            .build()
            .perform(post("/login").contentType(MediaType.APPLICATION_JSON).body("{\"username\":\"user\",\"password\":\"password\"}".getBytes()))
                .andExpect(status().isOk())
                .andExpect(content().string("{\"username\":\"user\",\"password\":\"password\"}"));
    }

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages="org.bk.webtest")
    public static class TestConfiguration{

    }
}

@Controller
class JsonController{
    @RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
    @ResponseBody
    public Login login(@RequestBody Login login) {
        System.out.println(login);
        return login;
    }
}

class Login{
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

}

Upvotes: 2

Related Questions