简体   繁体   中英

How to set priority to Spring-Boot request mapping methods

I have a Spring-Boot (v2.0.2) application with a RestController with 2 methods which only differ by the Accept header. A simplified version of the code is this:

@RestController
@RequestMapping("/myapp")
public class FooController {

    @GetMapping(value = "/foo/{id}", headers = "Accept=application/json", produces = "application/json;charset=UTF-8")
    public ResponseEntity<String> fooJson(@PathVariable id) {
        return foo(pageId, true);
    }

    @GetMapping(value = "/foo/{id}", headers = "Accept=application/ld+json", produces = "application/ld+json;charset=UTF-8")
    public ResponseEntity<String> fooJsonLd(@PathVariable id) {
        return foo(pageId, false);
    }

    private ResponseEntity<String> foo(String id, boolean isJson) {
        String result = generateBasicResponse(id);
        if (isJson) {
            return result
        }
        return addJsonLdContext(result);
    }

This works fine. If we sent a request with accept header such as application/json;q=0.5,application/ld+json;q=0.6 for example it will return a json-ld response as it should.

My problem is that if we sent a request with no accept header, an empty accept header or a wildcard */* then it will by default always return a json response whereas I want the default response to be json-ld.

I've tried various things to make the json-ld request mapping take priority over the json one:

  • Reversing the order in which the mappings are declared.
  • Adding an @Order annotation to both methods (with value 1 for json-ld and value 2 for the json method)
  • Creating different classes and putting the @Order annotation at class-level
  • Adding Accept=*/* as a second accept header to the json-ld mapping does work in giving it preference but has the unwanted side-affect that all accept headers are accepted, even unsupported types as application/xml for example.

The only solution I can think of is creating one request-mapping method that accepts both headers and then processing the accept header ourselves, but I don't really like that solution. Is there a better, easier way to give preference to json-ld?

After some more searching this question on configuring custom MediaTypes pointed me in the right direction. The WebMvcConfigurerAdapter (Spring 3 or 4) or WebMvcConfigurer (Spring 5) allows you to set a default mediatype like this:

public static final String MEDIA_TYPE_JSONLD  = "application/ld+json";

@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.valueOf(MEDIA_TYPE_JSONLD));
    }
}

This works great for requests with no or an empty accept header, as well as accept: */* . However when you combine an unsupported type with the wildcard, for example accept: */*,text/plain it will return json instead of json-ld!? I suspect this is a bug in Spring.

I solved the issue using the consumes in the @GetMapping annotation. According to the official documentation :

The format is a single media type or a sequence of media types, with a request only mapped if the Content-Type matches one of these media types. Expressions can be negated by using the "!"operator, as in "!text/plain", which matches all requests with a Content-Type other than "text/plain".

In the solution bellow, note that I've added the consumes array to the normal json request mapping, making the client only be able to use the json endpoint if it have the correct Content-Type . Other requests go to the ld+json endpoint.

@GetMapping(value = "/json", headers = "Accept=application/json", consumes = {"application/json"})
@ResponseBody
public String testJson() {
    return "{\"type\":\"json\"}";
}

@GetMapping(value = "/json", headers = "Accept=application/ld+json")
@ResponseBody
public String textLDJson() {
    return "{\"type\":\"ld\"}";
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM