如何将异步 post 转换为同步?
How can I transform my aysnc post to sync?
我编写了一个 C# 程序,它异步地向供应商发出 API 调用以生成提交令牌。然后将此令牌附加到任何形式的 post 有效负载以验证其完整性。它作为独立的 .Net 4.6.1 应用程序运行良好。我 运行 遇到的问题是将其集成到我的 CMS 中。在与 CMS 支持人员交谈后,他们要求我将其改为同步操作。我在转换代码时遇到问题。特别是关于 POST 在我检索到令牌后编辑表单的部分。
这里是我原来写的源码。显然 AppID 和 secrets 已经被删除。
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
namespace FormSubmission
{
public partial class _Default : Page
{
protected async void Page_Load(object sender, EventArgs e)
{
var start = new Start();
await start.KickOff();
}
public class Start
{
public async Task<String> KickOff()
{
var auth = new Authentication();
var token = await auth.Authenticate();
// return await Task.FromResult(token);
// //Task.FromResult(token);
if (!String.IsNullOrEmpty(token))
{
var form = new FormSubmission();
var formFields = new Form();
formFields.createMethod = "SubmitForm";
formFields.email = "email@email.com";
formFields.mobile = "555-555-5555";
formFields.remark = "Hello, hope this works";
formFields.attr.attr3 = "mnc";
formFields.attr.attr6 = "1000";
formFields.attr.attr10 = "first name";
formFields.attr.attr11 = "lastname";
formFields.attr.attr14 = "City";
formFields.attr.attr15 = "State";
formFields.attr.attr18 = "USA";
formFields.attr.attr25 = "USA";
formFields.attr.attr28 = "Newsletter";
var serializer = JsonSerializer.Create();
var optionsString = new StringBuilder();
var writer = new StringWriter(optionsString);
serializer.Serialize(writer, formFields);
await form.Submit(token, optionsString.ToString());
}
return await Task.FromResult("");
}
}
public class Authentication
{
private const string appId = "XXXXXXXXXXXXX";
private const string secret = "XXXXXXXXXXXXXXXXXXXXXX";
public async Task<String> Authenticate()
{
string url = "https://api-url-goes-here";
string token = "";
try
{
using (var client = new HttpClient())
{
var responseMessage = await client.GetAsync(url + appId + "&secret=" + secret);
var content = await responseMessage.Content.ReadAsStringAsync();
var contentObject = JObject.Parse(content);
token = contentObject["access_token"].ToString();
return token;
};
}
catch (Exception e)
{
throw new Exception("Access token not found");
}
}
}
public class FormSubmission
{
public async Task Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = await client.PostAsync(
"https://api-url-goes-here/access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
var content = await message.Content.ReadAsStringAsync();
Console.Write(content);
}
}
}
}
}
当 运行在与我的 CMS 的 .Net 版本相匹配的新 .Net 项目中时,此代码非常有效。但是,他们再次告诉我它不支持异步操作(这看起来很奇怪,但我显然不是一个优秀的 C# 开发人员)
所以我重写了它。
public class Attr
{
public string attr3;
public string attr6;
public string attr10;
public string attr11;
public string attr14;
public string attr15;
public string attr18;
public string attr25;
public string attr28;
}
public class FormSubmission
{
public string mobile;
public string email;
public string remark;
public string createMethod;
public Attr attr = new Attr();
}
protected void Page_Load(object sender, EventArgs e)
{
string token = fetchToken();
var formFields = new FormSubmission();
formFields.createMethod = "SubmitForm";
formFields.email = "email@email.com";
formFields.mobile = "5555555555";
formFields.remark = "Non sync";
formFields.attr.attr3 = "mnc";
formFields.attr.attr6 = "1000";
formFields.attr.attr10 = "first name";
formFields.attr.attr11 = "last name";
formFields.attr.attr14 = "City";
formFields.attr.attr15 = "State";
formFields.attr.attr18 = "USA";
formFields.attr.attr25 = "USA";
formFields.attr.attr28 = "Newsletter";
var serializer = JsonSerializer.Create();
var optionsString = new StringBuilder();
var writer = new StringWriter(optionsString);
serializer.Serialize(writer, formFields);
var x = new submitForm();
x.Submit(token, optionsString.ToString());
}
protected string fetchToken()
{
string appId = "xxxxxxxxxxxxxxx";
string secret = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
string url = "https://api-url-goes-here/security/accesstoken?grant_type=client_credentials&appid=";
string token;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + appId + "&secret=" + secret);
request.Method = "GET";
request.KeepAlive = false;
request.ContentType = "appication/json";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string myResponse = "";
using (System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream()))
{
myResponse = sr.ReadToEnd();
}
JObject jobj = JObject.Parse(myResponse);
token = jobj["access_token"].ToString();
return token;
}
catch (Exception e)
{
return e.ToString();
}
}
public class submitForm
{
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
}
如您所见,应该是相同的代码。我在 submitForm class 上的提交方法中 运行 的问题。当我 运行 我的程序并打开 fiddler 时,我可以看到获取令牌的请求。并收到令牌。 submitForm class 给我带来了问题。我什至没有在 fiddler 中看到 POST 请求。谁能帮我吗?
这是给我带来问题的 class。
public class submitForm
{
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
}
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
这与您预期的不同,因为 message
现在是 Task
,不是您希望的响应,因为您没有等到 PostAsync
完成。
要使其同步,您有两种选择:
- 呼叫
.GetAwaiter().GetResult()
就可以了。它可以在您之前使用 await
的任何地方使用,并且不存在该方法的同步版本。例如:
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync(
"https://api-url-goes-here/access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
var content = message.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Console.Write(content);
}
}
描述了使用 .GetAwaiter().GetResult()
而不是 .Result
的原因 here(主要是因为抛出异常的方式)。
或者,
- 使用
HttpWebRequest
而不是 HttpClient
发出相同的请求,就像您在代码的其他部分所做的那样,因此您不必处理仅异步方法。
这可能不是您想听到的,但您不应该将异步方法转换为同步方法。您可能很幸运,当您使用 GetAwaiter().GetResult()
时它们可以工作,但存在死锁风险或者它们无法正常工作。您可以使用 Task.Run
然后 GetAwaiter().GetResult()
运行 它们,但这会旋转另一个线程,因此效率不高,并且您的代码仍在阻塞。使它比同步代码慢,因为它必须旋转另一个线程。另外,它肯定会分配 gc 必须处理的更多内存。
您还应该使用 Async
后缀命名您的异步方法。
我编写了一个 C# 程序,它异步地向供应商发出 API 调用以生成提交令牌。然后将此令牌附加到任何形式的 post 有效负载以验证其完整性。它作为独立的 .Net 4.6.1 应用程序运行良好。我 运行 遇到的问题是将其集成到我的 CMS 中。在与 CMS 支持人员交谈后,他们要求我将其改为同步操作。我在转换代码时遇到问题。特别是关于 POST 在我检索到令牌后编辑表单的部分。
这里是我原来写的源码。显然 AppID 和 secrets 已经被删除。
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
namespace FormSubmission
{
public partial class _Default : Page
{
protected async void Page_Load(object sender, EventArgs e)
{
var start = new Start();
await start.KickOff();
}
public class Start
{
public async Task<String> KickOff()
{
var auth = new Authentication();
var token = await auth.Authenticate();
// return await Task.FromResult(token);
// //Task.FromResult(token);
if (!String.IsNullOrEmpty(token))
{
var form = new FormSubmission();
var formFields = new Form();
formFields.createMethod = "SubmitForm";
formFields.email = "email@email.com";
formFields.mobile = "555-555-5555";
formFields.remark = "Hello, hope this works";
formFields.attr.attr3 = "mnc";
formFields.attr.attr6 = "1000";
formFields.attr.attr10 = "first name";
formFields.attr.attr11 = "lastname";
formFields.attr.attr14 = "City";
formFields.attr.attr15 = "State";
formFields.attr.attr18 = "USA";
formFields.attr.attr25 = "USA";
formFields.attr.attr28 = "Newsletter";
var serializer = JsonSerializer.Create();
var optionsString = new StringBuilder();
var writer = new StringWriter(optionsString);
serializer.Serialize(writer, formFields);
await form.Submit(token, optionsString.ToString());
}
return await Task.FromResult("");
}
}
public class Authentication
{
private const string appId = "XXXXXXXXXXXXX";
private const string secret = "XXXXXXXXXXXXXXXXXXXXXX";
public async Task<String> Authenticate()
{
string url = "https://api-url-goes-here";
string token = "";
try
{
using (var client = new HttpClient())
{
var responseMessage = await client.GetAsync(url + appId + "&secret=" + secret);
var content = await responseMessage.Content.ReadAsStringAsync();
var contentObject = JObject.Parse(content);
token = contentObject["access_token"].ToString();
return token;
};
}
catch (Exception e)
{
throw new Exception("Access token not found");
}
}
}
public class FormSubmission
{
public async Task Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = await client.PostAsync(
"https://api-url-goes-here/access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
var content = await message.Content.ReadAsStringAsync();
Console.Write(content);
}
}
}
}
}
当 运行在与我的 CMS 的 .Net 版本相匹配的新 .Net 项目中时,此代码非常有效。但是,他们再次告诉我它不支持异步操作(这看起来很奇怪,但我显然不是一个优秀的 C# 开发人员)
所以我重写了它。
public class Attr
{
public string attr3;
public string attr6;
public string attr10;
public string attr11;
public string attr14;
public string attr15;
public string attr18;
public string attr25;
public string attr28;
}
public class FormSubmission
{
public string mobile;
public string email;
public string remark;
public string createMethod;
public Attr attr = new Attr();
}
protected void Page_Load(object sender, EventArgs e)
{
string token = fetchToken();
var formFields = new FormSubmission();
formFields.createMethod = "SubmitForm";
formFields.email = "email@email.com";
formFields.mobile = "5555555555";
formFields.remark = "Non sync";
formFields.attr.attr3 = "mnc";
formFields.attr.attr6 = "1000";
formFields.attr.attr10 = "first name";
formFields.attr.attr11 = "last name";
formFields.attr.attr14 = "City";
formFields.attr.attr15 = "State";
formFields.attr.attr18 = "USA";
formFields.attr.attr25 = "USA";
formFields.attr.attr28 = "Newsletter";
var serializer = JsonSerializer.Create();
var optionsString = new StringBuilder();
var writer = new StringWriter(optionsString);
serializer.Serialize(writer, formFields);
var x = new submitForm();
x.Submit(token, optionsString.ToString());
}
protected string fetchToken()
{
string appId = "xxxxxxxxxxxxxxx";
string secret = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
string url = "https://api-url-goes-here/security/accesstoken?grant_type=client_credentials&appid=";
string token;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + appId + "&secret=" + secret);
request.Method = "GET";
request.KeepAlive = false;
request.ContentType = "appication/json";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string myResponse = "";
using (System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream()))
{
myResponse = sr.ReadToEnd();
}
JObject jobj = JObject.Parse(myResponse);
token = jobj["access_token"].ToString();
return token;
}
catch (Exception e)
{
return e.ToString();
}
}
public class submitForm
{
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
}
如您所见,应该是相同的代码。我在 submitForm class 上的提交方法中 运行 的问题。当我 运行 我的程序并打开 fiddler 时,我可以看到获取令牌的请求。并收到令牌。 submitForm class 给我带来了问题。我什至没有在 fiddler 中看到 POST 请求。谁能帮我吗?
这是给我带来问题的 class。
public class submitForm
{
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
}
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync("https://api-url-goes-here/v1/customers?access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json"));
}
}
这与您预期的不同,因为 message
现在是 Task
,不是您希望的响应,因为您没有等到 PostAsync
完成。
要使其同步,您有两种选择:
- 呼叫
.GetAwaiter().GetResult()
就可以了。它可以在您之前使用await
的任何地方使用,并且不存在该方法的同步版本。例如:
public void Submit(string token, string json)
{
using (var client = new HttpClient())
{
var message = client.PostAsync(
"https://api-url-goes-here/access_token=" + token,
new StringContent(json, Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
var content = message.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Console.Write(content);
}
}
描述了使用 .GetAwaiter().GetResult()
而不是 .Result
的原因 here(主要是因为抛出异常的方式)。
或者,
- 使用
HttpWebRequest
而不是HttpClient
发出相同的请求,就像您在代码的其他部分所做的那样,因此您不必处理仅异步方法。
这可能不是您想听到的,但您不应该将异步方法转换为同步方法。您可能很幸运,当您使用 GetAwaiter().GetResult()
时它们可以工作,但存在死锁风险或者它们无法正常工作。您可以使用 Task.Run
然后 GetAwaiter().GetResult()
运行 它们,但这会旋转另一个线程,因此效率不高,并且您的代码仍在阻塞。使它比同步代码慢,因为它必须旋转另一个线程。另外,它肯定会分配 gc 必须处理的更多内存。
您还应该使用 Async
后缀命名您的异步方法。