1

I'm experimenting with Spring 4 WebSocket STOMP application. Is there a way to explicitly specify content type of the returned message produced by a handler? By default the handler below produces application/json and processed by corresponding message converter.

@Controller
public class ProductController {

    @MessageMapping("/products/{id}")
    public String getProduct(@DestinationVariable int id) {
        return getProductById(id);
    }

}

I'm looking for something like @RequestMapping(produces = "text/xml") in Spring MVC.

UPDATE (reply to Rossen's answer):

Ideally I would like to be able to return both formats depending on what the user asks for. But if I have to choose, I would say XML and almost never JSON (XML is just an example, we use binary format). I went the second way you suggested - configuring custom converters instead of the default ones.

  1. I have implemented custom MessageConverter extending AbstractMessageConverter. In the constructor I've registered appropriate supported MimeType.
  2. Then I've registered my custom converter by overriding WebSocketMessageBrokerConfigurer's configureMessageConverters, and return false from the method to not add default converters.
  3. As soon as my controller returns the value I get NPE in SendToMethodReturnValueHandler's postProcessMessage. This happens because CompositeMessageConverter contains only a single converter - my custom one. But my converter fails AbstractMessageConverter's supportsMimeType check and AbstractMessageConverter's toMessage returns null. This null causes the exception in postProcessMessage.

As the workaround I can register additional default MimeType application/json for my custom converter. But it looks like too dirty to me.

Community
  • 1
  • 1
Anton Moiseev
  • 2,834
  • 4
  • 24
  • 30

2 Answers2

0

There is nothing like the produces condition. The only way to do this right now is to inject SimpMessagingTemplate and then use the convertAndSend method variation that takes a MessagePostProcessor. It could be made simpler. What's your use case? Are you using JSON primarily and need to use XML in a few places? Or do you need to use XML primarily? If it is the latter you can simply configure the converters to use instead of the default ones.

Rossen Stoyanchev
  • 4,910
  • 23
  • 26
  • Please, see my reply above. – Anton Moiseev Jan 29 '14 at 11:07
  • Sounds like a possible bug. Could you open a ticket in JIRA with stack trace details? Why does supportsMimeType return false by the way given that you have configured supported mime types and it returns true if no content-type header is found. Is there no match for it then, and if so should there be another converter registered perhaps? – Rossen Stoyanchev Jan 29 '14 at 16:08
  • Will file an issue. `supportsMimeType` returns `false` because `getMimeType(headers)` returns `"application/json"` even if `headers == null`. But it doesn't match any of my converter's registered MimeTypes. Not sure why `getMimeType(headers)` returns JSON for `null`, will be able to check it later today. – Anton Moiseev Jan 29 '14 at 16:33
  • I guess I see why it happens: `AbstractMessageBrokerConfiguration` finds `com.fasterxml.jackson.databind.ObjectMapper` on the classpath and sets `jackson2Present` to `true`. It makes `getContentTypeResolver()` method to set `"application/json"` as default MimeType. In turn `DefaultContentTypeResolver#resolve` returns this default MimeType if `headers==null` – Anton Moiseev Jan 29 '14 at 20:27
  • Thanks I've updated the issue. Probably a better solution before it's fixed is to set the default content type to something other than application/json (or not set it at all). See my comments under the filed issue. Lets continue there. – Rossen Stoyanchev Jan 29 '14 at 21:41
0

I see that you can change the headers changing the Message converter. For example:

SockJsClient sockJsClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
..
stompClient.setMessageConverter(new StringMessageConverter());
or
stompClient.setMessageConverter(new SimpleMessageConverter());

when you use StringMessageConverter then the Java client add an content-type header.

GenericMessage [payload=byte[33], headers={simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={destination=[/app/chatchannel], content-type=[text/plain;charset=UTF-8], content-length=[33]}, simpSessionAttributes={ip=/127.0.0.1:59629}, simpHeartbeat=[J@147a2bf, contentType=text/plain;charset=UTF-8, lookupDestination=/chatchannel, simpSessionId=79431feb8b5f4a9497492ccc64f8965f, simpDestination=/app/chatchannel}]

But if you use SimpleMessageConverter, the content-type header is not add.

this is a clue about what you want to do?

Check this question, I put some code that could help you:

How configure the Spring Sockjs Java Client message converters

Community
  • 1
  • 1
Sergio
  • 441
  • 9
  • 22