Marcel Overdijk
Marcel Overdijk

Reputation: 11467

Spring @RestController not returning plain text response

I'm experimenting with the new Spring 4.0 @RestController to return a simple text response from a controller:

@RestController
@RequestMapping(value = "/heartbeat")
public class HeartbeatController {

    private static final Logger logger = LoggerFactory.getLogger(HeartbeatController.class);

    @RequestMapping
    public String heartbeat() {
        logger.info("Received heartbeat!");
        return "I'm Alive!";
    }

    @RequestMapping(value = "/test", produces = MediaType.TEXT_PLAIN_VALUE)
    public String heartbeat2() {
        logger.info("Received heartbeat!");
        return "I'm Alive!";
    }
}

When I access /heartbeat then I get back:

"I'm Alive!"

The result includes the double quotes, what I did not expect.

When I access /heartbeat/test then I get a empty response back, but I expect the I'm Alive! text.

UPDATE

curl -i http://myserver.com/rest/heartbeat

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Server: Development/1.0 Date: Tue, 17 Dec 2013 18:59:08 GMT Cache-Control: no-cache Expires: Fri, 01 Jan 1990 00:00:00 GMT Content-Length: 12

"I'm Alive!"

curl -i -H "Accept: application/json" http://myserver.com/rest/heartbeat HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Server: Development/1.0 Date: Tue, 17 Dec 2013 19:01:12 GMT Cache-Control: no-cache Expires: Fri, 01 Jan 1990 00:00:00 GMT Content-Length: 12

"I'm Alive!"

curl -i http://myserver.com/rest/heartbeat/test

HTTP/1.1 406 Not Acceptable Server: Development/1.0 Date: Tue, 17 Dec 2013 19:00:13 GMT Cache-Control: no-cache Expires: Fri, 01 Jan 1990 00:00:00 GMT Content-Length: 0

curl -i -H "Accept: text/plain" http://myserver.com/rest/heartbeat/test

HTTP/1.1 406 Not Acceptable Server: Development/1.0 Date: Tue, 17 Dec 2013 19:02:06 GMT Cache-Control: no-cache Expires: Fri, 01 Jan 1990 00:00:00 GMT Content-Length: 0

Upvotes: 12

Views: 25300

Answers (5)

cn123h
cn123h

Reputation: 2332

This works for me:

  1. add this in maven

    com.fasterxml.jackson.core jackson-databind 2.4.4

  2. make sure <mvc:annotation-driven /> is configured in spring

Upvotes: 0

This solution worked for me. Please check the followings.

  1. Ensure your DTO is serializable and has serializable fields, getters and setters
  2. Check the dependencies for jackson. You should have
    • com.fasterxml.jackson.core:jackson-core:2.4.1
    • com.fasterxml.jackson.core:jackson-databind:2.4.1
    • com.fasterxml.jackson.core:jackson-annotations:2.4.1
    • com.fasterxml.jackson.datatype:jackson-datatype-joda:2.4.1
    • com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.1
  3. Fix the RequesMapping Annotation:

    @RequestMapping(value = "/test", consumes = "*/*")

  4. Check you have <mvc:annotation-driven /> directive

Upvotes: 1

Marcel Overdijk
Marcel Overdijk

Reputation: 11467

I found out I was missing the StringHttpMessageConverter in my WebConfig's configureMessageConverters. I was configuring the message converters to control the Jackson ObjectMapper.

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
    mappingJackson2HttpMessageConverter.setPrettyPrint(SystemProperty.environment.value() == Development);
    mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper());
    converters.add(mappingJackson2HttpMessageConverter);
    converters.add(new StringHttpMessageConverter()); // THIS WAS MISSING
}

Upvotes: 21

Brian Clozel
Brian Clozel

Reputation: 59056

@RestController combines @Controller and @ResponseBody on your Controller class, as stated in the documentation.

When you annotate a method/a controller with @ResponseBody, Spring assists you with content negotiation, using the Accept HTTP request header and the produces attribute on your annotation.

In your case:

  • You get an application/json response for your heartbeat action, because your HTTP client probably asks for that Content-Type and Spring did the content negotiation.
  • You get a HTTP 406 for your hearbeat2 action, because content negotiation failed. You specified text/plain as a produces Content-Type on your Controller, whereas your HTTP client probably lists only application/json in its Accept request header.

Update: I've used the exact same curl requests but don't get the same results. Maybe a Filter or a HTTP proxy-cache is modifying HTTP headers?

Default format is text/plain:

➜ curl -v http://localhost:8080/heartbeat
> GET /heartbeat HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: text/plain;charset=ISO-8859-1
< Content-Length: 13
< Date: Wed, 18 Dec 2013 13:34:12 GMT
<
Hello, World!%

And with a produces text/plain attribute:

➜ curl -H "Accept: text/plain" -v http://localhost:8080/heartbeat/test
> GET /heartbeat/test HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: text/plain
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: text/plain
< Content-Length: 13
< Date: Wed, 18 Dec 2013 13:39:07 GMT
<
Hello, World!%

This sample application does the same thing and get good results.

Upvotes: 2

englishteeth
englishteeth

Reputation: 228

@RestController is a convenience annotation that means you no longer need to specify @ResponseBody annotation on your methods.

But it will mean that your response type is being defaulted to JSON and therefore wrapped in quotes to be properly formed.

Upvotes: 4

Related Questions