2013-05-09 104 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