在旧版本的ASP.NET MVC应用程序中创建HttpClient,无需启动项。
在旧版本的ASP.NET MVC应用程序中创建HttpClient,无需启动项。
我有一个旧版本的ASP.NET MVC应用程序,没有Startup.cs
。我想要实现一种清晰的方式来使用HttpClient
来进行与第三方API的调用。
根据一些我收到的想法/建议,我已经做了一些工作。问题是,当我进行API调用时,它没有任何结果。我将其放在了try catch
中,但我甚至都没有得到异常。API提供商告诉我,他们没有看到搜索参数。
首先,我创建了一个用于延迟加载的HttpClientAccessor
。
public static class HttpClientAccessor { public static FuncValueFactory = () => { var client = new HttpClient(); client.BaseAddress = new Uri("https://apiUrl.com"); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1"); client.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2"); return client; }; private static Lazy client = new Lazy (ValueFactory); public static HttpClient HttpClient { get { return client.Value; } } }
然后,我创建了自己的API客户端,以便我可以将API调用函数放在一个地方,代码如下:
public class MyApiClient { public async Task GetSomeData() { var client = HttpClientAccessor.HttpClient; try { var result = await client.GetStringAsync("somedata/search?text=test"); var output = JObject.Parse(result); } catch(Exception e) { var error = e.Message; } } }
然后在我的ASP.NET Controller动作中,我这样做:
public class MyController : Controller { private static readonly MyApiClient _apiClient = new MyApiClient (); public ActionResult ApiTest() { var data = _apiClient.GetSomeData().Wait(); } }
有任何想法我哪里出错了吗?
更新:
这种简单的方法可以正常工作:
public class MyController : Controller { private static readonly HttpClient _client = new HttpClient(); public ActionResult ApiTest() { _client.BaseAddress = new Uri("https://apiUrl.com"); _client.DefaultRequestHeaders.Accept.Clear(); _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _client.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1"); _client.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2"); var response = _client.GetStringAsync("somedata/search?text=test").Result; } }
在这个问题中,出现的原因是旧的ASP.NET MVC应用程序没有使用依赖注入,因此在没有初始化的情况下不需要使用组合根。
解决方法是使用延迟单例方法。可以使用一个Lazy单例方法来实现。在需要使用客户端的地方调用服务,客户端将在首次使用时进行初始化,并在后续调用实例时获得相同的实例。
在控制器中使用时,代码中混合了异步调用和阻塞调用,如.Wait()或.Result。这可能会导致死锁,应该避免使用。
代码应该在整个过程中都是异步的。
对于HttpClient来说,建议在应用程序的生命周期内使用一个实例。使用多个实例并对其进行处理会导致性能问题。
在解决方案中,可以创建一个"通用"的HttpClient,然后从ApiClient中访问它。
当控制器操作设置为async后,代码开始正常工作。
在BaseAddress中不需要使用斜杠,因为它的路径为空。
参考Async/Await - Best Practices in Asynchronous Programming。
以上是关于创建旧的ASP.NET MVC应用程序中HttpClient的文章。
问题的原因是使用了错误的异步代码。使用Task.Wait()
和异步MyApiClient.GetSomeData()
在ASP.NET请求上下文中导致了死锁。这是一个非常常见的问题,可以在StackOverflow上的An async/await example that causes a deadlock找到更多信息。使用Task.Result
属性调用的代码是正常工作的,可能是因为HttpClient.GetStringAsync()
采取了预防死锁的措施。请参阅MSDN上的Task.ConfigureAwait()
页面和StackOverflow上的Best practice to call ConfigureAwait for all server-side code讨论了解更多信息。
有多种选项可以在C#中编写单例。请参阅Jon Skeet的Implementing the Singleton Pattern in C#文章,详细了解单例模式的实现。
在较旧的ASP.NET MVC应用程序中创建HttpClient而不使用Startup的问题是由于对HttpClient实例在应用程序启动时创建的原因引起的。然而,实际上HttpClient是一种"资源",您可以在需要使用它时才创建它,并且不需要将其设置为"Singleton"。可以将它包装在using块中即可。如果要创建API包装器作为Singleton,可以在APICaller类中实现相应的逻辑。
HttpClient应该是一个静态资源,在应用程序生命周期内重复使用。官方文档中的说明也支持了这一点。通过正确使用HttpClient,可以避免在高负载下耗尽可用套接字的问题,从而导致SocketException错误。
关于如何正确使用HttpClient,微软的官方文档中提供了一些示例和建议。通过使用HttpClientFactory来实现弹性的HTTP请求可以解决这个问题。具体的实现方法可以参考相关文档。
参考链接:
- [msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.110).aspx](https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.110).aspx)
- [learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests)