Часть URL нулевой длины в контроллере Spring. RequestMapping PathVariable разрывает разрешение. - PullRequest
0 голосов
/ 07 января 2019

Я пытаюсь сделать REST API приложения более RESTful, и мне кажется, что я не использую Spring RequestMappings так, как задумано.

У меня есть одна конечная точка GET для чтения:

@RequestMapping(value = "thing/{thingName}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String getThing(
        @PathVariable(value = "thingName", required = false)
                String thingName,
        @RequestParam(value = "findByComponent", required = false)
                String findByComponentQuery,
        @AuthenticationPrincipal User user) {
...

Чтобы быть более спокойным, я хочу, чтобы эта конечная точка выполняла оба действия:

  1. GET / thing / {thingName} возвращает одну вещь с таким именем
  2. GET / thing или / thing / с параметрами запроса возвращает списки вещей

Таким образом, в моем контроллере я могу проверить, является ли {thingName} нулевым или нулевой длиной, и если да, проверить параметры запроса на наличие известных имен запросов.

Однако вызов этого с помощью http://localhost:8080/thing/?findByComponent=component123 возвращает 404 из Spring с этим журналированием:

12:45:18.485 PageNotFound : No mapping found for HTTP request with URI [/thing/] in DispatcherServlet with name 'dispatcher' : WARN : XNIO-1 task-3 : org.springframework.web.servlet.DispatcherServlet  

1 Ответ

0 голосов
/ 07 января 2019

Spring не позволяет отображать переменные пути ({thingName}) в пустые String. На практике это означает, что URL /thing/?findByComponent=component123 не не отображается на thing/{thingName} с пустым {thingName}, а скорее ожидает, что будет какое-то сопоставление для thing. Поскольку нет конечной точки, которая сопоставляется с путем thing (без переменной пути), возвращается ошибка 404.

Чтобы решить эту проблему, вы можете разбить эту единственную конечную точку на две отдельные конечные точки:

@RequestMapping(value = "thing/{thingName}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String getThing(
        @PathVariable(value = "thingName") String thingName,
        @AuthenticationPrincipal User user) {
    // ...
}

@RequestMapping(value = "thing",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String getThings(,
        @RequestParam(value = "findByComponent", required = false) String findByComponentQuery,
        @AuthenticationPrincipal User user) {
    // ...
}

Для получения дополнительной информации см. В Spring 3.0 можно ли сделать необязательную переменную пути? .

Флаг required=false допускает два типа запросов:

  1. /thing
  2. /thing/<some_value>

Строго говоря, включение косой черты в конце URL (т. Е. /thing/) означает, что было принято решение включить значение для переменной пути, но ничего не было предоставлено. В контексте API REST /thing и /thing/ - это две разные конечные точки, где последняя означает, что ожидалось значение после завершающего слеша.

Обходной путь для того, чтобы не создавать три отдельных сопоставления запросов (по одному для каждого вышеописанного случая), состоит в том, чтобы установить значение @RequestMapping для контроллера на базовый путь, а затем иметь сопоставление запроса "" и "/{thingName} для две конечные точки:

@RestController
@RequestMapping("thing")
public class ThingController {

    @RequestMapping(value = "/{thingName}",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String getThing(
            @PathVariable(value = "thingName") String thingName) {
        return "foo";
    }

    @RequestMapping(value = "",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String getThings(
            @RequestParam(value = "findByComponent", required = false) String findByComponentQuery) {
        return "bar";
    }
}

В этом случае произойдут следующие сопоставления:

  1. /thing: getThings
  2. /thing/: getThings
  3. /thing/foo: getThing

Примером этого обходного пути, включая тестовые примеры, может быть , найденный здесь .

...