异步调用 API 并解析 JSON
Calling an API asynchronously and parsing JSON
我来自 iOS (Swift) 背景。在我的一个 Swift 应用程序中,我有一个调用 API 的 class。我正在尝试将其移植到 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);
}
}
}
}
这些是我遇到的问题。
- 在我的 Swift 方法中,它们有完成处理程序。我怎样才能在 C# 中做同样的事情?
- 在Swift中,你得到一个
NSData
对象,你可以将它传递给NSJSONSerialization
来创建一个JSON对象。在我当前的实现中,我在 XElement.Load(jsonReader);
处得到一个 XML 异常。我什至不确定这是否是正确的方法。我在这里找到了大量不同的解决方案。但有些是针对 Metro 应用程序的,有些是针对 Web 的,这一切都让人不知所措。此外,大多数解决方案都使用第三方库,如 JSON.NET。我试图在没有第三方库的情况下实现这一目标。
In my Swift methods, they have completion handlers. How can I do the
same in C#?
连接完成处理程序的目的是让您在等待 HTTP 调用完成时不会占用线程。 async
/await
的美妙之处在于您不必在 C# 中执行此操作。 await
关键字指示编译器按字面意义将方法的其余部分重写为回调。一旦遇到 await
,当前线程就会被释放,防止您的 UI 冻结。您已经正确编写了异步代码;即使它看起来是同步的,它也会异步运行。
你的第二个问题有点笼统,不过我提两个建议:
处理 JSON 数据时不要使用 XElement
。微软的那部分XML parsing library(其中之一)与JSON.
无关
我不确定为什么在没有第 3 方库的情况下实现这一点很重要。我知道人们有他们的理由,但特别是 Json.NET 已经变得如此流行和无处不在,以至于微软本身已经将其融入他们的 ASP.NET MVC 和 Web API 框架中。也就是说,如果您必须避免它,here 就是您仅使用 Microsoft 库反序列化 JSON 的方式。
我来自 iOS (Swift) 背景。在我的一个 Swift 应用程序中,我有一个调用 API 的 class。我正在尝试将其移植到 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);
}
}
}
}
这些是我遇到的问题。
- 在我的 Swift 方法中,它们有完成处理程序。我怎样才能在 C# 中做同样的事情?
- 在Swift中,你得到一个
NSData
对象,你可以将它传递给NSJSONSerialization
来创建一个JSON对象。在我当前的实现中,我在XElement.Load(jsonReader);
处得到一个 XML 异常。我什至不确定这是否是正确的方法。我在这里找到了大量不同的解决方案。但有些是针对 Metro 应用程序的,有些是针对 Web 的,这一切都让人不知所措。此外,大多数解决方案都使用第三方库,如 JSON.NET。我试图在没有第三方库的情况下实现这一目标。
In my Swift methods, they have completion handlers. How can I do the same in C#?
连接完成处理程序的目的是让您在等待 HTTP 调用完成时不会占用线程。 async
/await
的美妙之处在于您不必在 C# 中执行此操作。 await
关键字指示编译器按字面意义将方法的其余部分重写为回调。一旦遇到 await
,当前线程就会被释放,防止您的 UI 冻结。您已经正确编写了异步代码;即使它看起来是同步的,它也会异步运行。
你的第二个问题有点笼统,不过我提两个建议:
处理 JSON 数据时不要使用
XElement
。微软的那部分XML parsing library(其中之一)与JSON. 无关
我不确定为什么在没有第 3 方库的情况下实现这一点很重要。我知道人们有他们的理由,但特别是 Json.NET 已经变得如此流行和无处不在,以至于微软本身已经将其融入他们的 ASP.NET MVC 和 Web API 框架中。也就是说,如果您必须避免它,here 就是您仅使用 Microsoft 库反序列化 JSON 的方式。