Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicitly set response Content-Type in @ResponseBody [SPR-6702] #11368

Closed
spring-issuemaster opened this issue Jan 14, 2010 · 19 comments
Closed

Explicitly set response Content-Type in @ResponseBody [SPR-6702] #11368

spring-issuemaster opened this issue Jan 14, 2010 · 19 comments

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Jan 14, 2010

Arjen Poutsma opened SPR-6702 and commented

It would be useful to override the default content-type written by the HttpMessageConverters in the @ResponseBody annotation. Something like:

@ResponseBody("foo/bar")
public String myHandlerMethod() {
  return "myFooBar";
}

Issue Links:

  • #11225 @ResponseBody overwrites ContentType
  • #12012 Add equivalent of JAX-RS @Produces to Spring MVC ("is superseded by")

10 votes, 15 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 20, 2010

Keith Donald commented

I'm wondering why would this be useful? I thought it was the client's responsibility to send along the contentType they can accept. Then the message converter can just respond to that.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 24, 2010

Ted Bergeron commented

I'm a bit confused by this issue also. Is this to do something like force json when the browser requests xml, or doesn't bother to set an accept header?

What seems to be needed is something for the @ResponseBody to indicate MessageConverter compatibility. "myFooBar" would convert fine into json or xml, but if the client sets the accept header to image/*, what is BufferedImageHttpMessageConverter going to do with this string?

Rather than generate some unpredictable exception, wouldn't some sort of content type unsupported response be better? In fact, that check could come first allowing the method invocation to be skipped entirely.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 24, 2010

Keith Donald commented

I believe what this JIRA is after is incoporating the semantics of the JAX-RS @Produces annotation into the Spring MVC model.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 6, 2010

Keith Donald commented

Arjen,
Can this one be closed?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 10, 2010

Paul Bakker commented

I believe it's actually useful in some rare situations. I was just trying write a controller to insert testdata. When it's done it should just output "done" to the browser instead of rendering a view. The browser will try to render HTML anyway because you can't explicitly set the content-type. There are easy work arounds, but it would be nice if it's supported.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 17, 2010

Nate Rock commented

We are currently integrating Flex Builder 4 + Spring 3.x + Roo 1.1.0.RELEASE. In flex builder, we can't explicitly set the accepts header using their HTTP data services GUI interface. You can do an HTTP GET to the spring REST URL but without the correct accepts header you get a redirect + HTML response. To make this work, we have to create different url mappings that return the same JSON, but setting the @ResponseBody annotation returns the header "application/xml", which is even more confusing if your application actually uses these headers: "they asked for XML, lets give them JSON!".

This is really an issue with the Adobe tool, but the bottom line is that there are interfaces that developers will be using where they don't have control over which Accepts headers are sent, but they do know what type of data they will be returning (JSON, XML, HTML, binary etc.). Allowing them the flexibility to be able to override the default values produced by spring is something that might pop up more and more as people start using it with legacy interfaces.

I think that Keith is correct in mentioning the @Produces JAX-RS annotation because it fulfills this need.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 20, 2011

Joey Novak commented

We have a similar problem. Even just outputting html from an @ResponseBody annotated method will sometimes be rendered as text in ie8.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 25, 2011

Alan Casagrande commented

I have the same problem of Joe Novak.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 26, 2011

omar elmandour commented

??I believe it's actually useful in some rare situations. I was just trying write a controller to insert testdata. When it's done it should just output "done" to the browser instead of rendering a view.??

You are talking about a specific httpstatus in response & without response.
I think there is already a solution for that, please correct me if i'm wrong :

@ResponseStatus(value=HttpStatus.OK)
@(...)
public void inserStuff(...) {
...
}
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Apr 2, 2011

adam commented

Being able to change the Content-Type is necessary at the moment when considering file upload quirks:

"The file upload plugin makes use of iframes for browsers like Microsoft Internet Explorer and Opera, which do not yet support XMLHTTPRequest uploads. They will only register a load event if the Content-type of the response is set to text/plain or text/html, not if it is set to application/json." https://github.com/blueimp/jQuery-File-Upload/wiki/Setup

I guess this is another example of fixing somebody else's problem, but seems there are enough cases which warrant it now, and probably in the future.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 6, 2011

Arjen Poutsma commented

Fixed as part of #12012

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 11, 2012

Cédric Laruelle commented

I do not agree it was fixed as part of SPR 7353.

Indeed, let's consider the following example :

@RequestMapping(value= = "foo/bar", produces = "text/html")
@ResponseBody
public String myHandlerMethod() {
return "myFooBar";
}

Let's now consider we have a single MessageConverter that accepts String and can produce "text/plain" and "text/html", and that its default media type is "text/plain".

Now, if we have a browser that sends a request that specifies only "*/*" as Accept header, the result is that we will have a "text/plain" content type and not a "text/html" content type, as the produces value is only use to filter the request (process or send 415) and not to determine the content type that should be output (which is only resolved from Accept headers and MessageConverter supported media types/default media type)

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 11, 2012

Cédric Laruelle commented

For those who would be stuck on this, we have a "dirty" workaround that defines text/html as the default media type for MappingJacksonHttpMessageConverter.
It however requires to define the AnnotationMethodHandlerAdapter manually

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
	<property name="messageConverters">
		<list>
			<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
				<property name="supportedMediaTypes">
					<list>
						<value>text/html;charset=UTF-8</value>
						<value>application/json;charset=UTF-8</value>
					</list>
				</property>
			</bean>
		</list>
	</property>
</bean>
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 12, 2012

Arjen Poutsma commented

Rossen, can you take a look at this?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 12, 2012

Rossen Stoyanchev commented

@Cédric, your example would actually work:

@RequestMapping(value = "/foo/bar", produces = "text/html")
@ResponseBody
public String handle() { return "whatever"; }

A single MessageConverter is configured that accepts String and can produce "text/plain" and "text/html".

If a browser sends Accept "*/*", the result would be "text/html". That's because a produces is used not only to match the method to the request but it is also the top preference for the actual content type to write out. As opposed to the default media type of a converter or the order the converters.

Hence I do think #12012 addresses the motivations listed for this issue.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 16, 2012

Cédric Laruelle commented

Hi Rossen.
Thanks for taking time to look into this.

In my example, I have a single message converter, so I'm not using the default Spring MessageConverter that accepts String.
Actually, I think you make a mistake saying

it is also the top preference for the actual content type to write out . Could you please let me know where in the code produces is used to determine the output content type ? As far as I can see, in AnnotationMethodHandlerAdapter,

List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (getMessageConverters() != null) {
	for (MediaType acceptedMediaType : acceptedMediaTypes) {
		for (HttpMessageConverter messageConverter : getMessageConverters()) {
			if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
				messageConverter.write(returnValue, acceptedMediaType, outputMessage);
				if (logger.isDebugEnabled()) {
					MediaType contentType = outputMessage.getHeaders().getContentType();
					if (contentType == null) {
						contentType = acceptedMediaType;
					}
					logger.debug("Written [" + returnValue + "] as \"" + contentType +
							"\" using [" + messageConverter + "]");
				}
				this.responseArgumentUsed = true;
				return;
			}
		}
	}
	for (HttpMessageConverter messageConverter : messageConverters) {
		allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
	}
}

starting line 1019

So as far as I can see, the output content type only depends on :

  • The return type which defines the converter to use
  • The accept headers
  • The supported media types for the converter
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 16, 2012

Rossen Stoyanchev commented

Ah, I see now where the difference comes from. The produces/consumes @RequestMapping conditions (along with all other Spring 3.1 improvements related to annotates controllers) are only implemented in the new @MVC support classes. You'll find pointers to the details in the What's New in 3.1 section of the reference docs.

In short the AnnotationMethodHandlerAdapter doesn't contain this functionality. The RequestMappingHandlerAdapter does and the code you wanted to see is in AbstractMessageConverterMethodProcessor#writeWithMessageConverters.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 17, 2012

Cédric Laruelle commented

Thanks for that answer.
You're right ! I'm sorry for my mistake, for some reason, I did not make the connection between the new @MVC support classes and the produces/consumes.
Thanks again for your time !

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 17, 2012

Rossen Stoyanchev commented

No problem!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.