当 API 关闭时,Xamarin 表单依赖注入 HttpClient 超时不起作用
Posted
技术标签:
【中文标题】当 API 关闭时,Xamarin 表单依赖注入 HttpClient 超时不起作用【英文标题】:Xamarin Forms Dependeny Injected HttpClient Timeout not working when API is down 【发布时间】:2021-10-03 16:36:15 【问题描述】:经过一些研究,我了解到为每个请求创建新的 HttpClients 对 HttpClient 的使用并不好。使用依赖注入是最好的解决方案之一。所以,我已经在我的 Xamarin Forms App 中实现了 DI,但我遇到了一个问题。我将客户端服务作为 Singleton 并将 httpclient 注入其中。如果 API 正在运行,则 httpclient 超时属性有效。不幸的是,当 API 未运行时,超时不起作用。即使我将 http 超时设置为 10 秒,也需要两分钟以上才会发生异常。我预计在设置的超时后会发生异常,并且会出现类似这样的消息,“无法建立连接,因为目标机器主动拒绝了它......”,但是直到两分钟以上才再次发生任何事情。但是,如果我创建一个新的 HttpClient,一切都会按预期工作。我已经在 Startup 文件和 Service 构造函数中初始化了 timeout 属性,但没有区别。当 API 未运行时,httpclient 的超时不起作用。我真的很感激这方面的任何帮助。请参阅下面我用作参考的文章的链接。如果您有任何问题,请告诉我。
提前谢谢你!!!
Xamarin Forms Dependency Injection Article
编辑:
Startup.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using Xamarin.Essentials;
namespace HttpClientTimeoutTest
public class Startup
public static IServiceProvider ServiceProvider get; set;
public static void Init()
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
c.AddCommandLine(new string[] $"ContentRoot=FileSystem.AppDataDirectory" );
)
.ConfigureServices((c, x) => ConfigureServices(c, x))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
services.AddHttpClient("", client =>
client.Timeout = TimeSpan.FromSeconds(10);
);
services.AddSingleton<IMyAppService, MyAppService>();
services.AddTransient<MainViewModel>();
MainViewModel.cs
using System;
using System.Windows.Input;
using Xamarin.Forms;
namespace HttpClientTimeoutTest
public class MainViewModel
public ICommand CallAPICommand
get
return new Command(async () =>
try
var races = await _appService.CallApiAsync();
catch (Exception ex)
);
IMyAppService _appService;
public MainViewModel(IMyAppService appService)
_appService = appService;
MyAppService.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace HttpClientTimeoutTest
public class MyAppService : IMyAppService
private readonly HttpClient httpClient;
public MyAppService(IHttpClientFactory httpClientFactory)
//public MyAppService(HttpClient client)
httpClient = httpClientFactory.CreateClient();
//httpClient = client;
public async Task<string> CallApiAsync()
//HttpClient client = new HttpClient();
var response = await httpClient.GetAsync("YOUR API CALL HERE");
if (response.IsSuccessStatusCode)
return await response.Content.ReadAsStringAsync();
return null;
IMyAppService.cs
using System.Threading.Tasks;
namespace HttpClientTimeoutTest
public interface IMyAppService
Task<string> CallApiAsync();
MainPage.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace HttpClientTimeoutTest
public partial class MainPage : ContentPage
public MainPage()
InitializeComponent();
BindingContext = Startup.ServiceProvider.GetService<MainViewModel>();
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HttpClientTimeoutTest.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Label Text="Start developing now" FontSize="Title" Padding="30,10,30,10"/>
<Label Text="Make changes to your XAML file and save to see your UI update in the running app with XAML Hot Reload. Give it a try!" FontSize="16" Padding="30,0,30,0"/>
<Label FontSize="16" Padding="30,24,30,0">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Learn more at "/>
<Span Text="https://aka.ms/xamarin-quickstart" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
<Button Command="Binding CallAPICommand" Text="Call API"/>
</StackLayout>
</ContentPage>
WebAPI HttpClientTimeoutTestController
using Microsoft.AspNetCore.Mvc;
using System.Threading;
namespace WebAPI.Controllers
[ApiController]
[Route("api/[controller]")]
public class HttpClientTimeoutTestController : ControllerBase
public HttpClientTimeoutTestController()
[HttpGet]
public IActionResult Get()
//Thread.Sleep(60000);
return Ok(true);
【问题讨论】:
请在您的问题正文中发布格式正确的相关代码。链接到异地回购违反了 SO 准则。请阅读How to Ask 获取指导。 我很抱歉。我已经用重现我的问题所需的重要代码修改了我的帖子。请让我知道我是否还有其他遗漏。谢谢。 ASP.NET 核心主机构建器的东西在这里绝对是矫枉过正。您可以使用 Xamarin.Forms 依赖服务注册单例。您可以使用静态类创建一个单例,您可以将您的 httpclient 放在 app.cs 文件中(它本身就是一个单例)。我个人为我的 xamarin 应用程序(Prism)使用 DI 容器,并将我的客户注册为单例,并且没有您描述的问题。我会想象主机构建器正在做一些时髦的事情,因为它不是为这个应用程序设计的。 哇。矫枉过正是轻描淡写。 :) 我对 XF 的内置 DependencyService 了解不多。谢谢你的参考。我所要做的就是使用 DependencyService 在 App.xaml.cs 文件中将我的服务注册为单例。 【参考方案1】:这是我的解决方案。所以我从我的项目中删除了 Startup.cs 文件,并简单地将我的服务注册为单例:
App.xaml.cs
using System;
using System.Net.Http;
using Xamarin.Forms;
namespace HttpClientTimeoutTest
public partial class App : Application
public HttpClient Client get; set;
public App()
InitializeComponent();
Client = new HttpClient();
Client.Timeout = TimeSpan.FromSeconds(10);
var appService = new MyAppService(Client);
DependencyService.RegisterSingleton<IMyAppService>(appService);
MainPage = new MainPage();
protected override void OnStart()
protected override void OnSleep()
protected override void OnResume()
然后,我将 Singleton 的值分配给要注入到 ViewModel 中的变量,如下所示:
MainPage.xaml.cs
using Xamarin.Forms;
namespace HttpClientTimeoutTest
public partial class MainPage : ContentPage
public MainPage()
InitializeComponent();
var service = DependencyService.Get<IMyAppService>();
BindingContext = new MainViewModel(service);
我已经对此进行了测试和重新测试,即使 API 未运行,我的超时也能正常工作。我也在我的“真实”应用程序中实现了这一点,一切都在那里工作。包括正在注入的 httpclient 上的标头标记。如果这可以进一步简化,请告诉我,但到目前为止一切都很好。再次感谢@Axemasta。
【讨论】:
以上是关于当 API 关闭时,Xamarin 表单依赖注入 HttpClient 超时不起作用的主要内容,如果未能解决你的问题,请参考以下文章
使用 Prism 在 Xamarin Forms 的后台服务中实现依赖注入
xamarin 形式的跨平台移动应用程序(iOS、Android)中的依赖注入