【问题标题】:Jersey REST - sending MultivaluedMap to GET causes Unsupported Media TypeJersey REST - 将 MultivaluedMap 发送到 GET 导致不受支持的媒体类型
【发布时间】:2012-09-20 13:33:01
【问题描述】:

我正在尝试进行参数化获取(也称为搜索)。我不确定为什么这不起作用。我们使用最新的 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&amp;offset=10,我会得到第二组 10 个。我可以使用查询参数解决该特定问题,但我也希望能够定义 where 子句参数,例如name=foo 在同一个参数映射中。这些参数应该是动态的,因为我想创建一个通用的 getAll 方法,该方法可以在 GET 调用中获取任何参数映射。

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

【问题讨论】:

  • 你能发布 HTTP 请求吗?我的猜测是您将 FormParams 与 QueryParams 混淆

标签: java http rest jersey mime-types


【解决方案1】:

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

someurl/resource?param1=value1&param2=value2

form url 编码信息格式相同,但参数在正文中。由于 GET 请求不接受正文,因此您的意思要么是要执行 POST,要么是要使用查询参数而不是 x-www-form-urlencoded。

祝你好运!

编辑:

如果您确切知道要输入哪些参数,您想使用 @QueryParam,如果您不确定 URL 中需要多少/什么查询参数,则使用 @Context UriInfo。

【讨论】:

  • 是的,我想对 GET 使用多个查询参数,以便我可以在 GET 资源上实现搜索。更新了我的问题。
【解决方案2】:

@GET 来自JAX-RS。 GET 请求不应包含正文。用@POST尝试同样的例子

【讨论】:

  • 我认为他想要查询参数之类的东西?param1=value1&param2=... 是我的猜测。
  • Hm.. 如果我想实现搜索,我应该怎么做?总是这样的帖子吗?如何区分 create 调用的 POST 和 get 调用的 POST? PS:我认为将搜索参数作为表单参数而不是查询参数可能会更好。但这仍然意味着 POST,对吧?
  • 你可以使用@QueryParam@PathParam
【解决方案3】:

我为更复杂的问题编写了我的解决方案:map GET params throw MultivaluedMap to Object。可能对其他人有帮助。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2015-06-02
    • 1970-01-01
    相关资源
    最近更新 更多