【问题标题】:Portable Class Library HttpClient可移植类库 HttpClient
【发布时间】:2014-06-26 11:06:03
【问题描述】:

对于我的一个项目,我想开发一个可用于不同平台(桌面、移动、Surface 等)的库。因此选择了可移植类库。

我正在开发一个使用 HttpClient 调用不同 API 调用的类。我对如何调用方法、响应和解决方法感到困惑。这是我的代码:-

    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) 我仅在 IsSuccessStatusCode 时通过 JObject 解析,那么我如何知道任何其他错误或消息以通知用户。 (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  

我尝试仅从 PCL 库中添加 System.Threading.Tasks,这给出了 7 个不同的错误。我哪里错了?怎样做才能使它工作?

请指导我。花费大量时间寻找最佳方式来开发可访问桌面应用程序和 Win Phone 应用程序的库。 任何帮助都非常感谢。谢谢。

【问题讨论】:

    标签: c# .net async-await dotnet-httpclient


    【解决方案1】:

    如果您在进行 http 调用时调用了 async api,您还应该将该异步端点公开给用户,而不是使用 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 为假,您可能会向调用代码抛出异常以显示出现问题。为此,您可以使用HttpResponseMessage.EnsureSuccessStatusCode,如果状态码 != 200 OK 则会引发异常。

    就个人而言,如果ExecuteGet 是一个公共API 方法,我绝对不会将它公开为JObject,而是一个强类型类型。

    【讨论】:

    • ExecuteGet 将只是一个方法,由该类的其他方法在内部调用以执行 et 或 Post API 调用。返回值的解析或创建要发送的数据将由其他公共方法(如 AgentStatus)完成。而从其他项目,这个库的 AgentStatus() 将被调用。这就是我打算设计的方式。老实说,我这样做只是为了避免为 Win Desktop & win Phone 应用程序编写两次相同的代码,并确保它适用于所有平台。这一切我的计划和想法都是正确的 - 我的意思是我的方向是正确的!
    • 如果 ExecuteGet 是内部的,那么你可以放弃我关于返回强类型的评论,其他一切都成立。此外,如果您的所有请求(get、post、put 等)都返回相同的响应类型,您可以将 ExecuteMethod 统一为接受字符串和方法类型的响应类型
    • 为所有 Get 调用统一一个方法并只解析其他方法是我的座右铭。好吧,我对此一无所知,但现在我坚持通过我的 GUI 项目调用 AgentStatus()。我已经更新了我上面的代码,请你看看它,如果可以的话帮助我。谢谢尤瓦尔。你能告诉我调用一个传递一些返回(Task)值的异步方法(AgentStatus)和一个通过我的GUI应用程序只返回Task而不是void的异步方法吗?
    • 至于你的第二个错误,尝试安装nuget.org/packages/Microsoft.Bcl.Async
    • 当调用agentStatus 时不要使用ConfigureAwait(false),因为它将继续在线程池线程上执行。只需await agentStatus()
    【解决方案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()
    

    同步和异步代码不能很好地协同工作,因此异步往往会在整个代码库中传播。

    【讨论】:

    • 感谢您的快速回复。我按照你的建议做了。如果我将 agentStatus() 也设为 asnyc,那么在从我的项目调用时,我将调用 API_Utility.agentStatus();该方法不会是异步的,所以这行得通吗?
    • @Tvd,同样的问题,只是上一级...理想情况下,调用agentStatus 的代码也应该是异步的。如果它是一个 void-returning 方法并且您不能更改返回类型(例如事件处理程序或方法覆盖),只需将其设为 async void(但一定要捕获发生的任何异常,否则应用程序将崩溃)
    • 你的意思是让像agentStatus这样的方法异步无效,对吧?或者使事件处理程序类型方法异步无效-如何使此方法异步?我真的不明白人们如何使用这个 HttpClient 来访问多个平台并调用它!!!想知道这是一个好主意还是会继续前进!!!。
    • @Tvd,不是 agentStatus;因为它返回一个值,所以它必须是async Task&lt;bool&gt;。但是如果它没有返回值,它的调用者可以是async void;但是,建议尽可能避免使用async void,并使用async Task 代替不返回任何内容的方法。如果您别无选择(例如在事件处理程序中),请仅使用 async void。看看这篇文章:msdn.microsoft.com/en-us/magazine/jj991977.aspx
    • @Tvd,如果使用 ConfigureAwait(false),则不会捕获同步上下文,因此继续在工作线程而不是 UI 线程上运行。不要在等待之后需要访问 UI 的方法中使用 ConfigureAwait(false)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多