【问题标题】:Passing partial REST responses as they are received在收到部分 REST 响应时传递它们
【发布时间】:2017-02-07 15:06:10
【问题描述】:

我有一个 ASP.NET MVC 应用程序,它使用 RestSharp 连接到第 3 方,以便接收域名列表,然后在结果中获得有关每个域的更多详细信息。

我相信响应来自部分数据包,RestSharp 正在等待,直到所有数据包都收到后,才会以反序列化格式输出数据。

有问题的有效负载如下所示。 “标题”首先被填充,并且是我想立即返回到视图的响应。其余数据的时间敏感性较低:

[
{"header":{"names":["test.1","test.2","test.3","test.4","test.5","test.6"]}}
,
{"name":"test.1","can":"transfer?"}
,
{"name":"test.2","can":"transfer?"}
,
{"name":"test.3","can":"transfer?"}
,
{"name":"test.4","can":"transfer?"}
,
{"name":"test.5","can":"transfer?"}
,
{"name":"test.6","can":"register"}
]

目前有两种执行方式,一种是同步的,一种是异步的:

public T ExecuteGetRequest<T>(RestRequest request) where T : class
{
    try
    {
        IRestResponse response = _client.Execute(request);

        if (response.ErrorException == null)
        {
            return JsonConvert.DeserializeObject<T>(response.Content);
        }
        return null;
    }
    catch (Exception ex)
    {
        return null;
    }
}

public Task<T> ExecuteGetRequestAsync<T>(RestRequest request) where T : new()
{
    try
    {
        var source = new TaskCompletionSource<T>();
        _client.ExecuteAsync<T>(request, (response) => { source.SetResult(response.Data); });
        return source.Task;
    }
    catch (Exception ex)
    {
        return null;
    }
}

应用程序有一个本地 API 调用来使用异步实现搜索,如下所示:

public async Task<DomainSearchResults> DomainSearchAsync(string domain)
{
    var request = _client.CreateRequest("domain-search/{domain}", Method.GET);
    request.AddUrlSegment("domain", domain);
    var response = await _client.ExecuteGetRequestAsync<List<DomainSearch>>(request);
    return new DomainSearchResults(response);
}

然后解释响应并向客户端提供相关的搜索结果。

从某种意义上说,当所有数据都已由第 3 方发送时,该对象将返回到视图并相应地填充,这可以正常工作。但是,请求的完全完成可能需要长达 20 秒,这对用户来说并不是特别有用。

有没有办法让 ExecuteGetRequestAsync 在收​​到完整响应之前开始将不完整的响应发送回调用视图?

我最初的尝试看起来很像这样:

public Task<T> ExecuteGetRequestAsyncIncomplete<T>(RestRequest request) where T : new()
{
    try
    {
        var source = new TaskCompletionSource<T>();
        _client.ExecuteAsync<T>(request, (response) =>
        {
            source.SetResult(response.Data);
            if (response.StatusCode == HttpStatusCode.PartialContent)
            {
                // Somehow return part of this response
            }

        });
        return source.Task;
    }
    catch (Exception ex)
    {
        return null;
    }
}

更新

根据@Evk 的回答,这是返回部分结果的新调用的样子,特别是针对这种情况:

public async Task<T> ExecuteGetRequestPartial<T>(RestRequest request) where T : new()
{

    try
    {
        var source = new TaskCompletionSource<T>();
        request.ResponseWriter = (st) => {
            using (var reader = new StreamReader(st))
            {
                var sb = new StringBuilder();
                // read response 100 chars at a time
                char[] buffer = new char[1];
                int read = 0;
                while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                {
                    sb.Append(buffer, 0, read);
                    // now here you have your partial response
                    // you need to somehow parse it and feed to your view
                    // note that you should wait until you get some meaningful part, like first "header" element
                    // for example at some point there might be ["header":{"names":["te < partial response
                    if (sb.ToString().Contains("header") && sb.ToString().EndsWith("}"))
                    {
                        sb.Append("}]");
                        source.SetResult(JsonConvert.DeserializeObject<T>(sb.ToString()));
                        return;
                    }
                }
                // at this point you have full response in sb
            }
        };
        await _client.ExecuteGetTaskAsync<T>(request);
        return await source.Task;
    }
    catch (Exception ex)
    {
        return default(T);
    }
}

简而言之,缓冲区已减少到 1 个字符,以便我们知道我们在字符串中的位置。然后为了将部分结果转换为有效的 JSON,我们检查对象的结尾,然后通过手动将额外的“}]”添加到结果并返回它来关闭它。

不错的 Evk!

【问题讨论】:

    标签: c# asp.net-mvc rest restsharp


    【解决方案1】:

    首先,http状态“部分内容”与您的情况无关。它用于响应带有 Range 标头的请求。

    您需要的是读取响应流,而不是等待完整响应被传递和反序列化。使用常规 HttpWebRequest 更容易做到这一点,但如果你想使用 RestSharp,它也是可能的。请注意,您必须手动反序列化 partial(因此,无效)json。这是一个草图:

        public static Task<T> ExecuteGetRequestAsync<T>(RestRequest request) where T : new() {
            var client = new RestClient("http://google.com");
            try {
                var source = new TaskCompletionSource<T>();
                request.ResponseWriter = (st) => {
                    using (var reader = new StreamReader(st)) {
                        var sb = new StringBuilder();
                        // read response 100 chars at a time
                        char[] buffer = new char[100];
                        int read = 0;
                        while ((read = reader.Read(buffer, 0, buffer.Length)) > 0) {
                            sb.Append(buffer, 0, read);
                            // now here you have your partial response
                            // you need to somehow parse it and feed to your view
                            // note that you should wait until you get some meaningful part, like first "header" element
                            // for example at some point there might be ["header":{"names":["te < partial response
                        }
                        // at this point you have full response in sb
                    }
                };
                client.ExecuteAsync<T>(request, null);
                return source.Task;
            }
            catch (Exception ex) {
                return null;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-08
      • 1970-01-01
      • 2015-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多