发送 List/Map 作为 POST 参数球衣
我想将 HashMap 对象作为 POST 变量发送到 ReST 资源.我使用 Form
类来发送对象.客户端代码:
I want to send a HashMap object to a ReST resource as a POST variable. I used the Form
class to send the object. The client code:
public static void main(String[] args)
{
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(getBaseURI());
HashMap<String, String> hashmap = new HashMap<String, String>();
hashmap.put("Key1", "value1");
hashmap.put("Key2", "value2");
Form form = new Form();
form.add("data1", hashmap);
ClientResponse response = service.path("hello2").path("hello2").type(MediaType.APPLICATION_FORM_URLENCODED).post(ClientResponse.class, form);
@SuppressWarnings("unchecked")
MultivaluedMap<String, String> map = response.getEntity(MultivaluedMap.class);
System.out.println(map.get("response").get(0));
System.out.println(map.get("response2"));
}
REST资源如下:
@Path("/hello2")
public class FormResource
{
@Context
UriInfo uriInfo;
@Context
Request request;
public FormResource()
{
}
public FormResource(UriInfo uriInfo, Request request)// , String data1)
{
this.uriInfo = uriInfo;
this.request = request;
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public MultivaluedMap<String, String> newProj(@FormParam("data1") HashMap<String, String> postVarMap, @Context HttpServletResponse response)
{
System.out.println(postVarMap);
MultivaluedMap<String, String> hash = new MultivaluedMapImpl();
hash.add("response", "response1");
hash.add("response", "response2");
hash.add("response2", "fbshabfsdb");
URI uri = uriInfo.getAbsolutePathBuilder().build();
Response.created(uri).build();
return hash;
}
}
我在 Tomcat 6.0 日志中得到以下异常
I get the following exception in the Tomcat 6.0 log
Dec 7, 2011 3:38:39 PM org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/JerCDemo] has started
Dec 7, 2011 3:38:40 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
com.reflexis.nbe.jersey
Dec 7, 2011 3:38:40 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
class com.reflexis.nbe.jersey.FormResource
Dec 7, 2011 3:38:40 PM com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Dec 7, 2011 3:38:40 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.9.1 09/14/2011 02:05 PM'
Dec 7, 2011 3:38:40 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for method public javax.ws.rs.core.MultivaluedMap com.reflexis.nbe.jersey.FormResource.newProj(java.util.HashMap,javax.servlet.http.HttpServletResponse) at parameter at index 0
SEVERE: Missing dependency for method public javax.ws.rs.core.MultivaluedMap com.reflexis.nbe.jersey.FormResource.newProj(java.util.HashMap,javax.servlet.http.HttpServletResponse) at parameter at index 0
SEVERE: Method, public javax.ws.rs.core.MultivaluedMap com.reflexis.nbe.jersey.FormResource.newProj(java.util.HashMap,javax.servlet.http.HttpServletResponse), annotated with POST of resource, class com.reflexis.nbe.jersey.FormResource, is not recognized as valid resource method.
Dec 7, 2011 3:38:40 PM org.apache.catalina.core.ApplicationContext log
SEVERE: StandardWrapper.Throwable
com.sun.jersey.spi.inject.Errors$ErrorMessagesException
at com.sun.jersey.spi.inject.Errors.processErrorMessages(Errors.java:170)
at com.sun.jersey.spi.inject.Errors.postProcess(Errors.java:136)
at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:199)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:771)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:766)
at com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:488)
at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:318)
at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:609)
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:210)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4420)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4733)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3460)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:426)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1357)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1649)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1638)
at java.lang.Thread.run(Thread.java:619)
Dec 7, 2011 3:38:40 PM org.apache.catalina.core.StandardContext loadOnStartup
SEVERE: Servlet /JerCDemo threw load() exception
com.sun.jersey.spi.inject.Errors$ErrorMessagesException
at com.sun.jersey.spi.inject.Errors.processErrorMessages(Errors.java:170)
at com.sun.jersey.spi.inject.Errors.postProcess(Errors.java:136)
at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:199)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:771)
at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:766)
at com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:488)
at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:318)
at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:609)
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:210)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4420)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4733)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3460)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:426)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1357)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1649)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1658)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1638)
at java.lang.Thread.run(Thread.java:619)
我写的客户端代码正确吗?还有其他方法可以将 HashMap 对象作为 POST 变量发送吗?
Is the client code written by me correct? Is there any other way to send a HashMap object as POST variable?
推荐答案
只是把事情弄清楚一点.MultivaluedMap
用于获取表单参数的一般映射,例如通过 POST HTTP 请求提交给您的服务的参数.它应该是这样使用的:
Just to clear things a bit. The MultivaluedMap<String, String>
is meant to be used for obtaining general map of form parameters e.g. parameters submitted to your service via POST HTTP request. It is supposed to be used like this:
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}
但是,当您的客户端应用程序需要为您的 REST 服务提供某种数据时(在您的情况下,我认为 HashMap
包含很多重要信息)它会首先将其序列化为 XML,然后将其发送到服务,该服务将反序列化并使用它.不幸的是,Jersey 无法自动编组/取消编组 HashMap
,因此如果您只是在 newProj
方法中提供了 HashMap
参数,您将得到一个例外.
However, when your client application needs to provide your REST service with some sort of data (in your case a HashMap
containing I suppose a lot of important information) it would Serialize it to XML first, then send it to the service which would then deserialize and use it. Unfortunately, Jersey is not able to automatically marshal/unmmarshal HashMap
s so if you just provided HashMap
parameter in your newProj
method, you would get an exception.
那么如何将 HashMap 发送到您的服务?好吧,关键是 JAXB @XmlRootElement
和自定义 XmlAdapter
:-)
So how to send a HashMap to your service? Well, the key is the JAXB @XmlRootElement
and a custom XmlAdapter
:-)
首先,您需要为地图编写自己的包装器.包装器将使用 @XmlRootElement
First you need to write your own wrapper for the map. The wrapper will be annotated with @XmlRootElement
@XmlRootElement
public class MyHashMapObject<T, U> {
private Map<T, U> mapProperty;
public MyHashMapObject() {
mapProperty = new HashMap<T, U>();
}
@XmlJavaTypeAdapter(MapAdapter.class) // NOTE: Our custom XmlAdaper
public Map<T, U> getMapProperty() {
return mapProperty;
}
public void setMapProperty(Map<T, U> map) {
this.mapProperty = map;
}
}
然后您需要定义启用 JAXB"的地图元素:
Then you need to define your "JAXB enabled" map elements:
public class MapElement {
@XmlElement
public String key;
@XmlElement
public String value;
private MapElement() {
}
public MapElement(String key, String value) {
this.key = key;
this.value = value;
}
}
最后定义您的自定义 XmlAdapter:
And in the end define your custom XmlAdapter:
public class MapAdapter extends XmlAdapter<MapElement[], Map<String, String>> {
public MapElement[] marshal(Map<String, String> arg0) throws Exception {
MapElement[] mapElements = new MapElement[arg0.size()];
int i = 0;
for (Map.Entry<String, String> entry : arg0.entrySet())
mapElements[i++] = new MapElement(entry.getKey(), entry.getValue());
return mapElements;
}
public Map<String, String> unmarshal(MapElement[] arg0) throws Exception {
Map<String, String> r = new HashMap<String, String>();
for (MapElement mapelement : arg0)
r.put(mapelement.key, mapelement.value);
return r;
}
}
一旦你准备好了所有这些(它必须被你的服务和客户端使用,所以把它放到某个分片 jar 中),你可以像这样定义你的服务:
Once you have all of this in place (it must be used by your service and the client so put it to some shard jar), you can define your service like this:
@Path("/hello")
public class FormResource
{
//@GET
@POST
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
public MyHashMapObject<String, String> post(
MyHashMapObject<String, String> anotherMap) {
anotherMap.getMapProperty().put("e", "10");
anotherMap.getMapProperty().put("f", "11");
anotherMap.getMapProperty().put("g", "12");
return anotherMap;
}
}
你们现在都准备好了.你的客户应该是这样的:
You're all set to go now. Your client should go like this:
public class Test {
public static void main(String[] args) {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(getBaseURI());
// Now do the MAP stuff
MyHashMapObject<String, String> map = new MyHashMapObject<String, String>();
map.getMapProperty().put("a", "1");
map.getMapProperty().put("b", "2");
ClientResponse response = service.path("rest").path("hello2")
.type(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML)
.post(ClientResponse.class, map);
Map<String, String> myMap = response.getEntity(MyHashMapObject.class).getMapProperty();
for(Map.Entry<String, String> entry : myMap.entrySet())
System.out.format("Key: %s, Value: %s
", entry.getKey(), entry.getValue());
}
private static URI getBaseURI() {
return UriBuilder.fromUri(
"http://localhost:8080/org.nowaq.jersey.first").build();
}
}
现在您可以轻松地将 HashMap<String, String>
传递给 REST 服务.您还可以使实现更加通用.希望这会有所帮助.
Now you can easily pass your HashMap<String, String>
to your REST service. You can also make the implementation a bit more generic. Hope this helps.
相关文章