Тупик при регистрации http-запросов в основном пользовательском интерфейсе c#

В моем приложении WinForms я хочу видеть необработанные данные запроса / ответа, когда он взаимодействует с webApi. Мне нужно показать запрос / ответ в пользовательском интерфейсе RichTextBoxes. Для этого я настроил свой HttpClient следующим образом:

        private HttpClient client;
        private void CreateService()
        {
            client = new HttpClient(new LoggingHandler(new HttpClientHandler(), this))
            {
                BaseAddress = new Uri(this._URI)
            };
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
        }

Мой обработчик журналов:

    public class LoggingHandler : DelegatingHandler
    {
        private SimplySignRestClientAsync _client;

        public LoggingHandler(HttpMessageHandler innerHandler, SimplySignRestClientAsync client) : base(innerHandler)
        {
            this._client = client;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            string p_Request = "";
            if (_client._updateRequest != null)
            {
                p_Request = request.ToString();
                if (request.Content != null)
                {
                    p_Request += "\n\nContent:\n" + FormatJsonString(await request.Content.ReadAsStringAsync());    
                }

                //_client._request.Text = p_Request; << this works but causes a cross-thread exception in debug only
                SetText(p_Request); << This deadlocks the UI
            }

            return response;
        }

        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            if (_client._request.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                _client._response.Invoke(d, new object[] { text });
            }
            else
            {
                _client._request.Text = text;
            }
        }
    }

Если я пытаюсь напрямую получить доступ к richtextbox, он работает только тогда, когда я запускаю приложение без отладки. Но во время отладки возникает исключение между потоками.

Вот как я вызываю httpClient:

//This is how the original call gets initiated from UI
private void uiLogin_Click(object sender, EventArgs e)
{
   Agent resp = Client.Login(new Credentials(uiUsername.Text, uiPassword.Text));
   uiAuthToken.Text = Client.getAuthToken();
}

//this method has to stay synchronous, but it in turn calls an async method below
public Agent Login(Credentials loginInfo)
{
    var task = Task.Run(async () => await LoginAsync(loginInfo));
    return task.Result.Content;  <<< this is where the application stops if i hit pause during debug when deadlock happens
}

//
async public Task<RestResponse<Agent>> LoginAsync(Credentials loginInfo)
{
    HttpResponseMessage response = await client.PostAsJsonAsync(this._URI + "api/users/login", loginInfo);

    var content = await response.Content.ReadAsStringAsync();
    RestResponse<Agent> respAgent respAgent = JsonConvert.DeserializeObject<RestResponse<Agent>>(content);

    return respAgent;
}

насколько я понимаю, мой "return task.Result.Content" блокирует поток пользовательского интерфейса и "_client._response.Invoke (d, new object [] {text});" ожидает разблокировки, вызывая тупик. Но я не уверен, как заставить invoke ждать.

Проверьте, как синхронно запускать асинхронный код stackoverflow.com/questions/5095183/…

cellik 10.08.2018 17:09
1
1
72
1

Ответы 1

Если вы хотите сделать это с использованием правильных асинхронных конструкций, вам необходимо сделать точку входа (ваш обработчик событий Click) асинхронной и никогда не блокировать асинхронный код, поскольку он вызывает тупиковую ситуацию, с которой вы столкнулись.

//This is how the original call gets initiated from UI
private async void uiLogin_Click(object sender, EventArgs e)
{
   Agent resp = await Client.Login(new Credentials(uiUsername.Text, uiPassword.Text));
   uiAuthToken.Text = Client.getAuthToken();
}

public async Task<Agent> Login(Credentials loginInfo)
{
    var result = await LoginAsync(loginInfo);
    return result.Content;
}

Функция входа (учетные данные loginInfo) должна оставаться синхронной.

AnKing 10.08.2018 17:03

@AnKing Почему? Невозможно сделать это правильно с помощью синхронного метода.

Camilo Terevinto 10.08.2018 17:04

потому что я использую выходы req / resp в testApp. Основное приложение использует все синхронные вызовы. Я тоже хотел имитировать синхронные звонки в тестовом приложении

AnKing 10.08.2018 17:10

@AnKing Вы, вероятно, подключаетесь к онлайн-сервису для обработки входа в систему, да? Это означает, что у вас есть ровно два варианта. A] вы обрабатываете вход в систему асинхронно, или B] вы блокируете поток пользовательского интерфейса (он же замораживаете все приложение), пока вы ждете ответа онлайн-службы.

Abion47 10.08.2018 17:45

Другие вопросы по теме