HttpClient.SendAsync 即使发送的数据已处理,也会坐以待毙
HttpClient.SendAsync sits and never progress, even though the data sent is processed
我不得不将应用程序从 ASP.Net Core
重写为 ASP.Net 4.6.1
,在应用程序的核心版本中,我将 Json
数据发送到 BugZilla 进行处理,它工作正常。它发送数据并相当快地得到响应。应用程序 4.6.1 版本中的相同代码位于并挂在发送命令上。我可以进入 BugZilla 并看到它获取了数据并生成了错误,但即使在 15 分钟后应用程序也没有进展。我是 Task 和通过 Json 发送数据的新手,所以我不确定为什么 运行 会遇到这个问题。不会返回任何错误,并且在调试模式下我可以单步执行所有操作并知道它正在处理并在 SendAsync
行等待。我做错了什么在 4.6.1 中打破了这个?
private static async Task<int> LogToBugZilla(string Component, Exception ex)
{
var ErrorSummary = "Auto Error Logging: " + ex.Message + " " + ex.InnerException.Message;
var BugData = new Dictionary<string, string>
{
{ "Bugzilla_api_key", ConfigurationManager.AppSettings["BugZillaAPIKey"] },
{ "product", "Job Site" },
{ "component", Component },
{ "version", ConfigurationManager.AppSettings["AppVersion"] },
{ "summary", ErrorSummary },
{ "op_sys", "All" },
{ "platform", "Other" },
{ "description", ex.StackTrace + ex.InnerException.StackTrace }
};
string Json = JsonConvert.SerializeObject(BugData, Formatting.None);
var Client = new HttpClient();
var Request = new HttpRequestMessage(HttpMethod.Post, "http://bugzilla/rest/bug");
Request.Content = new StringContent(Json, Encoding.UTF8, "application/json");
Request.Headers.Add("Accept", "application/json");
var Response = await Client.SendAsync(Request);
var JsonResults = await Response.Content.ReadAsStringAsync();
if (Response.StatusCode == HttpStatusCode.OK)
{
var Results = JsonConvert.DeserializeObject<Dictionary<string, int>>(JsonResults);
return Results["id"];
}
else
{
return -1;
}
}
编辑:
public static string ProcessError(Exception ex, string OriginatingController)
{
var Component = GetComponent(OriginatingController);
string UserErrorMessage = "";
switch (ex.GetType().ToString())
{
case "System.Data.DataException":
LogToITSupport("Database Connection");
UserErrorMessage = @"An error has accrued connecting to the database. <a href=""mailto:"">IT Support</a> has been notified of this error. Please try your request again later, thank you.";
break;
default:
var BugID = LogToBugZilla(Component, ex).Result;
UserErrorMessage = @"An error has accrued processing your request and a log of this error has been made on the <a href=""http://bugzilla/show_bug.cgi?id=" + BugID + "\">Internal Tools BugZilla system</a> with an ID of " + BugID + ". Please try your request again later, thank you.";
break;
}
return UserErrorMessage;
}
重申一下,应用程序在代码 var Response = await Client.SendAsync(Request);
上挂起
编辑 2:
控制器代码:
public ActionResult About()
{
try
{
using (var db = new JobSightDbContext())
{
var model = db.ReleaseVersions
.OrderByDescending(rv => rv.DateReleased)
.Select(rv => new ReleaseNotesVM()
{
ID = rv.ID,
CurrentVersion = string.Concat(rv.Major, ".", rv.Minor, ".", rv.Patch),
CurrentVersionReleaseDate = rv.DateReleased,
ReleaseNotes = rv.ReleaseNotes.Select(rn => rn.Note).ToList()
}).First();
//model.VersionList = db.ReleaseVersions
// .OrderByDescending(rv => rv.DateReleased)
// .Select(rv => new SelectListItem()
// {
// Value = rv.ID.ToString(),
// Text = string.Concat(rv.Major, '.', rv.Minor, '.', rv.Patch)
// }).ToList();
return View(model);
}
}
catch (System.Data.DataException ex)
{
ViewBag.ErrorMessage = EHALM.ProcessError(ex, RouteData.Values["controller"].ToString());
return View("Dashboard");
}
}
这是我用来发送 HTTP 请求的一些通用代码。工作正常,唯一不同的是我使用的是 .Net 4.5。对于 4.6
应该不是问题
private async Task<HttpUtilResponse> DoRequest(List<RequestResponseHeader> headers, HttpMethod method, string url, string requestData) {
HttpUtilResponse response = new HttpUtilResponse();
string responseData = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) {
using (HttpRequestMessage request = new HttpRequestMessage(method, url)) {
if (headers != null) {
headers.ForEach(h => request.Headers.Add(h.Name, h.Value));
}
if (!string.IsNullOrEmpty(requestData)) {
if (AppConfig.LogRequestContent) {
LOG.Debug(requestData);
}
request.Content = new StringContent(requestData, Encoding.UTF8, JSON_CONTENT);
}
using (HttpResponseMessage msg = await client.SendAsync(request, CancelToken.Token)) {
response.StatusCode = msg.StatusCode;
response.ReasonPhrase = msg.ReasonPhrase;
using (HttpContent content = msg.Content) {
response.ResponseText = await content.ReadAsStringAsync();
}
}
}
}
return response;
}
我要在这里继续跛行并说你在你的调用堆栈中像这样使用 LogToBugZilla
:
public void DoStuff()
{
var result = LogToBugZilla("Component", exception).Result;
}
这实际上会使您的代码陷入僵局,这也是 you shouldn't block on async code 的原因。您需要将代码 "async all the way" 放到调用堆栈的顶部。这意味着将 DoStuff
转换为 DoStuffAsync
并像这样调用它:
public async Task DoStuffAsync()
{
var result = await LogToBugZillaAsync("Component", exception);
// Do stuff with result
}
请注意,我根据推荐的编码约定将 "async" 后缀添加到 LogToBugZilla
。
编辑:
鉴于您提供了调用方法,它应该如下所示:
public static async Task<string> ProcessErrorAsync(
Exception ex, string OriginatingController)
{
var Component = GetComponent(OriginatingController);
string UserErrorMessage = "";
switch (ex.GetType().ToString())
{
case "System.Data.DataException":
LogToITSupport("Database Connection");
UserErrorMessage = @"An error has accrued connecting to the
database. <a href=""mailto:"">IT Support</a> has been notified
of this error. Please try your request again later, thank you.";
break;
default:
var bugId = await LogToBugZillaAsync(Component, ex);
UserErrorMessage = @"An error has accrued processing your request
and a log of this error has been made
on the <a href=""http://bugzilla/show_bug.cgi?id=" + BugID +
"\">Internal Tools BugZilla system</a>
with an ID of " + bugId + ". Please try your request again later, thank you.";
break;
}
return UserErrorMessage;
}
现在记住这需要一直向上。这意味着每个调用 ProcessErrorAsync
的方法本身都应该是 async
和 return 一个 Task
或 Task<T>
.
我不得不将应用程序从 ASP.Net Core
重写为 ASP.Net 4.6.1
,在应用程序的核心版本中,我将 Json
数据发送到 BugZilla 进行处理,它工作正常。它发送数据并相当快地得到响应。应用程序 4.6.1 版本中的相同代码位于并挂在发送命令上。我可以进入 BugZilla 并看到它获取了数据并生成了错误,但即使在 15 分钟后应用程序也没有进展。我是 Task 和通过 Json 发送数据的新手,所以我不确定为什么 运行 会遇到这个问题。不会返回任何错误,并且在调试模式下我可以单步执行所有操作并知道它正在处理并在 SendAsync
行等待。我做错了什么在 4.6.1 中打破了这个?
private static async Task<int> LogToBugZilla(string Component, Exception ex)
{
var ErrorSummary = "Auto Error Logging: " + ex.Message + " " + ex.InnerException.Message;
var BugData = new Dictionary<string, string>
{
{ "Bugzilla_api_key", ConfigurationManager.AppSettings["BugZillaAPIKey"] },
{ "product", "Job Site" },
{ "component", Component },
{ "version", ConfigurationManager.AppSettings["AppVersion"] },
{ "summary", ErrorSummary },
{ "op_sys", "All" },
{ "platform", "Other" },
{ "description", ex.StackTrace + ex.InnerException.StackTrace }
};
string Json = JsonConvert.SerializeObject(BugData, Formatting.None);
var Client = new HttpClient();
var Request = new HttpRequestMessage(HttpMethod.Post, "http://bugzilla/rest/bug");
Request.Content = new StringContent(Json, Encoding.UTF8, "application/json");
Request.Headers.Add("Accept", "application/json");
var Response = await Client.SendAsync(Request);
var JsonResults = await Response.Content.ReadAsStringAsync();
if (Response.StatusCode == HttpStatusCode.OK)
{
var Results = JsonConvert.DeserializeObject<Dictionary<string, int>>(JsonResults);
return Results["id"];
}
else
{
return -1;
}
}
编辑:
public static string ProcessError(Exception ex, string OriginatingController)
{
var Component = GetComponent(OriginatingController);
string UserErrorMessage = "";
switch (ex.GetType().ToString())
{
case "System.Data.DataException":
LogToITSupport("Database Connection");
UserErrorMessage = @"An error has accrued connecting to the database. <a href=""mailto:"">IT Support</a> has been notified of this error. Please try your request again later, thank you.";
break;
default:
var BugID = LogToBugZilla(Component, ex).Result;
UserErrorMessage = @"An error has accrued processing your request and a log of this error has been made on the <a href=""http://bugzilla/show_bug.cgi?id=" + BugID + "\">Internal Tools BugZilla system</a> with an ID of " + BugID + ". Please try your request again later, thank you.";
break;
}
return UserErrorMessage;
}
重申一下,应用程序在代码 var Response = await Client.SendAsync(Request);
编辑 2:
控制器代码:
public ActionResult About()
{
try
{
using (var db = new JobSightDbContext())
{
var model = db.ReleaseVersions
.OrderByDescending(rv => rv.DateReleased)
.Select(rv => new ReleaseNotesVM()
{
ID = rv.ID,
CurrentVersion = string.Concat(rv.Major, ".", rv.Minor, ".", rv.Patch),
CurrentVersionReleaseDate = rv.DateReleased,
ReleaseNotes = rv.ReleaseNotes.Select(rn => rn.Note).ToList()
}).First();
//model.VersionList = db.ReleaseVersions
// .OrderByDescending(rv => rv.DateReleased)
// .Select(rv => new SelectListItem()
// {
// Value = rv.ID.ToString(),
// Text = string.Concat(rv.Major, '.', rv.Minor, '.', rv.Patch)
// }).ToList();
return View(model);
}
}
catch (System.Data.DataException ex)
{
ViewBag.ErrorMessage = EHALM.ProcessError(ex, RouteData.Values["controller"].ToString());
return View("Dashboard");
}
}
这是我用来发送 HTTP 请求的一些通用代码。工作正常,唯一不同的是我使用的是 .Net 4.5。对于 4.6
应该不是问题private async Task<HttpUtilResponse> DoRequest(List<RequestResponseHeader> headers, HttpMethod method, string url, string requestData) {
HttpUtilResponse response = new HttpUtilResponse();
string responseData = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) {
using (HttpRequestMessage request = new HttpRequestMessage(method, url)) {
if (headers != null) {
headers.ForEach(h => request.Headers.Add(h.Name, h.Value));
}
if (!string.IsNullOrEmpty(requestData)) {
if (AppConfig.LogRequestContent) {
LOG.Debug(requestData);
}
request.Content = new StringContent(requestData, Encoding.UTF8, JSON_CONTENT);
}
using (HttpResponseMessage msg = await client.SendAsync(request, CancelToken.Token)) {
response.StatusCode = msg.StatusCode;
response.ReasonPhrase = msg.ReasonPhrase;
using (HttpContent content = msg.Content) {
response.ResponseText = await content.ReadAsStringAsync();
}
}
}
}
return response;
}
我要在这里继续跛行并说你在你的调用堆栈中像这样使用 LogToBugZilla
:
public void DoStuff()
{
var result = LogToBugZilla("Component", exception).Result;
}
这实际上会使您的代码陷入僵局,这也是 you shouldn't block on async code 的原因。您需要将代码 "async all the way" 放到调用堆栈的顶部。这意味着将 DoStuff
转换为 DoStuffAsync
并像这样调用它:
public async Task DoStuffAsync()
{
var result = await LogToBugZillaAsync("Component", exception);
// Do stuff with result
}
请注意,我根据推荐的编码约定将 "async" 后缀添加到 LogToBugZilla
。
编辑:
鉴于您提供了调用方法,它应该如下所示:
public static async Task<string> ProcessErrorAsync(
Exception ex, string OriginatingController)
{
var Component = GetComponent(OriginatingController);
string UserErrorMessage = "";
switch (ex.GetType().ToString())
{
case "System.Data.DataException":
LogToITSupport("Database Connection");
UserErrorMessage = @"An error has accrued connecting to the
database. <a href=""mailto:"">IT Support</a> has been notified
of this error. Please try your request again later, thank you.";
break;
default:
var bugId = await LogToBugZillaAsync(Component, ex);
UserErrorMessage = @"An error has accrued processing your request
and a log of this error has been made
on the <a href=""http://bugzilla/show_bug.cgi?id=" + BugID +
"\">Internal Tools BugZilla system</a>
with an ID of " + bugId + ". Please try your request again later, thank you.";
break;
}
return UserErrorMessage;
}
现在记住这需要一直向上。这意味着每个调用 ProcessErrorAsync
的方法本身都应该是 async
和 return 一个 Task
或 Task<T>
.