我有一个相对简单的RESTful Web服务,它使用Jersey和Eclipselink MOXy。
如果数据格式化为XML,我可以向服务POST请求,但如果我将其作为JSON发送,服务器会生成HTTP 400(错误请求),并显示消息:“客户端发送的请求是语法不正确。“。
服务端看起来像这样:
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Subscription post(Subscription Subscription) {
return Subscriptions.addSubscription(Subscription);
}
如果我在这样的网页中从Javascript发送XML数据,则没有问题:
$.ajax({
url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
data: "<subscription><notificationType>EMAIL</notificationType><notificationAddress>test@example.com</notificationAddress></subscription>",
headers: {
Accept : "application/xml",
"Content-Type": "application/xml"
},
// .. other params ...
);
但是,使用等效的JSON,我得到HTTP 400 - 错误请求:
$.ajax({
url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
data: JSON.stringify(
{subscription:{notificationType:"EMAIL",notificationAddress:"test@example.com"}}
),
dataType: 'json'
headers: {
Accept : "application/json",
"Content-Type": "application/json"
}
// .. other params ...
);
我已经使用Fiddler检查了请求,数据格式和标题看起来都是正确的。
有趣的是,如果我将其插入此代码,我可以成功解组完全相同的JSON字符串:
String json = "{\"subscription\":{\"notificationType\":\"EMAIL\",\"notificationAddress\":\"test@example.com\"}}";
JAXBContext context = JAXBContext.newInstance(Subscription.class);
Unmarshaller m = context.createUnmarshaller();
m.setProperty("eclipselink.media-type", "application/json");
StringReader sr = new StringReader(json);
Subscription sub = (Subscription)m.unmarshal(sr);
System.out.println(sub.toString());
订阅类定义为:
@XmlRootElement(name="subscription")
public class Subscription {
public enum NotificationType { EMAIL, SMS };
private String notificationAddress;
private NotificationType notificationType;
public String getNotificationAddress() {
return notificationAddress;
}
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public NotificationType getNotificationType() {
return notificationType;
}
public void setNotificationType(NotificationType notificationType) {
this.notificationType = notificationType;
}
public Subscription() {
}
@Override
public String toString() {
String s = "Subscription";
if (getNotificationAddress() != null) {
s += "(" + getNotificationType().toString() + ":" + getNotificationAddress() + ")";
}
return s;
}
}
我已将Eclipselink MOXy配置为我的JAXB提供程序,方法是将此行添加到包含我的Subscriber类的包中的jaxb.properties:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
这似乎有效,至少是为了从服务中调出对象。
我错过了什么?
修改: 这是Fiddler在发布JSON数据时捕获的内容:
POST http://localhost:8080/MyService/subscription HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 86
Accept: application/json
Origin: http://localhost:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36
Content-Type: application/json
Referer: http://localhost:8080/TestPage/AddSubscription.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
{"subscription":{"notificationType":"EMAIL","notificationAddress":"test@example.com"}}
更新
我从下面的Blaise的答案中选择了#2,创建了一个Application派生类,因此:
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class MyApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(SubscriptionResource.class); // the class containing my @POST service method.
return set;
}
}
并添加到web.xml:
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.MyApplication</param-value>
现在我没有获得HTTP 400,并且我的服务中的代码被命中,而之前没有,但是传入的Subscription对象具有所有未初始化的字段,例如notificationAddress为null。 如果我使用XML发布,它仍然可以正常工作。
更新#2:
我已将代码缩减为演示问题的最小子集,您可以在此处获取:
https://www.dropbox.com/sh/2a6iqw65ey0ahrk/D2ILi_722z
上面的链接包含一个带有2个Eclipse项目的.zip; TestService(接受Subscription对象的Jersey RESTful服务)和TestPage(带有一些JavaScript的.html页面,用JSON或XML发布订阅对象)。
如果在服务的post方法中放置断点,并使用测试页面发送JSON请求,您将看到传入未初始化的Subscription对象。
答案 0 :(得分:2)
EclipseLink JAXB (MOXy)可以通过几种不同的方式与Jersey一起使用来生成JSON。
选项#1 - 添加jaxb.properties文件
Jersey可以利用JAXB(JSR-222)实现来生成JSON。如果您在域模型中添加jaxb.properties
,则可以将MOXy指定为该提供者。有一段时间,泽西岛存在以下错误,阻止MOXy以这种方式使用,这可能就是你现在所要达到的。
这种生成JSON的方法有一些限制,可能不是你最终想要的。
选项#2 - 利用MOXyJsonProvider
从EclipseLink 2.4.0开始,MOXy提供了基于JAXB和MOXy元数据的自己的JSON绑定。您可以使用MOXyJsonProvider配置它。
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class MyApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(1);
set.add(SubscriptionResource.class);
return set;
}
@Override
public Set<Object> getSingletons() {
MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
moxyJsonProvider.setIncludeRoot(true);
HashSet<Object> set = new HashSet<Object>(1);
set.add(moxyJsonProvider);
return set;
}
}
了解更多信息
这是我推荐使用MOXy与Jersey或任何其他JAX-RS提供商的方式。