SmartGWT RestDataSource调用现有的REST服务

时间:2013-03-28 14:36:02

标签: json rest spring-mvc smartgwt

我有一个数据库后端,我已经通过几个单元测试进行了彻底的测试。控制器如下所示:

@RequestMapping(value = "/create", method = RequestMethod.POST, produces = "application/json", headers = "content-type=application/json")
public @ResponseBody UserDTO createUser(@RequestBody UserDTO user)
{
    UserEntity userEntity = service.add(user);
    return mappingUser(userEntity);
}

单元测试如下:

 @Test
    public void testCreateUser() throws Exception
    {
        UserDTO userDto = createUserDto();
        String url = BASE_URL + "/rest/users/create";
        UserDTO newUserDto = restTemplate
        .postForObject(url, userDto, UserDTO.class, new Object[]{});
    }

我已经确认单元测试工作正常,并且正确调用了实际的Web服务,并将数据放入数据库。

现在,我正在使用SmartGWT RestDataSource,我正在尝试正确配置RestDataSource以在请求正文中传递新用户,并返回新对象。我想在主体中将数据作为JSON发送,并从此调用返回JSON。因此,我可能需要更改控制器本身以匹配数据源。

这是扩展RestDataSource的AbstractDataSource:

import java.util.Map;

import com.google.gwt.http.client.URL;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.OperationBinding;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.DSProtocol;

public abstract class AbstractRestDataSource extends RestDataSource
{
    public AbstractRestDataSource(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");
        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();
    }

    @Override
    protected Object transformRequest(DSRequest request)
    {
        super.transformRequest(request);

        // now post process the request for our own means
        postProcessTransform(request);

        return request.getData();
    }

    /*
     * Implementers can override this method to create a 
     * different override.
     */
    @SuppressWarnings("rawtypes")
    protected void postProcessTransform(DSRequest request)
    {
        StringBuilder url = new StringBuilder(getServiceRoot());

        Map dataMap = request.getAttributeAsMap("data");
        if (request.getOperationType() == DSOperationType.REMOVE)
        {
            // in case of remove, append the primary key
            url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
        }
        else if (request.getOperationType() == DSOperationType.UPDATE)
        {
            url.append("update");
            appendParameters(url, request);
        }
        else if (request.getOperationType() == DSOperationType.FETCH && dataMap.size() > 0)
        {
            url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
        }
        else if (request.getOperationType() == DSOperationType.ADD)
        {
            url.append("create");
        }

        System.out.println("AbstractRestDataSource: postProcessTransform: url=" + url.toString());
        request.setActionURL(URL.encode(url.toString()));
    }

    /*
     * This simply appends parameters that have changed to the URL
     * so that PUT requests go through successfully. This is usually
     * necessary because when smart GWT updates a row using a form,
     * it sends the data as form parameters. Most servers cannot
     * understand this and will simply disregard the form data
     * sent to the server via PUT. So we need to transform the form
     * data into URL parameters.
     */
    @SuppressWarnings("rawtypes")
    protected void appendParameters(StringBuilder url, DSRequest request)
    {
        Map dataMap = request.getAttributeAsMap("data");
        Record oldValues = request.getOldValues();
        boolean paramsAppended = false;

        if (!dataMap.isEmpty())
        {
            url.append("?");
        }

        for (Object keyObj : dataMap.keySet())
        {
            String key = (String) keyObj;
            if (!dataMap.get(key).equals(oldValues.getAttribute(key)) || isPrimaryKey(key))
            {
                // only append those values that changed or are primary keys
                url.append(key).append('=').append(dataMap.get(key)).append('&');
                paramsAppended = true;
            }
        }

        if (paramsAppended)
        {
            // delete the last '&'
            url.deleteCharAt(url.length() - 1);
        }
    }

    private boolean isPrimaryKey(String property)
    {
        return getPrimaryKeyProperty().equals(property);
    }

    /*
     * The implementer can override this to change the name of the
     * primary key property.
     */
    protected String getPrimaryKeyProperty()
    {
        return "id";
    }

    protected abstract String getServiceRoot();

    protected abstract void init();
}

这是UserDataSource,它扩展了AbstractRestDataSource:

import java.util.Map;

import com.google.gwt.http.client.URL;
import com.opensource.restful.shared.Constants;
import com.smartgwt.client.data.DSRequest;
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;

public class UserDataSource extends AbstractRestDataSource
{
    private static UserDataSource instance = null;

    public static UserDataSource getInstance()
    {
        if (instance == null)
        {
            instance = new UserDataSource("restUserDS");
        }

        return instance;
    }

    private UserDataSource(String id)
    {
        super(id);
    }

    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);
    }

    protected String getServiceRoot()
    {
        return "rest/users/";
    }

    protected String getPrimaryKeyProperty()
    {
        return "userId";
    }

    /*
     * Implementers can override this method to create a 
     * different override.
     */
    @SuppressWarnings("rawtypes")
    protected void postProcessTransform(DSRequest request)
    {
        // request.setContentType("application/json");

        StringBuilder url = new StringBuilder(getServiceRoot());

        Map dataMap = request.getAttributeAsMap("data");
        if (request.getOperationType() == DSOperationType.REMOVE)
        {
            // in case of remove, append the primary key
            url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
        }
        else if (request.getOperationType() == DSOperationType.UPDATE)
        {
            url.append("update");
            System.out.println("UserDataSource: postProcessTransform: update: url=" + url.toString());
        }
        else if (request.getOperationType() == DSOperationType.FETCH && dataMap.size() > 0)
        {
            url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
        }
        else if (request.getOperationType() == DSOperationType.ADD)
        {
            url.append("create");
        }

        System.out.println("UserDataSource: postProcessTransform: url=" + url.toString());
        request.setActionURL(URL.encode(url.toString()));
    }
}

如果我能找到如何将UserDTO作为JSON进入requestBody,我想我会解决所有问题。有关额外信息,您应该知道,我使用Spring 3.2和Springmvc-servlet.xml文件中配置的Jackson消息转换器。

有一次,我确实看到所有数据都附加到了URL,但我更愿意,如果数据不在URL中作为参数,而是在请求正文中。所以,我需要知道这是否可能,以及如何做到这一点。

感谢您的帮助!!!

1 个答案:

答案 0 :(得分:0)

您可能想要撤消所有这些修改,只需实现默认的RestDataSource协议,如果您只是调用RestDataSource.setDataFormat(),它已经将请求体作为JSON传递。文档中有示例JSON消息:

http://www.smartclient.com/smartgwtee/javadoc/com/smartgwt/client/data/RestDataSource.html

您创建的其他问题:

  1. 不同的CRUD操作现在转到不同的URL并使用不同的HTTP谓词,因此它们不能再组合成单个队列并一起发送。这意味着您不能执行基本之类的事情,例如在事务中一起执行创建和更新操作的混合,保存新订单及其OrderItems,或者保存数据并获取转换所需的依赖数据到新的屏幕。

  2. 您假设“fetch”将基于HTTP GET,但这需要将嵌套标准结构(AdvancedCriteria)笨拙地编码为URL参数,这可以轻松达到最大URL长度

    < / LI>

    由于这些和其他原因,大多数生成的Java服务无法满足现代UI的需求,因此不应使用自动生成方法。常见问题解答中的更深入解释:

    http://forums.smartclient.com/showthread.php?t=8159#aExistingRest