0

我正在使用 Spring 3.2 MVC 控制器和 Spring-WS 来创建 RESTful Web 服务。Spring 控制器正确地接受对象文件对数据库的更新,然后将 JSON 返回到前端。Spring Context 是为 JSON 的消息转换设置的。我有这些的单元测试,所以我知道 Spring 控制器正在工作并相应地归档数据。

当我从 Web 服务中获取数据/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 的目的。接收更新的 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":"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 问题,而是杰克逊如何在对象内转换 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"   

所以,这是迄今为止的进展。

4

1 回答 1

1

来自验证错误 - defaultErrorMessage:"Must be a date"

由于birthdateField 是DataSourceDateField,所以你UserDTO.userBirthDate必须是ajava.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 日期格式
jackson2 JSON ISO 8601 日期来自 Spring 3.2RC1 中的 JodaTime

SmartGWT 在使用以下日期格式时效果最佳(例如 2013-05-09T00:00:00)。
yyyy-MM-dd'T'HH:mm:ss

System.out.println()不能在 SmartGWT/GWT 中使用,因为客户端代码被转换为 JavaScript 并在浏览器中运行,无需 JVM。

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

于 2013-05-09T16:13:11.520 回答