我使用Spring 3.2 MVC Controller和Spring-WS来创建RESTful Web服务。 Spring控制器接受一个对象文件正确更新数据库,然后将JSON返回给前端。为JSON的消息转换设置了Spring Context。我有这些单元测试,所以我知道Spring控制器正在工作并且相应地提交数据。
当我从网络服务中获取数据/ JSON时出现错误,实际上是警告:
10:05:08.906[ERROR[Phonebook]10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate:value:-99187200000 failed on validator {type:"isDate",typeCastValidator:true,_generated:true,defaultErrorMessage:"Must be a date."}
com.smartgwt.client.core.JsObject$SGWT_WARN: 10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate: value: -99187200000 failed on validator: {type: "isDate",typeCastValidator: true,_generated: true,defaultErrorMessage: "Must be a date."}
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
at java.lang.Thread.run(Thread.java:662)
所以,这是我的UserDataSource:
package com.opensource.restful.client.datasource;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.JavaScriptObject;
import com.opensource.restful.shared.Constants;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.OperationBinding;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.data.fields.DataSourceBooleanField;
import com.smartgwt.client.data.fields.DataSourceDateField;
import com.smartgwt.client.data.fields.DataSourceIntegerField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.DSDataFormat;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.DSProtocol;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.util.JSON;
public class UserDataSource extends RestDataSource
{
private static UserDataSource instance = null;
public static UserDataSource getInstance()
{
if (instance == null)
{
instance = new UserDataSource("restUserDS");
}
return instance;
}
private UserDataSource(String id)
{
setID(id);
setClientOnly(false);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.GETPARAMS);
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("GET");
fetch.setRequestProperties(fetchProps);
// set up ADD to use POST requests
OperationBinding add = new OperationBinding();
add.setOperationType(DSOperationType.ADD);
add.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest addProps = new DSRequest();
addProps.setHttpMethod("POST");
// addProps.setContentType("application/json");
add.setRequestProperties(addProps);
// set up UPDATE to use PUT
OperationBinding update = new OperationBinding();
update.setOperationType(DSOperationType.UPDATE);
update.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest updateProps = new DSRequest();
updateProps.setHttpMethod("PUT");
// updateProps.setContentType("application/json");
update.setRequestProperties(updateProps);
// set up REMOVE to use DELETE
OperationBinding remove = new OperationBinding();
remove.setOperationType(DSOperationType.REMOVE);
DSRequest removeProps = new DSRequest();
removeProps.setHttpMethod("DELETE");
remove.setRequestProperties(removeProps);
// apply all the operational bindings
setOperationBindings(fetch, add, update, remove);
init();
}
private DataSourceIntegerField userIdField;
private DataSourceBooleanField userActiveField;
private DataSourceTextField usernameField;
private DataSourceTextField passwordField;
private DataSourceTextField firstnameField;
private DataSourceTextField lastnameField;
private DataSourceTextField emailField;
private DataSourceTextField securityQuestion1Field;
private DataSourceTextField securityAnswer1Field;
private DataSourceTextField securityQuestion2Field;
private DataSourceTextField securityAnswer2Field;
private DataSourceDateField birthdateField;
private DataSourceIntegerField positionIdField;
protected void init()
{
setDataFormat(DSDataFormat.JSON);
setJsonRecordXPath("/");
// set the values for the datasource
userIdField = new DataSourceIntegerField(Constants.USER_ID, Constants.TITLE_USER_ID);
userIdField.setPrimaryKey(true);
userIdField.setCanEdit(false);
userActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
usernameField = new DataSourceTextField(Constants.USER_USERNAME, Constants.TITLE_USER_USERNAME);
passwordField = new DataSourceTextField(Constants.USER_PASSWORD, Constants.TITLE_USER_PASSWORD);
firstnameField = new DataSourceTextField(Constants.USER_FIRST_NAME, Constants.TITLE_USER_FIRST_NAME);
lastnameField = new DataSourceTextField(Constants.USER_LAST_NAME, Constants.TITLE_USER_LAST_NAME);
emailField = new DataSourceTextField(Constants.USER_EMAIL, Constants.TITLE_USER_EMAIL);
securityQuestion1Field =
new DataSourceTextField(Constants.USER_SECURITY_QUESTION_1, Constants.TITLE_USER_SECURITY_QUESTION_1);
securityAnswer1Field =
new DataSourceTextField(Constants.USER_SECURITY_ANSWER_1, Constants.TITLE_USER_SECURITY_ANSWER_1);
securityQuestion2Field =
new DataSourceTextField(Constants.USER_SECURITY_QUESTION_2, Constants.TITLE_USER_SECURITY_QUESTION_2);
securityAnswer2Field =
new DataSourceTextField(Constants.USER_SECURITY_ANSWER_2, Constants.TITLE_USER_SECURITY_ANSWER_2);
birthdateField = new DataSourceDateField(Constants.USER_BIRTHDATE, Constants.TITLE_USER_BIRTHDATE);
positionIdField = new DataSourceIntegerField(Constants.USER_POSITION_ID, Constants.TITLE_USER_POSITION_ID);
// positionActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
// positionCodeField;
// positionDescriptionField;
setFields(userIdField, userActiveField, usernameField, passwordField, firstnameField, lastnameField,
emailField, birthdateField, securityQuestion1Field, securityAnswer1Field, securityQuestion2Field,
securityAnswer2Field, positionIdField);
setFetchDataURL(getServiceRoot() + "/userId/{id}"); // works great
setAddDataURL(getServiceRoot() + "/create");
setUpdateDataURL(getServiceRoot() + "/update");
setRemoveDataURL(getServiceRoot() + "/remove"); // works great
}
protected String getServiceRoot()
{
return "rest/users";
}
protected String getPrimaryKeyProperty()
{
return "userId";
}
@Override
protected Object transformRequest(DSRequest dsRequest)
{
System.out.println("UserDataSource: transformRequest: START");
dsRequest.setContentType("application/json");
JavaScriptObject jso = dsRequest.getData();
String jsoText = JSON.encode(jso);
System.out.println("UserDataSource: transformRequest: START: jsoText=" + jsoText);
// ================================================================================
// String strDob = JSOHelper.getAttribute(jso, Constants.USER_BIRTHDATE);
// Date dateDob = JSOHelper.getAttributeAsDate(jso, Constants.USER_BIRTHDATE);
// JSOHelper.setAttribute(jso, Constants.USER_BIRTHDATE, dateDob.getTime());
// System.out.println("UserDataSource: transformRequest: START2: jsoText2=" + jsoText);
// ================================================================================
// get the user position id which comes from the UI
// the name of this field from the UI 'userPositionId'
String userPositionId = JSOHelper.getAttribute(jso, Constants.USER_POSITION_ID);
// create a small JavaScriptObject to be used for the position
// the JSON string would look like {"id":x} x = userPositionId
Map mapPositionId = new HashMap();
mapPositionId.put("id", userPositionId);
JavaScriptObject jsoPositionId = JSOHelper.convertMapToJavascriptObject(mapPositionId);
// This creates the new JSON attribute:
// ... , "position":{"id":x}
JSOHelper.setAttribute(jso, "position", jsoPositionId);
// remove the JSON Attribute: ... , "userPositionId":x
JSOHelper.deleteAttribute(jso, Constants.USER_POSITION_ID);
String s1 = JSON.encode(jso);
System.out.println("UserDataSource: transformRequest: FINISH: s1=" + s1);
return s1;
// return super.transformRequest(dsRequest);
}
protected void transformResponse(DSResponse response, DSRequest request, Object data)
{
System.out.println("UserDataSource: transformResponse: START");
super.transformResponse(response, request, data);
System.out.println("UserDataSource: transformResponse: FINISH");
}
}
我可以确认我发送数据/ JSON就好了。我必须稍微改一下才能添加我要发回的属性。而且我相信这是TransformRequest的目的。 接收Update的Spring MVC Controller看起来像:
@RequestMapping(value="/update",
method=RequestMethod.PUT,produces="application/json",
headers="content-type=application/json")
public @ResponseBody UserDTO updateUser(@RequestBody UserDTO user)
{
System.out.println("UserController: START: updateUser: user=" + user);
UserEntity userEntity = service.update(user);
UserDTO userDto = Mapping.mappingUser(userEntity);
System.out.println("UserController: FINISH: updateUser: userDto=" + userDto);
return userDto;
}
我可以确认我收到了有效的UserDTO。当我查看transformResponse时:
System.out.println("UserDataSource: transformResponse: START");
super.transformResponse(response, request, data);
System.out.println("UserDataSource: transformResponse: FINISH");
我在第一个println上得到了错误,我还没有完成super.transformResponse。当我查看回来的数据时,这是我要回来的JSON。
{
"userId":1,
"userActive":true,
"position":{
"id":1,
"active":true,
"code":"ADMIN",
"description":"Administrator"
},
"username":"demo",
"password":"demo",
"otherPassword":null,
"userFirstName":"DemoXXX",
"userLastName":"DemoXXX",
"userEmail":"tom@tomholmes.netXXX",
"userSecurityQuestion1":"Meaning of Life?XXX",
"userSecurityAnswer1":"42XX",
"userSecurityQuestion2":"aaaXX",
"userSecurityAnswer2":"bbbXX",
"userBirthDate":-99100800000,
"contacts":[
{
"contactId":2,
"userId":1,
"prefix":"Mr.",
"firstName":"updated_fn",
"middleName":null,
"lastName":"updated_ln",
"suffix":"Jr.",
"address1":"123 main street",
"address2":"Apt. 456",
"city":"Randolph",
"state":"MA",
"zip":"12345-1234",
"companyId":0,
"enteredBy":0,
"enteredDate":null,
"editedBy":0,
"editedDate":null,
"birthDate":null,
"emails":null,
"phones":null,
"links":null
}
],
"userPositionId":null
}
那么......如何修复我的数据源或transformResponse以删除此警告? JSON似乎是正确的,唯一的问题是“userBirthDate”当它作为一个长的负数回来时,我假设从纪元开始的毫秒数。我可以在JSON / Jackson Mapper中进行一些更改以更改日期的格式吗?
感谢您的帮助!
更新1: 下面提供的帮助很有帮助,现在我知道这不是SmartGWT或RestDataSource问题,而是严格意义上jackson如何在对象中转换java.util.Date。转换将日期更改为负长号,并且应具有其他格式。我正在使用Spring 3.2并使用旧的Jackson 1.9.14。但现在,我升级到Jackson 2,我的pom.xml现在使用:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.1.4</version>
</dependency>
在我的spring-servlext.xml中:
<context:component-scan base-package="com.opensource.restful" />
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json"/>
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter"/>
</list>
</property>
</bean>
<mvc:annotation-driven />
我已经谷歌搜索了几个小时,并寻找在Spring配置中使用Jackson2映射器的解决方案,并且在我确保所有bean定义都正确之后,userBirthDate仍然以负数长度返回。我确信这个配置可以稍微调整一下,以便按照我想要的方式调整,所以日期以ISO格式返回:yyyy-MM-dd'T'HH:mm:ssZ
感谢您帮助我靠近。
更新2: 我想我做到了。如前所述,我升级到Jackson2,据我所知,它已经是Spring 3.2的一部分,这是我正在使用的Spring版本。
我正在使用的spring-servlet.xml,它的工作方式如下:
<context:component-scan base-package="com.opensource.restful" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>
我不得不第二次添加MappingJackson2HttpMessageConverter,因为它在restTemplate中被引用...但是如果我可以只定义一次,那就没问题了。所以,也许有人可以帮我更好地定义spring-servlet.xml。
无论如何,此更改有效,因此JSON日期将返回:
"userBirthDate":"1966-11-03T00:00:00-0500"
所以,到目前为止这是进展。
答案 0 :(得分:1)
从验证错误 - defaultErrorMessage:“必须是日期”
由于birthdateField是DataSourceDateField,因此UserDTO.userBirthDate
必须是java.util.Date
或类似且Date getUserBirthDate()
。
Constants.USER_BIRTHDATE
必须设置为"userBirthDate"
。
如果上述所有内容都正常,则由于java.util.Date对象的默认序列化为JSON。
有关其他信息,请查看以下内容
http://java.dzone.com/articles/how-serialize-javautildate(不要使用静态SimpleDateFormat)
Spring 3.1 JSON date format
jackson2 JSON ISO 8601 date from JodaTime in Spring 3.2RC1
使用以下日期格式时,SmartGWT效果最佳(例如,2013-05-09T00:00:00)。
yyyy-MM-dd'T'HH:mm:ss
System.out.println()
无法在SmartGWT / GWT中使用,因为客户端代码转换为JavaScript并在浏览器内运行,没有JVM。
在这种情况下,您可能不需要使用transformResponse()
。