我一直在谷歌搜索我的屁股试图找出如何做到这一点:我有一个 Jersey REST 服务。调用 REST 服务的请求包含一个 JSON 对象。我的问题是,从 Jersey POST 方法实现中,我怎样才能访问 HTTP 请求正文中的 JSON?
任何提示,技巧,示例代码的指针将不胜感激。
谢谢...
——史蒂夫
正如已经建议的那样,将@Consumes
Content-Type 更改为text/plain
将起作用,但从 REST API 的角度来看,它似乎并不正确。
想象一下,您的客户必须将 JSON 发布到您的 API,但需要将 Content-Type 标头指定为text/plain
. 在我看来它并不干净。简单来说,如果您的 API 接受 JSON,则请求标头应指定Content-Type: application/json
.
为了接受 JSON 但将其序列化为String
对象而不是 POJO,您可以实现自定义MessageBodyReader。这样做同样容易,而且您不必在 API 规范上妥协。
值得阅读MessageBodyReader的文档,以便您确切了解它的工作原理。我是这样做的:
步骤 1. 实现自定义MessageBodyReader
@Provider
@Consumes("application/json")
public class CustomJsonReader<T> implements MessageBodyReader<T> {
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations,MediaType mediaType) {
return true;
}
@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
/* Copy the input stream to String. Do this however you like.
* Here I use Commons IOUtils.
*/
StringWriter writer = new StringWriter();
IOUtils.copy(entityStream, writer, "UTF-8");
String json = writer.toString();
/* if the input stream is expected to be deserialized into a String,
* then just cast it
*/
if (String.class == genericType)
return type.cast(json);
/* Otherwise, deserialize the JSON into a POJO type.
* You can use whatever JSON library you want, here's
* a simply example using GSON.
*/
return new Gson().fromJson(json, genericType);
}
}
上面的基本概念是检查输入流是否期望转换为String
(由 指定Type genericType
)。如果是这样,那么只需将 JSON 转换为指定的type
(将是 a String
)。如果预期的类型是某种 POJO,则使用 JSON 库(例如 Jackson 或 GSON)将其反序列化为 POJO。
步骤 2. 绑定您的MessageBodyReader
这取决于您使用的框架。我发现 Guice 和 Jersey 配合得很好。这是我在 Guice 中绑定MessageBodyReader的方法:
在我的JerseyServletModule 中,我像这样绑定读者——
bind(CustomJsonReader.class).in(Scopes.SINGLETON);
以上CustomJsonReader
将 JSON 有效负载反序列化为 POJO,如果您只是想要原始 JSON,则String
对象。
这样做的好处是它会接受Content-Type: application/json
. 换句话说,您的请求处理程序可以设置为使用 JSON,这似乎是正确的:
@POST
@Path("/stuff")
@Consumes("application/json")
public void doStuff(String json) {
/* do stuff with the json string */
return;
}
Jersey 支持使用 Jettison 类型 JSONObject 和 JSONArray 对解析的 JSONObject 进行低级访问。
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.8</version>
</dependency>
例如:
{
"A": "a value",
"B": "another value"
}
@POST
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
public void doStuff(JSONObject json) {
/* extract data values using DOM-like API */
String a = json.optString("A");
Strong b = json.optString("B");
return;
}
有关更多示例,请参阅Jersey 文档。
我不确定您将如何获取 JSON 字符串本身,但您当然可以获取它包含的数据,如下所示:
定义一个带有 JAXB 注释的 Java 类 (C),它与请求中传递的 JSON 对象具有相同的结构。
例如对于 JSON 消息:
{
"A": "a value",
"B": "another value"
}
使用类似的东西:
@XmlAccessorType(XmlAccessType.FIELD)
public class C
{
public String A;
public String B;
}
然后,您可以在资源类中使用 C 类型的参数定义一个方法。当 Jersey 调用您的方法时,将基于 POSTed JSON 对象创建 JAXB 对象。
@Path("/resource")
public class MyResource
{
@POST
public put(C c)
{
doSomething(c.A);
doSomethingElse(c.B);
}
}
这使您可以访问原始帖子。
@POST
@Path("/")
@Consumes("text/plain")
@Produces(MediaType.APPLICATION_JSON)
public String processRequset(String pData) {
// do some stuff,
return someJson;
}
提交/发布表单/HTTP.POST,并带有一个参数,其中 JSON 作为值。
@QueryParam jsonString
公共desolveJson(jsonString)
一些答案说必须使用服务功能consumes=text/plain
,但我的 Jersey 版本可以使用application/json
类型。杰克逊和泽西版本是
jackson-core=2.6.1, jersey-common=2.21.0
.
@POST
@Path("/{name}/update/{code}")
@Consumes({ "application/json;charset=UTF-8" })
@Produces({ "application/json;charset=UTF-8" })
public Response doUpdate(@Context HttpServletRequest req, @PathParam("name") String name,
@PathParam("code") String code, String reqBody) {
System.out.println(reqBody);
StreamingOutput stream = new StreamingOutput() {
@Override public void write(OutputStream os) throws IOException, WebApplicationException {
..my fanzy custom json stream writer..
}
};
CacheControl cc = new CacheControl();
cc.setNoCache(true);
return Response.ok().type("application/json;charset=UTF-8")
.cacheControl(cc).entity(stream).build();
}
application/json
客户端使用 json 请求正文提交请求。Servlet 代码可以将字符串解析为 JSON 对象或按原样保存到数据库中。
简单的解决方案:
如果您只有一个简单的 JSON 对象进入服务器并且您不想创建新的 POJO(java 类),那么只需执行此操作。
我发送到服务器的 JSON
{
"studentId" : 1
}
服务器代码:
//just to show you the full name of JsonObject class
import javax.json.JsonObject;
@Path("/")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response deleteStudent(JsonObject json) {
//Get studentId from body <-------- The relevant part
int studentId = json.getInt("studentId");
//Return something if necessery
return Response.ok().build();
}