简体   繁体   中英

Portable Class Library HttpClient

For one of my projects I want to develop a library that can be used in different platforms (Desktop, Mobile, Surface, etc). Hence have opted Porable Class Library.

I am developing a class for calling different API calls' using HttpClient. I am stuck with how to call the method, response and work around. This is my code :-

    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
        {
        }

Like ExecuteGet, I will also create for ExecutePost. My query is from ExecuteGet, if (1) I pass JObject on parsing when IsSuccessStatusCode only, then how can I know about any other errors or messages to inform the user. (2) If I pass response, then how do I assign it here

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

that is giving error.

What would be the best approach to handle this situation ? And I got to call multiple API's, so different API will have different result sets.

Also, can you confirm that designing this way and adding PCL reference I will be able to access in multiple projects.

UPDATE :- As mentioned in below 2 answers I have updated my code. As mentioned in the provided link I am calling the from the other project. This is my code :-

Portable Class Library :-

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

From the other WPF project, in a btn click event I am calling this as follows :-

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

                ... 

I get these 4 errors :-

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  

I tried adding System.Threading.Tasks from the PCL library only, that gave 7 different errors. Where am I going wrong ? What to do to make this working ?

Please guide me on this. Have spend lots of hours figuring the best to develop a library accessible to desktop app & Win Phone app. Any help is highly appreciative. Thanks.

If you call an async api when making the http calls, you should also expose that async endpoint to the user, and not block the request using Task.Wait .

Also, when creating a third party library, it is recommanded to use ConfigureAwait(false) to avoid deadlocks when the calling code tries to access the Result property or the Wait method. You should also follow guidelines and mark any async method with Async , so the method should be called 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
    {
    }
}

And inside ExecuteGet :

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

In case IsSuccessStatusCode is false, you may throw an exception to the calling code to show something went wrong. To do that, you can use the HttpResponseMessage.EnsureSuccessStatusCode which throws an exception if the status code != 200 OK.

Personally, if ExecuteGet is a public API method i would definitely not expose it as a JObject but a strongly typed type.

If you want the result of the task, you need to use the Result property:

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

However, it's usually not a good idea to wait synchronously for an async method to complete, because it can cause deadlocks. The better approach is to await the method:

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

Note that you need to make the calling method async as well:

public static async Task<bool> agentStatus()

Sync and async code don't play together very well, so async tends to propagate across the whole code base.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM