没有来自HttpResponseMessage的回复

时间:2018-02-18 13:56:09

标签: c# asp.net-mvc web

我有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();
        }
    }
}

在debuger中我遇到这一行:

HttpResponseMessage response = await client.GetAsync(path);

程序从debuger退出,浏览器中没有响应。

在控制台应用程序中编写的相同代码有效。

在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()

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

您应该始终保持代码异步,而不是尝试混合同步和异步代码

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;
        }
    }
}

控制器操作也应该是async

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();
    }
}

参考Async/Await - Best Practices in Asynchronous Programming

答案 1 :(得分:2)

你得到的是一个僵局。与控制台应用程序不同,ASP.Net应用程序在同步上下文中运行。使用GetResult()阻止时捕获该上下文。然后,在GetProductAsync中,等待被阻止的上下文。它在GetResult完成之前无法恢复,在等待完成之前无法解决。

@NKosi的答案应该可以解决问题,你没有理由拥有任何同步代码。

仅供演示

您可以通过明确允许等待在不同的上下文中运行来 hack 您的代码。你不应该在生产中这样做,这不是一个修复。如果维护CompService的人不小心,可能会失败。

等待不同的背景改变:

 var timeServer = await compService.GetProductAsync("api/time");

对此:

 var timeServer = await compService.GetProductAsync("api/time").ConfigureAwait(false);

我之所以提到这一点,只是为了帮助您了解代码中发生的情况。不要以这种方式“修复”并继续前进。