SmartGWT RestDataSource 调用现有的 REST 服务 [英] SmartGWT RestDataSource calling an existing REST service

查看:105
本文介绍了SmartGWT RestDataSource 调用现有的 REST 服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个数据库后端,我已经通过多个单元测试对其进行了全面测试.控制器看起来像这样:

I have a database back-end that I've thoroughly tested with several unit tests. The controller looks like this:

@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 服务,并且数据已放入数据库中.

I have confirmed that the unit test works great, and the actual web-service is called correctly, and data is put into the database.

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

Now, I am using a SmartGWT RestDataSource, and I am trying to configure the RestDataSource correctly to pass a new user in the request body, and return the new object. I want to send the data as JSON in the body, and return JSON from this call. So, it might be that I need to change the controller itself to match up with the datasource.

这是扩展 RestDataSource 的 AbstractDataSource:

Here is the AbstractDataSource which extends RestDataSource:

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

这里是扩展 AbstractRestDataSource 的 UserDataSource:

And here is the UserDataSource which extends 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 Message Converters.

If I can find out how to get the UserDTO as JSON into the requestBody, I think I will have solved all my issues. For extra information you should know, I am using Spring 3.2 and the Jackson Message Converters configured in the springmvc-servlet.xml file.

有一次,我确实看到所有数据都附加到 URL 中,但我更希望数据不在 URL 中作为参数,而是在请求正文中.所以,我需要知道这是否可行,以及如何去做.

At one point I did see all the data getting appended to the URL, but I would prefer if the data was not in the URL as parameters, but rather in the request body. SO, I need to know if this is possible, and how to do it.

感谢您的帮助!!!

推荐答案

您可能想要撤消所有这些修改并只实现默认的 RestDataSource 协议,如果您只调用 RestDataSource.setDataFormat().文档中有示例 JSON 消息:

You probably want to undo all of these modifications and just implement the default RestDataSource protocol, which already passes the request body as JSON if you just call RestDataSource.setDataFormat(). There are sample JSON messages in the docs:

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

在您创建的其他问题中:

Among other problems you've created:

  1. 不同的 CRUD 操作现在转到不同的 URL 并使用不同的 HTTP 动词,因此它们不能再组合到一个队列中并一起发送.这意味着你不能做基本的事情,比如在一个事务中同时执行创建和更新操作的混合,保存一个新的订单和它的订单项,或者保存数据并获取转换所需的相关数据到新屏幕.

  1. the different CRUD operations now go to distinct URLs and use different HTTP verbs, so they can no longer be combined into a single queue and sent together. This means you can't do basic things like perform a mixture of create and update operations together in a transaction, save a new Order along with it's OrderItems, or save data and also fetch dependent data needed to transition to a new screen.

您假设获取"将基于 HTTP GET,但这需要将嵌套条件结构 (AdvancedCriteria) 笨拙地编码为 URL 参数,这很容易达到最大 URL 长度

you're assuming the "fetch" will be based on HTTP GET, but this requires awkward encoding of nested criteria structures (AdvancedCriteria) into URL parameters, which can easily hit the maximum URL length

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

For these and other reasons, most generated Java services do not meet the needs of a modern UI, and the auto-generation approach should not be used. Deeper explanation in the FAQ:

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

这篇关于SmartGWT RestDataSource 调用现有的 REST 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆