【问题标题】:Calling an API asynchronously and parsing JSON异步调用 API 并解析 JSON
【发布时间】:2015-11-08 11:40:44
【问题描述】:

我来自 iOS (Swift) 背景。在我的一个 Swift 应用程序中,我有一个调用 API 的类。我正在尝试将其移植到 C#(Windows 窗体应用程序),但遇到了一些障碍。首先是 Swift 代码。没有什么花哨。一个方法执行 POST 请求以登录 API,另一个函数执行 GET 方法以检索用户配置文件的 JSON 响应。这两种方法都是异步的。

import Foundation

class API {

    private let session = NSURLSession.sharedSession()
    private let baseURL = "https://www.example.com/api/"

    func login(userID userID: String, password: String, completion: (error: NSError?) -> ()) {
        let url = NSURL(string: baseURL + "login")!
        let params = ["username": userID, "password": password]
        let request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = "POST"
        request.encodeParameters(params) // encodeParameters is an extension method
        session.dataTaskWithRequest(request, completionHandler: { data, response, error in
            if let httpResponse = response as? NSHTTPURLResponse {
                if httpResponse.statusCode != 200 {
                    completion(error: error)
                } else {
                    completion(error: nil)
                }
            }
        }).resume()
    }

    func fetchUser(completion: (user: User?, error: NSError?) -> ()) {
        let url = NSURL(string: baseURL + "profile")!
        let request = NSURLRequest(URL: url)
        session.dataTaskWithRequest(request, completionHandler: { data, response, error in
            if let error = error {
                completion(user: nil, error: error)
            } else {
                // Parsing JSON
                var jsonDict = [String: AnyObject]()
                do {
                    jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
                } catch {
                    print("Error occurred parsing data: \(error)")
                    completion(user: nil, error: error)
                }

                let user = User()
                user.name = jsonDict["name"] as! String
                user.age = jsonDict["age"] as! Int

                completion(user: user, error: nil)
            }
        }).resume()
    }
}

这是我将其转换为 C# 的尝试。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;

namespace MyTrayApp
{
    public partial class Form1 : Form
    {
        private string baseURL = "https://www.example.com/api/";


        public Form1()
        {
            InitializeComponent();
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            await login("myusername", "mypassword");
            await fetchUser();
        }

        async Task login(string userID, string password)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(baseURL);
                var parameters = new Dictionary<string, string>
                {
                    { "username", userID },
                    { "password", password }
                };

                var encodedParameters = new FormUrlEncodedContent(parameters);
                var response = await client.PostAsync("login", encodedParameters);
                string responseString = await response.Content.ReadAsStringAsync();
                //Console.WriteLine(responseString);
            }
        }

        async Task fetchUser()
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(baseURL);
                var response = await client.GetAsync("profile");
                response.EnsureSuccessStatusCode();

                var responseString = await response.Content.ReadAsStringAsync();

                var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(responseString.ToCharArray()), new System.Xml.XmlDictionaryReaderQuotas());
                var root = XElement.Load(jsonReader);
                Console.WriteLine(root.XPathSelectElement("//name").Value);

                //Console.WriteLine(responseString);
            }
        }
    }
}

这些是我遇到的问题。

  1. 在我的 Swift 方法中,它们具有完成处理程序。如何在 C# 中做同样的事情?
  2. 在 Swift 中,你得到一个 NSData 对象,你可以将它传递给 NSJSONSerialization 来创建一个 JSON 对象。在我当前的实现中,我在XElement.Load(jsonReader); 得到了一个 XML 异常。我不确定这是否是正确的方法。我在 SO 上找到了很多不同的解决方案。但是有些是针对 Metro 应用程序的,有些是针对 Web 的,这一切都太过分了。此外,大多数解决方案都使用 JSON.NET 等第三方库。我正在尝试在没有第三方库的情况下实现这一目标。

【问题讨论】:

  • 托德说的话,大多数人只是使用 JSON.NET 作为事实上的 .net 解析器。但是,如果 3rd 方依赖真的是一个阻碍,有些人只是内联来自 SimpleJSON 的源代码simplejson.codeplex.com/SourceControl/latest
  • 另外,建议重用 HttpClient 的实例,因此只需将其声明为字段并在完成后将其释放。这将提供更好的性能,因为它允许您重用 HTTP 连接。

标签: c# json swift asynchronous dotnet-httpclient


【解决方案1】:

在我的 Swift 方法中,它们具有完成处理程序。我该怎么做 在 C# 中也一样?

连接完成处理程序的目的是让您在等待 HTTP 调用完成时不会占用线程。 async/await 的美妙之处在于您不必在 C# 中执行此操作。 await 关键字指示编译器将方法的其余部分逐字重写为回调。一旦遇到await,当前线程就会被释放,从而防止您的 UI 冻结。您已经正确编写了异步代码;即使它看起来是同步的,它也会异步运行。

你的第二个问题有点笼统,但我会提出两个建议:

  1. 在处理 JSON 数据时不要使用XElement。微软XML parsing library(其中之一)的那部分,与JSON无关。

  2. 我不确定为什么在没有第三方库的情况下实现这一点很重要。我知道人们有他们的理由,但特别是 Json.NET 已经变得如此流行和无处不在,以至于微软自己已经将它融入到他们的 ASP.NET MVC 和 Web API 框架中。也就是说,如果您必须避免它,here 是您仅使用 Microsoft 库来反序列化 JSON 的方式。

【讨论】:

    猜你喜欢
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    • 2019-06-20
    • 2016-06-04
    • 2020-08-07
    • 1970-01-01
    相关资源
    最近更新 更多