在旧版本的ASP.NET MVC应用程序中创建HttpClient,无需启动项。

5 浏览
0 Comments

在旧版本的ASP.NET MVC应用程序中创建HttpClient,无需启动项。

我有一个旧版本的ASP.NET MVC应用程序,没有Startup.cs。我想要实现一种清晰的方式来使用HttpClient来进行与第三方API的调用。

根据一些我收到的想法/建议,我已经做了一些工作。问题是,当我进行API调用时,它没有任何结果。我将其放在了try catch中,但我甚至都没有得到异常。API提供商告诉我,他们没有看到搜索参数。

首先,我创建了一个用于延迟加载的HttpClientAccessor

public static class HttpClientAccessor
{
   public static Func ValueFactory = () =>
   {
      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;
   }
}

0
0 Comments

在这个问题中,出现的原因是旧的ASP.NET MVC应用程序没有使用依赖注入,因此在没有初始化的情况下不需要使用组合根。

解决方法是使用延迟单例方法。可以使用一个Lazy单例方法来实现。在需要使用客户端的地方调用服务,客户端将在首次使用时进行初始化,并在后续调用实例时获得相同的实例。

在控制器中使用时,代码中混合了异步调用和阻塞调用,如.Wait()或.Result。这可能会导致死锁,应该避免使用。

代码应该在整个过程中都是异步的。

对于HttpClient来说,建议在应用程序的生命周期内使用一个实例。使用多个实例并对其进行处理会导致性能问题。

在解决方案中,可以创建一个"通用"的HttpClient,然后从ApiClient中访问它。

当控制器操作设置为async后,代码开始正常工作。

在BaseAddress中不需要使用斜杠,因为它的路径为空。

参考Async/Await - Best Practices in Asynchronous Programming。

以上是关于创建旧的ASP.NET MVC应用程序中HttpClient的文章。

0
0 Comments

问题的原因是使用了错误的异步代码。使用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#文章,详细了解单例模式的实现。

0
0 Comments

在较旧的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)

0