Джерси REST - отправка MultivaluedMap в GET приводит к неподдерживаемому типу носителя

Я пытаюсь выполнить параметризованное получение (также известное как поиск). Я не уверен, почему это не работает. Мы используем последние версии трикотажных зависимостей (1.14), и до сих пор все интерфейсы REST работали нормально. Простой ОТДЫХ:

@Path("/some/path")
@Component
public class SomeRest {

    @GET
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces({ MediaType.APPLICATION_JSON })
    public Response getAll(MultivaluedMap<String, String> queryParams) {

        // extract params, do search, return response as JSON
        // (Using ObjectMapper.writeValue() to convert object to JSON String)

    }
}

Я думаю, что работа метода не важна, поскольку в ошибке ясно указано, что он не может войти в него в первую очередь из-за какого-то неподдерживаемого типа носителя (когда я ввожу свой путь в браузере):

HTTP Status 415 - Unsupported Media Type

Выход:

SEVERE: A message body reader for Java class javax.ws.rs.core.MultivaluedMap, and Java type javax.ws.rs.core.MultivaluedMap<java.lang.String, java.lang.String>, and MIME media type application/octet-stream was not found.
The registered message body readers compatible with the MIME media type are:
....

У кого-нибудь есть идея? Спасибо!

изменить:

Моя цель — реализовать поиск, используя параметры запроса с ресурсом GET. Так что, когда я вызываю /some/path, я получаю все результаты, но если я вызываю some/path?limit=10&offset=10, я получаю второй набор из 10. Я мог бы решить эту конкретную проблему с параметрами запроса, но я также хочу иметь возможность определить, где параметры предложения, такие как name=foo, в та же карта параметров. И эти параметры должны быть динамическими, так как я хочу создать общий метод getAll, который может принимать любую карту параметров при вызове GET.

Итак, вопрос, который я думаю, заключается в следующем: как мне реализовать динамические параметры запроса?


person Pete    schedule 20.09.2012    source источник
comment
Не могли бы вы опубликовать HTTP-запрос? Я предполагаю, что вы путаете FormParams с QueryParams   -  person thatidiotguy    schedule 20.09.2012


Ответы (3)


Я думаю, вы путаете параметры запроса, которые находятся в фактическом URL-адресе:

какой-то URL/ресурс?param1=value1¶m2=value2

закодированная информация URL формы имеет тот же формат, но параметры находятся в теле. Поскольку запрос GET не принимает тело, вы либо имеете в виду, что хотите выполнить POST, либо хотите использовать параметры запроса вместо x-www-form-urlencoded.

Удачи!

РЕДАКТИРОВАТЬ:

Вы хотите использовать либо @QueryParam, если вы точно знаете, какие параметры входят, либо @Context UriInfo, если вы не уверены, сколько/каких параметров запроса ожидается в URL-адресе.

person thatidiotguy    schedule 20.09.2012
comment
Да, я хочу использовать несколько параметров запроса для GET, чтобы я мог реализовать поиск в ресурсе GET.. обновил свой вопрос.. - person Pete; 20.09.2012

@GET взят из JAX-RS. GET-запрос не должен содержать тела. Попробуйте тот же пример с @POST

person Chris    schedule 20.09.2012
comment
Я думаю, что ему нужны параметры запроса, например, что-то вроде param1=value1¶m2=... я думаю. - person thatidiotguy; 20.09.2012
comment
Хм.. Так что мне делать, если я хочу реализовать поиск? Это всегда пост? Как я могу различать POST вызова create и POST вызова get? PS: я думаю, что было бы даже лучше, чтобы параметры поиска были параметрами формы, а не параметрами запроса. Но это все равно будет означать POST, верно? - person Pete; 20.09.2012
comment
Вы можете использовать @QueryParam или @PathParam - person Chris; 20.09.2012

Я пишу свое решение для более сложной проблемы: параметры map GET выбрасывают MultivaluedMap в Object. Может быть, это будет полезно для других.

Запрос GET — это очень простой способ общения с вашим сервером. Даже непрограммист с контролем URL-адреса браузера может выполнять простые задачи API.

Но создавать объект из запроса "получить" не рекомендуется (одна из причин - максимальное ограничение в 4000 символов).

Запрос GET по умолчанию имеет тип MediaType.APPLICATION_OCTET_STREAM.

// GOAL: create simple in use API (for non-programmers) to call rest service to test functional. User can use any business object field.
//      So need map GET query params to server business object.

// Create Provider and save it in jersey rest folder (for auto detect).
// 
// PROBLEM: "get" query hasn't body and query params don't available get from readFrom prodiver method
//          So use uriInfo for getting query params 

@Provider
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
public class GETQueryMapperProvider<T> implements MessageBodyReader<T> {
    //[!!!] MAIN PART: save the uri
    @Context
    private UriInfo uriInfo;

    @Override
    public boolean isReadable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public T readFrom(Class<T> tClass, Type type, Annotation[] annotations, MediaType mediaType,
                      MultivaluedMap<String, String> stringStringMultivaluedMap,
                      InputStream inputStream) throws IOException, WebApplicationException {
        MultivaluedMap queryParams = uriInfo.getQueryParameters();
        //create from Map Object
        return ServiceUtils.convertMapToObject(queryParams, tClass);
    }
}

И решение второй части:

public static <T> T convertMapToObject(Map map, Class<T> objectClass) {
    try {
        //[!!!] MAIN PART - MultivaluedMap for each value create list. 
        //But we list only for fields with collection type, for other need take first value. 
        map = fixCollectionsValue(map, objectClass);

        ObjectMapper ob = new ObjectMapper();
        StringWriter json = new StringWriter();
        ob.writeValue(json, map);

        return ob.readValue(json.toString(), objectClass);
    } catch (IOException e) {
        log.error(e.getMessage(), e);
    }
    return null;
}
protected static Map fixCollectionsValue(Map map, Class objectClass) {
    Map newMap = new HashMap(map);
    for(Field field : objectClass.getDeclaredFields()) {
        if(!Modifier.isStatic(field.getModifiers())
                &&!Modifier.isFinal(field.getModifiers())) {
            String name = field.getName();
            Object value = map.get(name);
            Object newValue = value;
            if(value != null) {
                if(isCollectionClass(field.getType())) {
                    if(!isCollectionClass(value.getClass())) {
                        newValue = new ArrayList();
                        ((ArrayList) newValue).add(value);
                        newMap.put(name, newValue);
                    }
                } else {
                    if(isCollectionClass(value.getClass())) {
                        //take first value to our object
                        newValue = ((Collection) newValue).iterator().next();
                        newMap.put(name, newValue);
                    }
                }
            }
        }
    }
    return newMap;
}
protected static boolean isCollectionClass(Class clazz) {
    return clazz.isArray() ||
           Collection.class.isAssignableFrom(clazz);
}
person Kinjeiro    schedule 13.02.2013