Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 624
Marcel Overdijk Avatar asked Dec 13 '13 20:12

Marcel Overdijk


4 Answers

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
}
like image 114
Marcel Overdijk Avatar answered Oct 25 '22 10:10

Marcel Overdijk


@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.

like image 32
englishteeth Avatar answered Oct 25 '22 09:10

englishteeth


@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.

like image 2
Brian Clozel Avatar answered Oct 25 '22 10:10

Brian Clozel


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

like image 1
Fırat KÜÇÜK Avatar answered Oct 25 '22 10:10

Fırat KÜÇÜK