Как установить приоритет для методов отображения запросов Spring-Boot - PullRequest
1 голос
/ 15 апреля 2019

У меня есть приложение Spring-Boot (v2.0.2) с RestController с 2 методами, которые отличаются только заголовком Accept. Упрощенная версия кода выглядит так:

@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);
    }

Это отлично работает. Если мы отправили запрос с заголовком accept, например application/json;q=0.5,application/ld+json;q=0.6, он вернет ответ json-ld, как и должно быть.

Моя проблема в том, что если мы отправили запрос без заголовка принятия, пустого заголовка принятия или подстановочного знака */*, то он по умолчанию всегда будет возвращать ответ json, тогда как я хочу, чтобы ответом по умолчанию был json-ld.

Я пробовал разные вещи, чтобы отображение запросов json-ld имело приоритет над json:

  • Изменение порядка объявления сопоставлений.
  • Добавление аннотации @Order к обоим методам (со значением 1 для json-ld и значением 2 для метода json)
  • Создание разных классов и размещение аннотации @Order на уровне класса
  • Добавление Accept=*/* в качестве второго принимающего заголовка к отображению json-ld действительно дает ему предпочтение, но имеет нежелательный побочный эффект - все принимаемые заголовки принимаются, даже неподдерживаемые типы, например application/xml.

Единственное решение, которое я могу придумать, - это создать один метод отображения запросов, который принимает оба заголовка, а затем самостоятельно обрабатывать заголовок accept, но мне это решение не очень нравится. Есть ли лучший, более простой способ отдать предпочтение json-ld?

Ответы [ 2 ]

1 голос
/ 18 апреля 2019

После дополнительного поиска этот вопрос о настройке пользовательских типов MediaTypes указал мне правильное направление. WebMvcConfigurerAdapter (Spring 3 или 4) или WebMvcConfigurer (Spring 5) позволяет вам задать медиатип по умолчанию, например:

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));
    }
}

Это прекрасно работает для запросов без или с пустым заголовком принятия, а также accept: */*. Однако когда вы объединяете неподдерживаемый тип с подстановочным знаком, например accept: */*,text/plain, он вернет json вместо json-ld !? Я подозреваю, что это ошибка весной.

0 голосов
/ 15 апреля 2019

Я решил проблему, используя consumes в аннотации @GetMapping. Согласно официальной документации :

Формат представляет собой отдельный тип носителя или последовательность типов носителя, причем запрос отображается только в том случае, если Content-Type соответствует одному из этих носителей.типы.Выражения могут быть отменены с помощью "!"оператор, как в «! text / plain», который сопоставляет все запросы с Content-Type, отличным от «text / plain».

В приведенном ниже решении обратите внимание, что я добавил потребляющиемассив для обычного отображения запроса json, что позволяет клиенту использовать конечную точку json, только если она имеет правильный Content-Type.Другие запросы отправляются в конечную точку ld+json.

@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\"}";
}
...