4

I have this Spring Java Configuration with several custom HttpMessageConverters:

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorParameter(true).
            ignoreAcceptHeader(false).
            useJaf(true).
            defaultContentType(MediaType.TEXT_HTML).
            mediaType("html", MediaType.TEXT__HTML).
            mediaType("rdf", MediaTypes.RDFXML);
}

If I query this setup with Jena I get an error:

The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers

Jena sends a request with this Accept header:

Accept: text/turtle,application/n-triples;q=0.9,application/rdf+xml;q=0.8,application/xml;q=0.7,/;q=0.5

To my understanding, application/rdf+xml, should be returned by the config above. This works perfectly as long as the type with the highest value is configured. Why doesn't Spring fall back to the 0.8-valued application/rdf+xml, because text/turtle and application/n-triples are not available?

Is there an option to activate that?

Andreas
  • 303
  • 1
  • 17
  • 2
    You might be interested in [Serving RDF representation of Java model with Spring MVC?](http://stackoverflow.com/q/17313801/1281433). (I'm not sure whether it answers your question or not, but it's certainly related.) – Joshua Taylor Apr 23 '14 at 11:53

2 Answers2

1

ContentNegotiationConfigurer.mediaType(String,MediaType) applies to the request parsing and defines a mapping from the extension to the media type - if the request does not define explicit media type in the form of the Accept header but the request path ends with the specified extension that the specified media type is assumed.

Basically, ContentNegotiationConfigurer enriches (or modifies) the request data, but does not select the actual response type.

What you need is a controller that can produce either multiple media types or a multiple controllers (or multiple methods in the same controller) that produce different media types, application/rdf+xml being one of them. Spring will select that controller automatically if the application/rdf+xml is the highest common media type between the (possible enriched or modified) Accept header in the request and what your controller can actually produce.

Oleg Estekhin
  • 8,063
  • 5
  • 49
  • 52
  • Thanks for your explanation. I already have a Controller producing application/rdf+xml and it works fine. My problem is that the controller is not selected for the Accept Header shown above. It even works if I change rdf+xml to 0.9 and turtle etc. to 0.8. But I would like to have a clean solution that works with Jena's default requests as this should be available publicly. – Andreas Apr 23 '14 at 10:58
  • Do you mean that you have both "turtle" and "rdf" controllers, but want to force-select "rdf" controller when `Accept` header prefers "turtle"? – Oleg Estekhin Apr 23 '14 at 11:02
  • No I only have an rdf controller producing rdf/xml. Of course I could easily produce turtle, too. But I would like to make Spring map that request to rdf instead of building a controller(-method/HttpMessageConverter or Mapping) for each RDF serialization. – Andreas Apr 23 '14 at 11:07
  • Now as a workaround I implemented RDF/Turtle as an additional output format in my RDFXML-HttpMessageConverter. Now Jena can retrieve RDF from the application - although I do not consider that solution as very elegant. – Andreas Apr 23 '14 at 14:47
1

I achieve this either through defining different MVC handlers, or by reflecting on the content-type and then deciding what to return.

Defining Different Handlers

If you specify a @RequestMapping that produces some value, then that will be the type on your Content-Type header regardless of the automatic negotiation that lead your request there. You can 'force' requests to those handlers by being the only ones available to answer. I use this to return a more-specific type, but I suspect that you can also use it in order to return a more generic one as well.

@RequestMapping(value="/sparql/service", produces={"application/rdf+xml;charset=utf-8", MediaType.ALL_VALUE})
public @ResponseBody String serviceDescriptionAsRdfXml()
{
    return null; // something here
}

@RequestMapping( value="/sparql/service", produces={"text/turtle;charset=utf-8"} )
public @ResponseBody String serviceDescriptionAsTurtle( final HttpServletRequest request )
{
    return null; // something here
}

Reflecting on the Content-Type

To reflect on the type coming in, and produce something more generic, then you can actually retrieve a list of MediaType objects as part of your request, then use a ResponseEntity to define what the Content-Type will be for your result. This requires a little more thought.

@RequestMapping(value="/sparql/query", method=RequestMethod.GET)
public ResponseEntity<String> queryViaGet(@RequestHeader(value="Accept") final List<MediaType> contentTypes)
{
    MediaType.sortBySpecificityAndQuality(contentTypes);

    // Do some stuff to select your content type and generate your response
    final String results = null;
    final MediaType desiredType = null;

    // Create your REST response
    final HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(desiredType);
    return new ResponseEntity<String>(results, responseHeaders, HttpStatus.OK);
}
Rob Hall
  • 2,693
  • 16
  • 22
  • Thanks, I think this comes closest to what I would like to achieve. If Spring does not show the expected behaviour out of the box I can implement the decision on Content-Type myself as shown in your second example. – Andreas Apr 24 '14 at 16:38