WPF中自托管Web API遇请求超时问题求解决方案
WPF中自托管Web API超时问题的解决方案
问题根源分析
你的实现存在三个关键问题,直接导致请求超时:
- UI线程死锁:在
MainWindow构造函数中同步调用异步方法GetCall(),WPF的UI线程是单线程上下文,await会试图回到UI线程继续执行,但此时构造函数已经阻塞了UI线程,导致请求一直无法完成,最终触发超时。 - 中间件与路由顺序错误:
UseWebApi在UseCors之前调用,导致CORS中间件无法正确生效;同时属性路由配置在默认路由之后,不符合路由优先级规则。 - 未持有WebApp实例:
WebApp.Start返回的IDisposable未被保存,垃圾回收可能会回收服务器进程,导致请求无响应。
修复步骤与代码示例
1. 修正UI线程异步调用逻辑
不要在构造函数中直接调用异步方法,改用Loaded事件处理异步操作,并持有WebApp实例防止被GC回收:
using System; using System.Net.Http; using System.Threading.Tasks; using System.Windows; using Microsoft.Owin.Hosting; namespace WpfApp3 { public partial class MainWindow : Window { private readonly string _baseAddress = "http://localhost:4444/"; private IDisposable _webAppHost; public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; } private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { // 启动自托管API并持有实例,防止被GC回收 _webAppHost = WebApp.Start<StartUp>(_baseAddress); // 异步调用API,避免阻塞UI线程 await CallDemoApi(); } private async Task CallDemoApi() { using (HttpClient client = new HttpClient()) { try { var response = await client.GetStringAsync($"{_baseAddress}api/demo"); // 处理返回结果,如需更新UI请使用Dispatcher // Dispatcher.Invoke(() => { ResultTextBlock.Text = response; }); } catch (Exception ex) { // 异常处理 Console.WriteLine(ex.Message); } } } // 窗口关闭时释放API主机资源 protected override void OnClosed(EventArgs e) { _webAppHost?.Dispose(); base.OnClosed(e); } } }
2. 修正StartUp类的中间件与路由顺序
调整CORS中间件和WebAPI的顺序,同时确保属性路由优先配置:
using Owin; using System.Web.Http; namespace WpfApp3 { public class StartUp { public void Configuration(IAppBuilder appBuilder) { // 先启用CORS中间件,确保请求被正确拦截 appBuilder.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); HttpConfiguration config = new HttpConfiguration(); config.EnableCors(); // 优先配置属性路由,再配置默认路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); // 最后挂载WebAPI中间件 appBuilder.UseWebApi(config); } } }
替代方案:使用ASP.NET Core自托管
旧的OWIN自托管已逐渐被ASP.NET Core替代,后者性能更好、生态更完善,更适合WPF场景:
using System; using System.Net.Http; using System.Threading.Tasks; using System.Windows; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace WpfApp3 { public partial class MainWindow : Window { private readonly string _baseAddress = "http://localhost:4444/"; private IHost _apiHost; public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; } private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { // 构建ASP.NET Core主机 _apiHost = Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseUrls(_baseAddress); webBuilder.ConfigureServices(services => { services.AddControllers(); services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); }); webBuilder.Configure(app => { app.UseCors("AllowAll"); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }); }) .Build(); // 启动主机 await _apiHost.StartAsync(); await CallDemoApi(); } private async Task CallDemoApi() { using (HttpClient client = new HttpClient()) { try { var response = await client.GetStringAsync($"{_baseAddress}api/demo"); // 处理结果 } catch (Exception ex) { Console.WriteLine(ex.Message); } } } protected override async void OnClosed(EventArgs e) { if (_apiHost != null) { await _apiHost.StopAsync(); _apiHost.Dispose(); } base.OnClosed(e); } } }
内容的提问来源于stack exchange,提问作者praveena H M
相关产品推荐
相关产品推荐

