Портативная библиотека классов HttpClient

Для одного из моих проектов я хочу разработать библиотеку, которая может использоваться на разных платформах (Desktop, Mobile, Surface и т. Д.). Поэтому выбрали Porable Class Library.

Я разрабатываю класс для вызова различных вызовов API с использованием HttpClient. Я застрял с тем, как вызвать метод, ответ и обойти. Это мой код:-

    public static async Task<JObject> ExecuteGet(string uri)
    {
        using (HttpClient client = new HttpClient())
        {
            // TODO - Send HTTP requests
            HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Get, uri);
            reqMsg.Headers.Add(apiIdTag, apiIdKey);
            reqMsg.Headers.Add(apiSecretTag, ApiSecret);
            reqMsg.Headers.Add("Content-Type", "text/json");
            reqMsg.Headers.Add("Accept", "application/json");

            //response = await client.SendAsync(reqMsg);
            //return response;

            //if (response.IsSuccessStatusCode)
            //{
                string content = await response.Content.ReadAsStringAsync();
                return (JObject.Parse(content));
            //}
        }
    }

    // Perform AGENT LOGIN Process
    public static bool agentStatus() {
        bool loginSuccess = false;

        try
        {
            API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Wait();
            // ACCESS Response, JObject ???
        }
        catch
        {
        }
        finally
        {
        }

Как и ExecuteGet, я также создам для ExecutePost. Мой запрос от ExecuteGet, если (1) я передаю JObject при разборе только при IsSuccessStatusCode, то как я могу узнать о любых других ошибках или сообщениях, чтобы сообщить пользователю. (2) Если я передаю ответ, то как мне назначить его здесь?

response = API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Wait();  

это дает ошибку.

Каков наилучший подход к этой ситуации? И я должен вызвать несколько API, поэтому разные API будут иметь разные наборы результатов.

Кроме того, можете ли вы подтвердить, что проектируя этот способ и добавив ссылку на PCL, я смогу получить доступ к нескольким проектам.

ОБНОВЛЕНИЕ:- Как упомянуто ниже в 2 ответах, я обновил свой код. Как указано в приведенной ссылке, я звоню из другого проекта. Это мой код:-

Портативная библиотека классов:-

    private static HttpRequestMessage getGetRequest(string url)
    {
        HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Get, url);
        reqMsg.Headers.Add(apiIdTag, apiIdKey);
        reqMsg.Headers.Add(apiSecretTag, ApiSecret);
        reqMsg.Headers.Add("Content-Type", "text/json");
        reqMsg.Headers.Add("Accept", "application/json");

        return reqMsg;
    }

    // Perform AGENT LOGIN Process
    public static async Task<bool> agentStatus() {
        bool loginSuccess = false;
        HttpClient client = null;
        HttpRequestMessage request = null;

        try
        {
            client = new HttpClient();
            request = getGetRequest("http://api.mintchat.com/agent/autoonline");
            response = await client.SendAsync(request).ConfigureAwait(false);

            if (response.IsSuccessStatusCode)
            {
                string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                JObject o = JObject.Parse(content);
                bool stat = bool.Parse(o["status"].ToString());

                ///[MainAppDataObject sharedAppDataObject].authLogin.chatStatus = str;
                o = null;
            }
            loginSuccess = true;

        }
        catch
        {
        }
        finally
        {
            request = null;
            client = null;
            response = null;
        }

        return loginSuccess;
    }

Из другого проекта WPF в событии щелчка btn я вызываю это следующим образом:

    private async void btnSignin_Click(object sender, RoutedEventArgs e)
   {
         /// Other code goes here
         // ..........

            agent = doLogin(emailid, encPswd);
            if (agent != null)
            {
                //agent.OnlineStatus = getAgentStatus();

                // Compile Error at this line
                bool stat = await MintWinLib.Helpers.API_Utility.agentStatus();

                ... 

Я получаю эти 4 ошибки:-

Error   1   Predefined type 'System.Runtime.CompilerServices.IAsyncStateMachine' is not defined or imported D:\...\MiveChat\CSC 
Error   2   The type 'System.Threading.Tasks.Task`1<T0>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f89d50a3a'.   D:\...\Login Form.xaml.cs   97  21  
Error   3   Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly?   D:\...\Login Form.xaml.cs   97  33  
Error   4   Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly?   D:\...\Login Form.xaml.cs   47  28  

Я попытался добавить System.Threading.Tasks только из библиотеки PCL, что дало 7 разных ошибок. Куда я иду не так? Что нужно сделать, чтобы это работало?

Пожалуйста, объясните мне это. Потратьте много часов на то, чтобы понять, как лучше всего разработать библиотеку, доступную для настольного приложения и приложения Win Phone. Любая помощь очень признательна. Благодарю.

2 ответа

Решение

Если вы хотите получить результат задачи, вам нужно использовать Result имущество:

var obj = API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Result;

Однако обычно не стоит ожидать синхронного завершения асинхронного метода, поскольку это может привести к взаимоблокировке. Лучший подход заключается в await метод:

var obj = await API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline");

Обратите внимание, что вам нужно сделать вызывающий метод async также:

public static async Task<bool> agentStatus()

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

Если вы позвоните async api при выполнении вызовов http, вы также должны предоставить эту асинхронную конечную точку пользователю, а не блокировать запрос, используя Task.Wait,

Также при создании сторонней библиотеки рекомендуется использовать ConfigureAwait(false) чтобы избежать тупиков, когда вызывающий код пытается получить доступ к Result собственность или Wait метод. Вы также должны следовать рекомендациям и пометить любой асинхронный метод Async поэтому метод должен быть вызван ExecuteStatusAsync

public static Task<bool> AgentStatusAsync() 
{
    bool loginSuccess = false;

    try
    {
        // awaiting the task will unwrap it and return the JObject
        var jObject = await API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").ConfigureAwait(false);

    }
    catch
    {
    }
}

И внутри ExecuteGet:

response = await client.SendAsync(reqMsg).ConfigureAwait(false);
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

В случае IsSuccessStatusCode Значение false, вы можете выдать исключение в вызывающий код, чтобы показать, что что-то пошло не так. Для этого вы можете использовать HttpResponseMessage.EnsureSuccessStatusCode который выдает исключение, если код состояния!= 200 OK.

Лично, если ExecuteGet это публичный метод API, я бы определенно не выставлял его как JObject но строго типизированный тип.

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