Jersey REST - 发送MultivaluedMap到GET请求导致不支持的媒体类型异常

3

我正在尝试进行参数化的获取(也称为搜索)。我不确定为什么这不起作用。我们使用最新的jersey依赖项(1.14),到目前为止,所有REST接口都运行良好。

简单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的where子句参数。而且这些参数应该是动态的,因为我想创建一个通用的getAll方法,可以在GET调用上接受任何参数映射。

所以我的问题是:如何实现动态查询参数?


你能发布一下HTTP请求吗?我猜你把FormParams和QueryParams混淆了。 - thatidiotguy
3个回答

3

我认为您混淆了实际url中的查询参数:

someurl/resource?param1=value1&param2=value2

表单url编码信息与此格式相同,但参数在正文中。由于GET请求不带正文,因此您要么意味着要执行POST操作,要么意味着要使用查询参数而不是x-www-form-urlencoded。

祝好运!

编辑:

如果您确定要传入哪些参数,则可以使用@QueryParam,否则可以使用@Context UriInfo来确定URL中会有多少/什么查询参数。


是的,我想使用多个查询参数来进行GET操作,以便在GET资源上实现搜索。已更新我的问题。 - Pete

0

我为了解决更复杂的问题而编写了我的解决方案:将GET参数映射到MultivaluedMap以生成对象。这可能对其他人有帮助。

GET查询是与服务器交流的非常简单的方式。即使是没有编程经验的人也可以通过浏览器URL控制来执行简单的API任务。

但是,从"get"查询创建对象并不是推荐的方法之一(其中一个原因是最大限制为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);
}

0

@GET 来自 JAX-RS。GET 请求不应包含正文。请尝试使用 @POST 进行相同的示例。


我认为他想要查询参数,例如?param1=value1&param2=... 这是我的猜测。 - thatidiotguy
嗯..那么如果我想实现搜索,我该怎么办呢?这总是一个POST吗?我如何区分创建调用的POST和获取调用的POST?PS:我认为将搜索参数作为表单参数而不是查询参数甚至可能更好。但这仍然意味着一个POST,对吧? - Pete
你可以使用 @QueryParam@PathParam - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接