Java Jersey REST-发送多值映射以获取不支持的媒体类型

Java Jersey REST-发送多值映射以获取不支持的媒体类型,java,http,rest,jersey,mime-types,Java,Http,Rest,Jersey,Mime Types,我正在尝试进行参数化get(又名搜索)。我不知道这为什么不起作用。我们使用最新的jersey依赖项(1.14),到目前为止,所有REST接口都工作正常。 简单休息: @Path("/some/path") @Component public class SomeRest { @GET @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces({ MediaType.APPLICATION_JSON })

我正在尝试进行参数化get(又名搜索)。我不知道这为什么不起作用。我们使用最新的jersey依赖项(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)

    }
}
输出为:

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:
....
SEVERE:找不到Java类javax.ws.rs.core.MultivaluedMap、Java类型javax.ws.rs.core.MultivaluedMap和MIME媒体类型应用程序/八位字节流的消息正文读取器。
与MIME媒体类型兼容的已注册邮件正文读取器包括:
....
有人有主意吗? 谢谢

编辑:

我的目标是通过使用GET资源的查询参数来实现搜索。因此,当我调用
/some/path
时,我会得到所有结果,但如果我调用
some/path?limit=10&offset=10,我会得到第二组10。我可以用查询参数解决这个特定问题,但我也希望能够在同一个参数映射中定义where子句参数,比如name=foo。这些参数应该是动态的,因为我想创建一个通用的getAll方法,它可以在GET调用中获取任何参数映射


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

@GET
来自。GET请求不应包含正文。使用
@POST

尝试相同的示例,我认为您混淆了实际url中的查询参数:

someurl/resource?param1=value1¶m2=value2

表单url编码信息的格式相同,但参数位于正文中。由于GET请求不包含正文,您的意思可能是希望执行POST,也可能是希望使用查询参数而不是x-www-form-urlencoded

祝你好运

编辑:


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

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

获取查询是与服务器聊天的一种非常简单的方式。即使是具有浏览器url控件的非程序员也可以执行简单的API任务

但不推荐使用“get”查询创建对象的方式(原因之一是最大限制为4000个符号)

默认情况下,GET查询具有MediaType.APPLICATION\u OCTET\u流

// 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);
    }
}
//目标:创建简单的使用中API(针对非程序员)来调用rest服务来测试功能。用户可以使用任何业务对象字段。
//所以需要将GET查询参数映射到服务器业务对象。
//创建提供程序并将其保存在jersey rest文件夹中(用于自动检测)。
// 
//问题:“get”查询没有正文,并且查询参数不可用从readFrom ProDriver方法获取
//因此,请使用uriInfo获取查询参数
@提供者
@消耗(MediaType.APPLICATION\u八位字节\u流)
公共类GETQueryMapperProvider实现MessageBodyReader{
//[!!!]主要部分:保存uri
@上下文
私人UriInfo UriInfo;
@凌驾
公共布尔值可读取(类aClass,类型类型,注释[]注释,MediaType MediaType){
返回true;
}
@凌驾
public T readFrom(类tClass、类型Type、注释[]注释、MediaType、MediaType、,
多值映射字符串多值映射,
InputStream(InputStream)引发IOException、WebApplicationException{
多值Map queryParams=uriInfo.getQueryParameters();
//从地图对象创建
返回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);
}
publicstatict convertMapToObject(映射映射,类objectClass){
试一试{
//[!!!]主要部分-每个值创建列表的多值映射。
//但我们只列出集合类型的字段,其他需要取第一个值。
map=fixCollectionsValue(map,objectClass);
ObjectMapper ob=新的ObjectMapper();
StringWriter json=新的StringWriter();
ob.writeValue(json,map);
返回ob.readValue(json.toString(),objectClass);
}捕获(IOE异常){
log.error(e.getMessage(),e);
}
返回null;
}
受保护的静态映射fixCollectionsValue(映射映射,类objectClass){
Map newMap=新HashMap(Map);
for(字段:objectClass.getDeclaredFields()){
如果(!Modifier.isStatic(field.getModifiers())
&&!Modifier.isFinal(field.getModifiers())){
字符串名称=field.getName();
对象值=map.get(名称);
对象newValue=value;
if(值!=null){
if(isCollectionClass(field.getType())){
如果(!isCollectionClass(value.getClass())){
newValue=newArrayList();
((ArrayList)newValue).add(value);
newMap.put(名称,newValue);
}
}否则{
if(isCollectionClass(value.getClass())){
//将第一个值带到我们的对象
newValue=((集合)newValue).iterator().next();
newMap.put(名称,newValue);
}
}
}
}
}
返回newMap;
}
受保护的静态布尔isCollectionClass(clazz类){
返回clazz.isArray()||
Collection.class.isAssignableFrom(clazz);
}

您可以发布HTTP请求吗?我猜你把FormParams和QueryParams搞混了我认为他想要像这样的查询参数?param1=value1¶m2=。。。这是我的猜测。。那么,如果我想实现搜索,我应该怎么做?那总是一个帖子吗?如何区分create调用的POST和get调用的POST?PS:我认为最好将搜索参数作为表单参数,而不是查询参数。。但这仍然意味着P
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);
}