反序列化列表 <Map<String, String>> 球衣 1 中的 QueryParam
Deserializing List<Map<String, String>> QueryParam in jersey 1
我正在尝试在 dropwizard 资源中实现一个方法,该方法将为来自 JS 前端(使用数据表)的调用提供服务。
请求的查询参数如下所示:
columns[0][data]=0&columns[0][name]=&columns[0][searchable]=false&columns[0][orderable]=false&columns[0][search][value]=&columns[0][search][regex]=false
columns[1][data]=iata&columns[1][name]=iata&columns[1][searchable]=true&columns[1][orderable]=true&columns[1][search][value]=&columns[1][search][regex]=false
请求来自使用 DataTables 实现的 JS 前端,并使用服务器端处理。有关数据表如何在此处发送请求的信息:
https://datatables.net/manual/server-side
我在为上述查询参数定义数据类型时遇到问题。使用spring数据,我们可以将其定义为:
List<Map<String, String>> columns
它可以包装在一个用 ModelAttribute 注释的对象中,它会很好地反序列化。
在我的应用程序中,我使用的是旧版本的 dropwizard,它依赖于 jersey 1.19。
我尝试将其注释为 QueryParam,但应用程序在启动时失败。
方法:
@Path("/mappings")
@GET
@Timed
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getMappings(@QueryParam("columns") List<Map<String, String>> columns) {
// processing here.
}
当我这样做时,我得到:
ERROR [2016-11-07 14:16:13,061] com.sun.jersey.spi.inject.Errors: The
following errors and warnings have been detected with resource and/or
provider classes: SEVERE: Missing dependency for method public
javax.ws.rs.core.Response
com.ean.gds.proxy.ams.application.resource.gui.IataMappingGuiResource.getMappings(java.util.List)
at parameter at index 0 WARN [2016-11-07 14:16:13,070] /: unavailable
我的问题是:除了为它编写自定义反序列化器之外,我还有其他选择吗?
注意:如果我使用@Context 抓取请求,我可以看到 decodedQueryParams 是一个 MultivaluedMap,它将像 "columns[0][data]" 这样的字符串键映射到字符串值列表,它总是有一个元素,即是值。
更新:
经过一番挖掘,我发现了以下 JAX-RS 规范(第 3.2 节),它解释了为什么我的方法一开始就无效:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List, Set, or SortedSet where T satisfies 2 or 3 above.
来源:Handling Multiple Query Parameters in Jersey
所以我尝试只使用一个列表。这不会在启动时使应用程序崩溃,但是当请求进入时,它会反序列化为一个空列表。所以问题仍然是哪种方法是正确的。
事实上,您使用的结构与我们为完善 Rest Web 服务而映射的所有常见结构截然不同。此外,由于这种结构合规性问题,一旦我们没有传输基于对象的参数,尝试使用 JSON 到 marshal/unmarshal 值将不适合。
但是,我们有几个选择 "work this situation around"。让我们看看:
采用 @QueryParam
策略是不可能的,主要原因有两个:
- 如您所见,除
Lists
、Sets
等外,Collections
的使用存在一些限制;
- 此注释通过其(它们的)名称映射一个(或一个或多个)参数列表,因此您需要每个参数(由
&
分隔)具有相同的名称。当我们考虑提交(通过 GET
)复选框值列表的表单时,它会更容易:一旦它们都具有相同的 name
属性,它们将在 [=21] 中发送=]格式。
因此,为了满足此要求,您必须做出如下操作:
@GET
public Response getMappings(@QueryParam("columns") List<String> columns) {
return Response.status(200).entity(columns).build();
}
// URL to be called (with same param names):
// /mappings?columns=columns[1][name]=0&columns=columns[0][searchable]=false
// Result: [columns[1][name]=0, columns[0][searchable]=false]
您也可以尝试为参数注释创建自定义Java类型,就像您see here一样。这将避免编码问题,但在我的测试中它不适用于括号问题。 :(
您可以使用正则表达式和 @Path
注释来定义 String
参数将接受的内容。不幸的是,您的 URL 将由无效字符组成(如方括号 []
),这意味着您的服务器将 return 变成 500 error
。
另一种选择是,如果您 "replace" 此字符用于有效字符(例如下划线字符):
/mappings/columns_1_=0&columns_1__name_=
这样解决方案就可以放心应用了:
@GET
@Path("/{columns: .*}")
public Response getMappings(@PathParam("columns") String columns) {
return Response.status(200).entity(columns).build();
}
// Result: columns_1_=0&columns_1__name_=
一个更好的方法是通过 UriInfo
对象,正如您可能已经尝试过的那样。这更简单,因为不需要更改 URL 和参数。该对象有一个 getQueryParameters()
,return 是一个 Map
,参数值:
@GET
public Response getMappings(@Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
// In case you want to get the whole generated string
String query = uriInfo.getRequestUri().getQuery();
String output = "QueryParams: " + queryParams
+ "<br> Keys: " + queryParams.keySet()
+ "<br> Values: " + queryParams.values()
+ "<br> Query: " + query;
return Response.status(200).entity(output).build();
}
// URL: /mappings?columns[1][name]=0&columns[0][searchable]=false
/* Result:
* QueryParams: {columns[0][searchable]=[false], columns[1][name]=[0]}
* Keys: [columns[0][searchable], columns[1][name]]
* Values: [[false], [0]]
* Query: columns[1][name]=0&columns[0][searchable]=false
*/
但是,您必须知道,如果您遵循这种方法(使用 Map
),一旦结构不支持,就不能有重复的键。这就是为什么我包含 getQuery()
选项,您可以在其中获得整个字符串。
- 最后一种可能性是创建
InjectableProvider
,但我看不出 getQuery()
策略有多少差异(因为您可以拆分它并创建自己的值映射)。
我正在尝试在 dropwizard 资源中实现一个方法,该方法将为来自 JS 前端(使用数据表)的调用提供服务。
请求的查询参数如下所示:
columns[0][data]=0&columns[0][name]=&columns[0][searchable]=false&columns[0][orderable]=false&columns[0][search][value]=&columns[0][search][regex]=false
columns[1][data]=iata&columns[1][name]=iata&columns[1][searchable]=true&columns[1][orderable]=true&columns[1][search][value]=&columns[1][search][regex]=false
请求来自使用 DataTables 实现的 JS 前端,并使用服务器端处理。有关数据表如何在此处发送请求的信息:
https://datatables.net/manual/server-side
我在为上述查询参数定义数据类型时遇到问题。使用spring数据,我们可以将其定义为:
List<Map<String, String>> columns
它可以包装在一个用 ModelAttribute 注释的对象中,它会很好地反序列化。
在我的应用程序中,我使用的是旧版本的 dropwizard,它依赖于 jersey 1.19。 我尝试将其注释为 QueryParam,但应用程序在启动时失败。
方法:
@Path("/mappings")
@GET
@Timed
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getMappings(@QueryParam("columns") List<Map<String, String>> columns) {
// processing here.
}
当我这样做时,我得到:
ERROR [2016-11-07 14:16:13,061] com.sun.jersey.spi.inject.Errors: The following errors and warnings have been detected with resource and/or provider classes: SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.ean.gds.proxy.ams.application.resource.gui.IataMappingGuiResource.getMappings(java.util.List) at parameter at index 0 WARN [2016-11-07 14:16:13,070] /: unavailable
我的问题是:除了为它编写自定义反序列化器之外,我还有其他选择吗?
注意:如果我使用@Context 抓取请求,我可以看到 decodedQueryParams 是一个 MultivaluedMap,它将像 "columns[0][data]" 这样的字符串键映射到字符串值列表,它总是有一个元素,即是值。
更新: 经过一番挖掘,我发现了以下 JAX-RS 规范(第 3.2 节),它解释了为什么我的方法一开始就无效:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List, Set, or SortedSet where T satisfies 2 or 3 above.
来源:Handling Multiple Query Parameters in Jersey
所以我尝试只使用一个列表。这不会在启动时使应用程序崩溃,但是当请求进入时,它会反序列化为一个空列表。所以问题仍然是哪种方法是正确的。
事实上,您使用的结构与我们为完善 Rest Web 服务而映射的所有常见结构截然不同。此外,由于这种结构合规性问题,一旦我们没有传输基于对象的参数,尝试使用 JSON 到 marshal/unmarshal 值将不适合。
但是,我们有几个选择 "work this situation around"。让我们看看:
采用
@QueryParam
策略是不可能的,主要原因有两个:- 如您所见,除
Lists
、Sets
等外,Collections
的使用存在一些限制; - 此注释通过其(它们的)名称映射一个(或一个或多个)参数列表,因此您需要每个参数(由
&
分隔)具有相同的名称。当我们考虑提交(通过GET
)复选框值列表的表单时,它会更容易:一旦它们都具有相同的name
属性,它们将在 [=21] 中发送=]格式。
因此,为了满足此要求,您必须做出如下操作:
@GET public Response getMappings(@QueryParam("columns") List<String> columns) { return Response.status(200).entity(columns).build(); } // URL to be called (with same param names): // /mappings?columns=columns[1][name]=0&columns=columns[0][searchable]=false // Result: [columns[1][name]=0, columns[0][searchable]=false]
您也可以尝试为参数注释创建自定义Java类型,就像您see here一样。这将避免编码问题,但在我的测试中它不适用于括号问题。 :(
- 如您所见,除
您可以使用正则表达式和
@Path
注释来定义String
参数将接受的内容。不幸的是,您的 URL 将由无效字符组成(如方括号[]
),这意味着您的服务器将 return 变成500 error
。另一种选择是,如果您 "replace" 此字符用于有效字符(例如下划线字符):
/mappings/columns_1_=0&columns_1__name_=
这样解决方案就可以放心应用了:
@GET @Path("/{columns: .*}") public Response getMappings(@PathParam("columns") String columns) { return Response.status(200).entity(columns).build(); } // Result: columns_1_=0&columns_1__name_=
一个更好的方法是通过
UriInfo
对象,正如您可能已经尝试过的那样。这更简单,因为不需要更改 URL 和参数。该对象有一个getQueryParameters()
,return 是一个Map
,参数值:@GET public Response getMappings(@Context UriInfo uriInfo) { MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(); // In case you want to get the whole generated string String query = uriInfo.getRequestUri().getQuery(); String output = "QueryParams: " + queryParams + "<br> Keys: " + queryParams.keySet() + "<br> Values: " + queryParams.values() + "<br> Query: " + query; return Response.status(200).entity(output).build(); } // URL: /mappings?columns[1][name]=0&columns[0][searchable]=false /* Result: * QueryParams: {columns[0][searchable]=[false], columns[1][name]=[0]} * Keys: [columns[0][searchable], columns[1][name]] * Values: [[false], [0]] * Query: columns[1][name]=0&columns[0][searchable]=false */
但是,您必须知道,如果您遵循这种方法(使用
Map
),一旦结构不支持,就不能有重复的键。这就是为什么我包含getQuery()
选项,您可以在其中获得整个字符串。- 最后一种可能性是创建
InjectableProvider
,但我看不出getQuery()
策略有多少差异(因为您可以拆分它并创建自己的值映射)。