背景:我正在开发一个涉及WinForms应用程序的项目。客户希望通过REST API(或类似方式)公开本地HTTP服务器,以允许其他应用程序触发正在运行的WinForms应用程序实例上的功能。首选是使用ASP.NET Core来实现上述API。
我的问题是:如何在同一进程中结构化一个项目,使其具有ASP.NET Core API和WinForms GUI?是否有任何需要注意的潜在问题?
背景:我正在开发一个涉及WinForms应用程序的项目。客户希望通过REST API(或类似方式)公开本地HTTP服务器,以允许其他应用程序触发正在运行的WinForms应用程序实例上的功能。首选是使用ASP.NET Core来实现上述API。
我的问题是:如何在同一进程中结构化一个项目,使其具有ASP.NET Core API和WinForms GUI?是否有任何需要注意的潜在问题?
在Windows窗体应用程序中托管ASP.NET CORE API并与窗体进行交互
这是一个基本的逐步示例,演示如何创建一个项目,在Windows窗体应用程序内托管ASP.NET CORE API,并执行一些与窗体的交互。
要这样做,请按照以下步骤进行:
创建一个名为 MyWinFormsApp
的Windows窗体应用程序。
在设计模式下打开 Form1
,并在其上放置一个 TextBox
。
在设计器中更改 textBox1
的 Modifiers
属性为 Public
并保存它。
安装 Microsoft.AspNetCore.Mvc
包。
安装 Microsoft.AspNetCore
包。
在项目的根目录下创建一个 Startup.cs
文件,并复制以下代码:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace MyWinFormsApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
}
将以下代码复制到 Program.cs
中:
using System;
using System.Threading;
using System.Windows.Forms;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace MyWinFormsApp
{
public class Program
{
public static Form1 MainForm { get; private set; }
[STAThread]
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().RunAsync();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
Application.Run(MainForm);
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
在项目根目录下创建一个名为Controllers
的文件夹。
在Controllers
文件夹中创建一个名为ValuesController.cs
的文件,并将以下代码复制到文件中:
using System;
using Microsoft.AspNetCore.Mvc;
namespace MyWinFormsApp.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
string text = "";
Program.MainForm.Invoke(new Action(() =>
{
text = Program.MainForm.textBox1.Text;
}));
return text;
}
[HttpGet("{id}")]
public ActionResult Get(string id)
{
Program.MainForm.Invoke(new Action(() =>
{
Program.MainForm.textBox1.Text = id;
}));
return Ok();
}
}
}
运行该应用程序。
在 textBox1
中输入“hi”。
打开浏览器并浏览 http://localhost:5000/api/values → 您将看到响应中的 hi
。
http://localhost:5000/api/values/bye → 您将在 textBox1
中看到 bye
。
进一步阅读
您可能还对如何在Windows窗体(WinForms)中使用依赖注入(DI)感兴趣。
public async Task Listen(IServiceCollection services, List<Type> controllers, int port, CancellationToken token)
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options => options.Listen(System.Net.IPAddress.Parse("127.0.0.1"), port));
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.Add(services);
//builder.Services.AddSingleton<HostedCheckoutService>(hc);
// Add our external card processor controller. This mocks a web page that is returned
// in the Hosted Checkout iframe.
//var assembly = typeof(LocalCardProcessorController).Assembly;
foreach (var controller in controllers)
{
var assembly = controller.Assembly;
builder.Services.AddControllers().PartManager.ApplicationParts.Add(new Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart(assembly));
}
var app = builder.Build();
//Configure the HTTP request pipeline.
if (true)//app.Environment.IsDevelopment())
{
// Need to manually navigate to http://127.0.0.1:16600/swagger/index.html
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "MyAPI V1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());
await app.RunAsync(token);
await app.DisposeAsync();
}
在你的表单中这样调用它;
APIServer server = new APIServer();
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<SomeService1>();
serviceCollection.AddSingleton<SomeService2>();
server.Listen(serviceCollection, new() { typeof(SomeController1), typeof(SomeController2) }, 9001, new CancellationToken());
请注意,Swagger正在使用Swashbuckle.AspNetCore(6.3.0)
Microsoft.AspNetCore.Mvc
和 Microsoft.AspNetCore
包但无法使用。dotnet add package Microsoft.AspNetCore.App --version 2.2.8
Microsoft.AspNetCore.Builder.WebApplication app = Microsoft.AspNetCore.Builder.WebApplication.Create(new string[] { });
app.MapGet("/", () => "Hello World!");
app.RunAsync();
好的,现在你可以打开http:localhost:5000
来查看Hello World!
因为很少有人在WinForm中使用参数,所以这里省略了string[] args
,并使用new string[] { }
代替
如果您想使用控制器,就像ASP.NET Core一样,那么您可以使用以下内容:
var builder = Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder(new string[]{});
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.RunAsync();
HomeController.cs
using Microsoft.AspNetCore.Mvc;
namespace RuyutWinFormsApi;
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("This is a test api");
}
}
您可以打开 http://localhost:5000/api/home
查看 这是一个测试API
顺便说一句,如果您想更改端口,可以添加以下内容:
app.Urls.Add("http://0.0.0.0:8080");