2013-05-09 105 views
0

我正在使用Spring 3.2 MVC控制器和Spring-WS创建一个RESTful Web服务。 Spring控制器接受一个对象文件正确地更新数据库,然后将JSON返回到前端。 Spring上下文是为JSON的消息转换设置的。我有这些单元测试,所以我知道春季控制器正在工作,并相应地提交数据。smartgwt restdatasource json日期验证

的错误,实际上是一个警告,来的时候我得到的数据/ JSON从Web服务回:

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的目的。 的Spring MVC的控制器接收更新的样子:

@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":"[email protected]", 
"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。但现在,我升级到杰克逊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> 

在我的弹簧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> 

我有,因为它是在restTemplate引用添加MappingJackson2HttpMessageConverter第二次......但如果我能确定这一次,这将是精细。所以,也许有人可以帮助我更好地定义spring-servlet.xml。

无论如何,这种变化的作品,因此在JSON日期回来为:

"userBirthDate":"1966-11-03T00:00:00-0500" 

所以,这是迄今取得的进展。

回答

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的最有效以下日期格式时使用(e.g.- 2013-05-09T00:00:00)。
yyyy-MM-dd'T'HH:mm:ss

System.out.println()不能在SmartGWT的/ GWT被用作客户端代码转换成JavaScript和在浏览器内运行,无需一个JVM。

在这种情况下,您可能不需要使用transformResponse()

+0

感谢您的帮助。你最初列出的假设是正确的。您提供的链接非常有帮助,事实证明,它正在配置Jackson2Mapper,以返回yyyy-MM-dd的日期而不是时间戳。所以,谢谢你的帮助,这非常有用。 – tjholmes66 2013-05-16 13:33:14

+0

@ tjholmes66你应该接受这个答案,所以Sithsu得到一些声望。 – 2013-05-21 14:01:56