我有一个带有 GWT 2.5.1、SmartGWT 4.0、Spring 3.2.3.Release 和 Hibernate 4.1 的基于 Web 的应用程序。前端使用一个 SmartGWT RestDataSource 将数据传递给 RESTful Web 服务,一个 Spring MVC 控制器,它使用 Java 将数据传递到前端。
控制器经过单元测试并且效果很好,我使用 GET 以 JSON 格式传回数据,控制器调用后端,获取我的数据,然后我将 UserEntity 以 JSON 格式返回给 RestDataSource。
错误是:[ERROR] [TestAdmin] - 22:01:22.432:XRP8:WARN:RestDataSource:restLoginDS:RestDataSouce transformResponse(): JSON 响应文本似乎不是标准响应格式。
我做了很多谷歌搜索,并在这个网站上寻找,我可以找到有类似问题但没有好的解决方案的人。
这是 RestDataSource:
public class LoginDataSource extends RestDataSource
{
private static LoginDataSource instance = null;
public static LoginDataSource getInstance()
{
if (instance == null)
{
instance = new LoginDataSource("restLoginDS");
}
return instance;
}
private LoginDataSource(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; // "userId":"1",
private DataSourceTextField usernameField; // "username":"myusername",
private DataSourceTextField passwordField; // "password":"mypassword",
private DataSourceBooleanField userActiveField; // "active":true,
private DataSourceTextField fullnameField; // "fullname":"Thomas Holmes",
private DataSourceDateField birthdateField; // "birthdate":"1960-10-30",
private DataSourceTextField emailField; // "email":"myemail@test.net",
private DataSourceTextField cellPhoneField; // "cellPhone":"111-222-1234"
private DataSourceIntegerField updatedByField; // "updatedBy":1,
private DataSourceDateField updatedDateField; // "updatedDate":"2013-01-01",
private DataSourceIntegerField createdByField; // "createdBy":1,
private DataSourceDateField createdDateField; // "createdDate":"2013-01-01",
private DataSourceTextField securityQuestion1Field; // "securityQuestion1":"peanuts",
private DataSourceTextField securityAnswer1Field; // "securityAnswer1":"linus"
protected void init()
{
System.out.println("init: START");
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);
usernameField = new DataSourceTextField(Constants.USER_USERNAME, Constants.TITLE_USER_USERNAME);
usernameField.setCanEdit(false);
passwordField = new DataSourceTextField(Constants.USER_PASSWORD, Constants.TITLE_USER_PASSWORD);
passwordField.setCanEdit(false);
userActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
fullnameField = new DataSourceTextField(Constants.USER_FULLNAME, Constants.TITLE_USER_FULLNAME);
birthdateField = new DataSourceDateField(Constants.USER_BIRTHDATE, Constants.TITLE_USER_BIRTHDATE);
emailField = new DataSourceTextField(Constants.USER_EMAIL, Constants.TITLE_USER_EMAIL);
cellPhoneField = new DataSourceTextField(Constants.USER_CELL_PHONE, Constants.TITLE_USER_CELL_PHONE);
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);
updatedByField = new DataSourceIntegerField(Constants.USER_UPDATED_BY, Constants.TITLE_USER_UPDATED_BY);
updatedDateField = new DataSourceDateField(Constants.USER_UPDATED_DATE, Constants.TITLE_USER_UPDATED_DATE);
createdByField = new DataSourceIntegerField(Constants.USER_CREATED_BY, Constants.TITLE_USER_CREATED_BY);
createdDateField = new DataSourceDateField(Constants.USER_CREATED_DATE, Constants.TITLE_USER_CREATED_DATE);
System.out.println("init: FINISH");
setFields(userIdField, usernameField, passwordField, userActiveField, emailField, cellPhoneField,
fullnameField, birthdateField, securityQuestion1Field, securityAnswer1Field, updatedByField,
updatedDateField, createdByField, createdDateField);
// setFetchDataURL(getServiceRoot() + "/userId/{id}");
// setFetchDataURL(getServiceRoot() + "/contactId/{id}");
setAddDataURL(getServiceRoot() + "/create");
setUpdateDataURL(getServiceRoot() + "/update");
setRemoveDataURL(getServiceRoot() + "/remove/{id}");
}
protected String getServiceRoot()
{
return "rest/login/";
}
protected String getPrimaryKeyProperty()
{
return "userId";
}
/*
* Implementers can override this method to create a different override.
*/
@SuppressWarnings("rawtypes")
protected void postProcessTransform(DSRequest request)
{
System.out.println("LoginDataSource: postProcessTransform: START");
StringBuilder url = new StringBuilder(getServiceRoot());
System.out.println("LoginDataSource: postProcessTransform: url=" + url);
Map dataMap = request.getAttributeAsMap("data");
System.out.println("LoginDataSource: postProcessTransform: dataMap=" + dataMap.toString());
if (request.getOperationType() == DSOperationType.FETCH && dataMap.size() > 0)
{
if (dataMap.get(Constants.USER_USERNAME) != null && dataMap.get(Constants.USER_PASSWORD) != null)
{
url.append("user/" + dataMap.get(Constants.USER_USERNAME));
url.append("/pwd/" + dataMap.get(Constants.USER_PASSWORD));
}
else if (dataMap.get(Constants.USER_USERNAME) != null && dataMap.get(Constants.USER_PASSWORD) == null)
{
url.append("user/" + dataMap.get(Constants.USER_USERNAME));
url.append("/pwd/" + dataMap.get(Constants.USER_PASSWORD));
}
else if (dataMap.get(Constants.USER_EMAIL) != null)
{
url.append("email/" + dataMap.get(Constants.USER_EMAIL));
}
}
System.out.println("LoginDataSource: postProcessTransform: url=" + url.toString());
request.setActionURL(URL.encode(url.toString()));
}
@Override
protected Object transformRequest(DSRequest dsRequest)
{
// now post process the request for our own means
postProcessTransform(dsRequest);
System.out.println("LoginDataSource: transformRequest: START");
dsRequest.setContentType("application/json");
JavaScriptObject jso = dsRequest.getData();
String jsoText = JSON.encode(jso);
System.out.println("LoginDataSource: transformRequest: START: jsoText=" + jsoText);
// this code is used only when there is a password change, otherwise this will be skipped
String userPassword = JSOHelper.getAttribute(jso, Constants.USER_NEW_PASSWORD);
if (userPassword != null)
{
// This creates the new JSON attribute:
// ... , "position":{"id":x}
JSOHelper.setAttribute(jso, "password", userPassword);
// remove the JSON Attribute: ... , "userPassword":"newPassword"
JSOHelper.deleteAttribute(jso, Constants.USER_NEW_PASSWORD);
}
System.out.println("LoginDataSource: transformRequest: FINISH: url=" + dsRequest.getActionURL());
String s1 = JSON.encode(jso);
System.out.println("LoginDataSource: transformRequest: FINISH: s1=" + s1);
return s1;
}
protected void transformResponse(DSResponse response, DSRequest request, Object jsonData)
{
System.out.println("LoginDataSource: transformResponse: START");
JavaScriptObject jsObj = (JavaScriptObject) jsonData;
String jsoText1 = JSON.encode(jsObj);
System.out.println("LoginDataSource: transformResponse: jsoText=" + jsoText1);
System.out.println("LoginDataSource: transformResponse: jsonData=" + jsonData.getClass());
for (String attr : response.getAttributes())
{
System.out.println("LoginDataSource: transformResponse: attr=" + attr + " value="
+ response.getAttribute(attr));
}
super.transformResponse(response, request, jsonData);
}
}
错误出现就行了: super.transformResponse(response, request, jsonData);
我知道从 Controller 返回的数据是 JSON 数据,如下所示:
{
"userId":1,
"username":"my_username",
"password":"my_password",
"active":true,
"fullname":"Thomas Holmes",
"birthdate":"1960-10-13",
"email":"test@test.net",
"cellPhone":"111-222-1234",
"updatedBy":1,
"updatedDate":"2013-01-01",
"createdBy":1,
"createdDate":"2013-01-01",
"securityQuestion1":"peanuts",
"securityAnswer1":"linus"
}
我已经测试过这些名称是否与 json 数据中的数据源字段匹配。通过检查上面的数据源字段,我们应该能够看到这一点。我还使用 JUnit 和 Jackson Mapper 2.0 进行了单元测试,JSON 字符串数据可用于创建 UserDTO 对象和 UserEntity 对象。
我非常了解有关从控制器返回的数据以及它必须如何匹配所需格式的 SmartClient 文档。这是一个 SmartGWT RestDataSource,我查看了看起来不错的响应。
在 transformResponse 代码中:
for (String attr : response.getAttributes())
{
System.out.println("transformResponse: attr=" + attr + " value="
+ response.getAttribute(attr));
}
产生:
transformResponse: jsonData=class com.google.gwt.core.client.JavaScriptObject$
transformResponse: attr=data value=[object Object]
transformResponse: attr=startRow value=0
transformResponse: attr=status value=0
transformResponse: attr=endRow value=1
transformResponse: attr=totalRows value=1
transformResponse: attr=httpResponseCode value=200
transformResponse: attr=transactionNum value=0
transformResponse: attr=clientContext value=null
transformResponse: attr=httpHeaders value=[object Object]
transformResponse: attr=context value=[object Object]
看起来“Object jsonData”是一个 JavaScriptObject。最终,在 JavascriptObject 中返回的 JSON,我想转换为 UserDTO 对象。
所以,如果我能消除这个错误并解决这个目标,那就太好了。
谢谢!
更新
我一直在对此进行测试,我终于有了一个小型测试应用程序,我认为它表明 Isomorphic 破坏了 SmartGWT 中的 RestDataSource。我这么说是因为我的应用程序之前可以工作,但现在它不起作用。
我确认所有这些都是 100% 有效的 JSON 数据。通过网络上的各种测试。
test1.json: {"userId":1}
test2.json: {"userId":"1"}
test3.json: [{"userId":1}]
test4.json: [{"userId":"1"}]
然后我隔离到一个非常小的应用程序进行测试。这类似于 SmartGWT Showcase 上的内容。
public class TestApp implements EntryPoint
{
private DataSourceIntegerField userIdField;
public void onModuleLoad()
{
RestDataSource dataSource = new RestDataSource();
dataSource.setDataFormat(DSDataFormat.JSON);
dataSource.setDataURL("data/single_user.json");
// set the values for the datasource
userIdField = new DataSourceIntegerField("userId", "User Id");
userIdField.setPrimaryKey(true);
userIdField.setCanEdit(false);
dataSource.setFields(userIdField);
ListGrid grid = new ListGrid();
grid.setDataSource(dataSource);
grid.setWidth100();
grid.setHeight(150);
grid.setAutoFetchData(true);
grid.draw();
}
}
在每种情况下,我都会收到相同的错误消息:
[ERROR] [SoccerAdmin] - 15:20:55.945:XRP6:WARN:RestDataSource:isc_RestDataSource_0:RestDataSouce transformResponse(): JSON response text does not appear to be in standard response format.
但是,如果我从 RestDataSource 更改为 DataSource,则 TransformResponse 没有任何问题。
我想也许我不知道 TransformResponse 应该对 RestDataSource 做什么,但我确实阅读了 SmartClient Docs 以了解它的价值。
如果我找到一个很好的解决方法,那么我会发布一个答案。