У меня есть приложение ASP.NET MVC, которое использует RestSharp для подключения к третьей стороне, чтобы получить список доменных имен, а затем более подробную информацию о каждом домене позже в результате.Передача частичных ответов REST при их получении
Я считаю, что ответ приходит в частичных пакетах, и 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);
}
что тогда inpterprets ответ и дает клиенту соответствующие результаты поиска.
Это прекрасно работает в том смысле, что когда все данные были отправлены третьей стороной, объект возвращается в представление и заполняется соответствующим образом. Однако полное завершение запроса может занять до 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;
}
}
UPDATE
Работа на от ответа @Evk «s, вот что новый вызов вернуть частичный результат выглядит, в частности, для этого сценария:
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 полукокса, так что мы знаем, где мы находимся в ул ING. Затем, чтобы повернуть частичный результат в действительный JSON, мы проверяем конец объекта, а затем закрываем его, вручную добавляя дополнительный «)] к результату и возвращаем его.
Nice one Evk!