我有MVC项目,服务如下:
namespace comp.Services
{
public class CompService
{
public HttpClient client = new HttpClient();
public CompService()
{
client.BaseAddress = new Uri("someapiurl");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
protected async Task<string> GetProductAsync(string path)
{
var resp = "nothing here";
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
resp = await response.Content.ReadAsStringAsync();
}
return resp;
}
public string GetProduct(string path)
{
return GetProductAsync(path).GetAwaiter().GetResult();
}
}
}和actionResult查看:
namespace comp.Controllers
{
public class HomeController : Controller
{
public CompService compService;
public HomeController()
{
compService = new CompService();
}
public ActionResult About()
{
var timeServer = compService.GetProduct("/api/time");
ViewBag.timeServer = timeServer;
return View();
}
}
}当我在调试器中遇到这一行时:
HttpResponseMessage response = await client.GetAsync(path);程序从调试器退出,浏览器中没有响应。
在控制台应用程序中编写的相同代码可以工作。
在VS输出中是响应成功的消息:
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2018-02-18T13:48:31","tags":{"ai.internal.sdkVersion":"rddf:2.2.0-738","ai.internal.nodeName":"DESKTOP-xxxxx","ai.cloud.roleInstance":"DESKTOP-xxxxx"},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"/api/v1/time","id":"xxxxx=","data":"https://api.xxx.com/api/v1/time","duration":"00:00:01.3150000","resultCode":"200","success":true,"type":"Http","target":"xxx","properties":{"DeveloperMode":"true"}}}}在浏览器控制台输出:
[14:51:33 GMT+0100 (Central European Standard Time)] Browser Link: Failed to send message to browser link server:
Error: SignalR: Connection must be started before data can be sent. Call .start() before .send()谢谢你帮忙。
发布于 2018-02-18 14:12:51
您应该始终保持代码异步,而不是尝试混合同步和异步代码。
namespace comp.Services {
public class CompService {
static HttpClient client = new HttpClient();
static CompService() {
client.BaseAddress = new Uri("someapiurl");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<string> GetProductAsync(string path) {
var resp = string.Empty;
using(var response = await client.GetAsync(path)) {
if (response.IsSuccessStatusCode) {
resp = await response.Content.ReadAsStringAsync();
}
}
return resp;
}
}
}控制器操作也应该是异步的。
namespace comp.Controllers {
public class HomeController : Controller {
private CompService compService;
public HomeController() {
compService = new CompService();
}
public async Task<ActionResult> About() {
var timeServer = await compService.GetProductAsync("api/time");
ViewBag.timeServer = timeServer;
return View();
}
}
}也就是说,服务也应该是抽象的。
public interface ICompService {
Task<string> GetProductAsync(string path)
}
public class CompService : ICompService {
//...code removed for brevity
}然后注入控制器而不是手动创建它。
public class HomeController : Controller {
private ICompService compService;
public HomeController(ICompService compService) {
this.compService = compService;
}
public async Task<ActionResult> About() {
var timeServer = await compService.GetProductAsync("api/time");
ViewBag.timeServer = timeServer;
return View();
}
}发布于 2018-02-18 15:13:59
你现在陷入僵局了。与控制台应用程序不同,ASP.Net应用程序在同步上下文中运行。当您使用GetResult()进行阻塞时,将捕获该上下文。然后,在GetProductAsync中,等待被阻塞的上下文。在完成GetResult之前,它不能恢复,直到等待完成才能解决问题。
@NKosi的答案应该可以解决这个问题,您没有理由拥有任何同步代码。
只作示范
您可以通过显式地允许您的等待程序在不同的上下文上运行来破解代码。您不应该在生产中这样做,这不是一个修复。如果维护CompService的人不小心,它可能会失败。
若要等待不同的上下文,请更改以下内容:
var timeServer = await compService.GetProductAsync("api/time");对此:
var timeServer = await compService.GetProductAsync("api/time").ConfigureAwait(false);我提到这一点只是为了帮助您了解代码中正在发生的事情。不要用这种方式“修理”它,继续前进。
https://stackoverflow.com/questions/48852453
复制相似问题