Как передать массив как параметр строки GET-запроса в Java (Spring)?
Цель - сделать api более RESTful. Для получения ресурсов должен использоваться метод GET, но для получения списка ресурсов в контроллер должен передаваться фильтр такого вида:
@Data
public class Filter {
private int limit;
private List<Sort> sort;
public Filter(int limit, List<Sort> sort) {
this.limit = limit;
this.sort = sort;
}
}
@Data
public class Sort {
private String name;
private String direction;
}
Из-за наличия списка List<Sort> внутри фильтра, возникают проблемы с получением его в контроллере.
Тщательно изучив мануал и вопрос на тему передачи объектов в строке GET-запроса, мне так и не удалось добиться успеха. По-прежнему неясно, в каком виде надо передать массив с клиента, и что сделать на сервере, чтобы он его увидел как список (происходят разного вида ошибки при маппинге фильтра). Использую такую стратегию (для получения всего, кроме списка):
Контроллер:
@RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public List<SomeDTO> getList(@RequestParam("filter") String filterJson) {
ObjectMapper mapper = new ObjectMapper();
Filter filter = null;
try {
filter = mapper.readValue(filterJson, Filter.class);
} catch (IOException e) {
e.printStackTrace();
}
return getSomeDtos(filter);
}
Запрос с клиента:
http://localhost:3000/some-api?filter=%7B%22limit%22:10,%22sort%22:null%7D
Уважаемые знатоки, вопрос - в каком виде надо передать List<> в фильтре в строке GET-запроса?
Ответы (1 шт):
Решение вашей задачи для контроллера:
@Slf4j
@RestController
@RequiredArgsConstructor
public class ExampleController {
private final ObjectMapper mapper;
@GetMapping
@SneakyThrows
public Filter getSorted(@RequestParam( name = "filter") String filter) {
log.info("Input string is {}", filter);
Filter mapped = mapper.readValue(filter, Filter.class);
log.info("Filter dto is {}", filter);
return mapped;
}
}
Чтобы это решение заработало, запрос нужно полностью приводить в url encoded вид. Например, запрос для объекта вида
{
"limit": 12,
"sort": [
{
"name": "name",
"direction": "direction"
}
]
}
Будет выглядеть так:
http(s)://{host}:{port}/?filter=%7B%22limit%22%3A12%2C%20%22sort%22%3A%20%5B%7B%22name%22%3A%22name%22%2C%20%22direction%22%3A%20%22direction%22%7D%5D%7D
Здесь - можно перевести в url-encoded вид и обратно
Подробнее про url encoding можно прочесть хотя бы на вики
И немного бубнежа от меня:
Может быть вам вместо объекта использовать набор параметров для фильтрации? Параметров limit и direction, как правило, достаточно для большинства задач
Добавьте валидацию в dto запроса, если решите его оставить (может ли лимит быть null? или direction? - от ответа на эти вопросы сильно зависит дизайн и будущее всего апи - и клиентов тоже)
В контроллере не должно быть никакой логики (даже если вы в качестве примера привели инициализацию ObjectMapper и маппинг строки в объект)
Выносите в конфигурацию всего приложения инициализацию ObjectMapper, делайте метод его создания бином и затем инжекьте его во все сервисы.
Не забудьте где-нибудь прописать ExceptionHandler для ошибок маппинга.
Удачи!