From d453c312f2098d906a2b54d863ec77b239e95bd4 Mon Sep 17 00:00:00 2001 From: bicijinlian Date: Thu, 6 Jun 2024 00:17:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/1.5.总结.ipynb | 12 +- Docs/1.6.测试.ipynb | 110 ++++++++++-------- .../WebApiConfigExtensions.cs | 2 +- .../Utilities/DotnetCommondUtility.cs | 83 +++++++++++++ .../UtilitiesTest/DotnetCommondUtilityTest.cs | 13 ++- HttpClientStudy.WebApp/Program.cs | 86 +++++++------- .../appsettings.Development.json | 2 +- HttpClientStudy.WebApp/appsettings.json | 2 +- HttpClientStudy.WebClient/Program.cs | 20 ++-- .../appsettings.Development.json | 2 +- HttpClientStudy.WebClient/appsettings.json | 2 +- 11 files changed, 217 insertions(+), 117 deletions(-) diff --git a/Docs/1.5.总结.ipynb b/Docs/1.5.总结.ipynb index 7cb2415..f46bfad 100644 --- a/Docs/1.5.总结.ipynb +++ b/Docs/1.5.总结.ipynb @@ -33,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" @@ -45,15 +45,7 @@ "languageId": "polyglot-notebook" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello World!\r\n" - ] - } - ], + "outputs": [], "source": [ "#! csharp\n", "Console.WriteLine(\"Hello World!\");" diff --git a/Docs/1.6.测试.ipynb b/Docs/1.6.测试.ipynb index cbdaa72..afae443 100644 --- a/Docs/1.6.测试.ipynb +++ b/Docs/1.6.测试.ipynb @@ -33,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" @@ -45,51 +45,7 @@ "languageId": "polyglot-notebook" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ������ D �еľ�û�б�ǩ��\r\n", - " �������к��� BB7D-E2E7\r\n", - "\r\n", - " d:\\������Ŀ\\ѧϰ��Ŀ\\HttpClientStudy\\Docs ��Ŀ¼\r\n", - "\r\n", - "2024/05/30 23:53 .\r\n", - "2024/05/28 23:06 ..\r\n", - "2024/05/28 23:36 .vscode\r\n", - "2024/05/28 23:36 4,778 1.0��Ŀ����.dib\r\n", - "2024/05/28 23:15 7,855 1.1.����.ipynb\r\n", - "2024/05/30 23:53 27,025 1.2.ʹ��׼��.ipynb\r\n", - "2024/05/31 00:16 5,497 1.3.0.����ʹ��.�����ͻ���.ipynb\r\n", - "2024/05/26 23:55 1,053 1.3.1.����ʹ��.��������.ipynb\r\n", - "2024/05/26 23:55 965 1.3.2.����ʹ��.ʹ��������.ipynb\r\n", - "2024/05/26 23:55 965 1.3.3.����ʹ��.������Ӧ.ipynb\r\n", - "2024/05/26 23:55 965 1.3.4.����ʹ��.��������.ipynb\r\n", - "2024/05/26 23:55 965 1.3.5.����ʹ��.ʹ�ô���.ipynb\r\n", - "2024/05/26 23:55 965 1.3.6.����ʹ��.ʹ��Json.ipynb\r\n", - "2024/05/26 23:55 965 1.3.7.����ʹ��.ʹ��Cookie.ipynb\r\n", - "2024/05/26 23:55 965 1.4.0.�߼�ʹ��.����.ipynb\r\n", - "2024/05/26 23:55 965 1.4.1.�߼�ʹ��.��ʼ��.ipynb\r\n", - "2024/05/26 23:55 965 1.4.2.�߼�ʹ��.���ӳ�.ipynb\r\n", - "2024/05/26 23:55 965 1.4.3.�߼�ʹ��.�ظ�ʹ��.ipynb\r\n", - "2024/05/26 23:55 965 1.4.4.�߼�ʹ��.ʹ�ùܵ�.ipynb\r\n", - "2024/05/26 23:55 965 1.4.5.�߼�ʹ��.���ͻ��ͻ���.ipynb\r\n", - "2024/05/26 23:55 965 1.4.6..�߼�ʹ��.����ģʽ.ipynb\r\n", - "2024/05/26 23:55 965 1.4.7.�߼�ʹ��.Polly.ipynb\r\n", - "2024/05/28 00:31 1,496 1.5.�ܽ�.ipynb\r\n", - "2024/05/28 23:33 3,204 1.6.����.ipynb\r\n", - "2024/05/28 23:06 4,854 2.1.�ں��еĸ���·��.ipynb\r\n", - "2024/05/26 23:55 Assets\r\n", - "2024/05/28 01:12 Publish\r\n", - "2024/05/28 23:06 112 ѧϰ.ps1\r\n", - "2024/05/26 23:55 181 ˵��.md\r\n", - " 24 ���ļ� 69,565 �ֽ�\r\n", - " 5 ��Ŀ¼ 383,550,734,336 �����ֽ�\r\n", - "\r\n" - ] - } - ], + "outputs": [], "source": [ "//引用项目\n", "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n", @@ -104,7 +60,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 启动项目" + "## 2、Powershell 管理项目" ] }, { @@ -173,8 +129,68 @@ "else {\n", " $WebAppProc.Kill();\n", " Write-Host \"$WebAppProcName 进程已退出\"\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3、C#类库管理项目" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "//启动已发布的WebApi项目\n", + "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n", + "\n", + "{\n", + " var file = System.IO.Path.GetFullPath(\"./Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe\",System.Environment.CurrentDirectory);\n", + "\n", + " file.Display();\n", + " var message = HttpClientStudy.Core.Utilities.DotnetCommondUtility.RunWebApp(file);\n", + "\n", + " Console.WriteLine(message);\n", "}" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "//关闭项目进程\n", + "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n", + "{\n", + " var message = HttpClientStudy.Core.Utilities.DotnetCommondUtility.SopWebApp();\n", + " Console.WriteLine(message);\n", + "}\n" + ] } ], "metadata": { diff --git a/HttpClientStudy.Config/WebApiConfigExtensions.cs b/HttpClientStudy.Config/WebApiConfigExtensions.cs index f4853b8..23c45d5 100644 --- a/HttpClientStudy.Config/WebApiConfigExtensions.cs +++ b/HttpClientStudy.Config/WebApiConfigExtensions.cs @@ -33,7 +33,7 @@ namespace HttpClientStudy.Config public static IConfigurationBuilder AddWebApiConfiguration(this IConfigurationBuilder configuration) { //配置文件名称 - configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); configuration.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true); //配置文件根目录:配置项目Dll文件所在目录(被其它项目引用时,引用后dll的文件所在目录) string configDellPath = Path.GetDirectoryName(typeof(WebApiConfig).Assembly.Location) ?? Directory.GetCurrentDirectory(); diff --git a/HttpClientStudy.Core/Utilities/DotnetCommondUtility.cs b/HttpClientStudy.Core/Utilities/DotnetCommondUtility.cs index 6a65322..8dfa8fa 100644 --- a/HttpClientStudy.Core/Utilities/DotnetCommondUtility.cs +++ b/HttpClientStudy.Core/Utilities/DotnetCommondUtility.cs @@ -65,5 +65,88 @@ namespace HttpClientStudy.Core.Utilities return output; } + + + /// + /// 新窗口启动WebApp项目 + /// + /// 包含全路径的WebApp项目 + /// + public static string RunWebApp(string startFile) + { + string output = ""; + + try + { + ProcessStartInfo startInfo = new ProcessStartInfo() + { + FileName = "powershell.exe", + Arguments = $"Start-Process -FilePath '{startFile}'", + UseShellExecute = false, + CreateNoWindow = false, + }; + + + Process process = new Process() + { + StartInfo = startInfo, + }; + + process.Start(); + + output = "新窗口启动WebApp项目,已有实例在运行则窗口会自动关闭"; + + } + catch (Exception ex) + { + output = $"An error occurred: {ex.Message}"; + } + + return output; + } + + /// + /// 启动新窗口,执行指定文件 + /// + /// 包含全路径的可执行文件 + /// + public static string SopWebApp() + { + string output = ""; + + try + { + string projectAndMutexName = WebApiConfigManager.GetWebApiConfigOption().CurrentValue.WebAppMutexName; + + ProcessStartInfo startInfo = new ProcessStartInfo() + { + FileName = "powershell.exe", + Arguments = $"Stop-Process -Name '{projectAndMutexName}'", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + StandardOutputEncoding = Encoding.UTF8, + }; + + + Process process = new Process() + { + StartInfo = startInfo, + }; + + process.Start(); + + process.WaitForExit(); + + output = process.StandardOutput.ReadToEnd(); + + } + catch (Exception ex) + { + output = $"An error occurred: {ex.Message}"; + } + + return output; + } } } diff --git a/HttpClientStudy.UnitTest/UtilitiesTest/DotnetCommondUtilityTest.cs b/HttpClientStudy.UnitTest/UtilitiesTest/DotnetCommondUtilityTest.cs index f5c7174..5241709 100644 --- a/HttpClientStudy.UnitTest/UtilitiesTest/DotnetCommondUtilityTest.cs +++ b/HttpClientStudy.UnitTest/UtilitiesTest/DotnetCommondUtilityTest.cs @@ -43,9 +43,18 @@ namespace HttpClientStudy.UnitTest.UtilitiesTest [Fact] public void StartApp_Test() { - var webapiDll = Path.GetFullPath("../../../../Docs/Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.dll"); + var webapiDll = Path.GetFullPath("../../../../Docs/Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe"); - var result = DotnetCommondUtility.ExecuteCommand(webapiDll); + var result = DotnetCommondUtility.RunWebApp(webapiDll); + + _output.WriteLine(result); + } + + [Fact] + public void StopApp_Test() + { + + var result = DotnetCommondUtility.SopWebApp(); _output.WriteLine(result); } diff --git a/HttpClientStudy.WebApp/Program.cs b/HttpClientStudy.WebApp/Program.cs index 1aafb22..ee1b45d 100644 --- a/HttpClientStudy.WebApp/Program.cs +++ b/HttpClientStudy.WebApp/Program.cs @@ -1,4 +1,4 @@ - + using System.Text; using System.Diagnostics; using System.Threading ; @@ -17,44 +17,44 @@ using HttpClientStudy.Config; namespace HttpClientStudy.WebApp { /// - /// HttpClientѧϰWebAPIĿ + /// HttpClient学习:WebAPI项目 /// public class Program { /// /// Main /// - /// + /// 启动参数 public static void Main(string[] args) { string projectAndMutexName = WebApiConfigManager.GetWebApiConfigOption().CurrentValue.WebAppMutexName; var mutex = new Mutex(true,projectAndMutexName,out bool createdResult); if (createdResult == false) { - Console.WriteLine($"ѾһʵУʵ˳"); + Console.WriteLine($"已经有一个实例在运行,本实例退出!"); return; } var builder = WebApplication.CreateBuilder(args); - #region ļ + #region 加载配置文件 var configBasePath = AppContext.BaseDirectory; - Console.WriteLine($"WebAppļĿ¼{configBasePath}"); + Console.WriteLine($"WebApp配置文件根目录:{configBasePath}"); builder.Configuration.SetBasePath(configBasePath); builder.Configuration.AddJsonFile("appsettings.json", false, true); builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", true, true); builder.Configuration.AddJsonFile($"configFiles/Config.json", false, true); #endregion - #region ע + #region 向容器注册服务 builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); - //Sessionм + //Session中间件依赖项 builder.Services.AddDistributedMemoryCache(); - //Session + //配置Session builder.Services.AddSession(option => { option.Cookie.Name = "HttpClientStudy"; @@ -62,15 +62,15 @@ namespace HttpClientStudy.WebApp option.IdleTimeout = TimeSpan.FromHours(1); }); - //Kestrelѡ + //配置Kestrel服务器选项 builder.Services.Configure(option => { - //ASP.NET Core 3.0 ֮ǰİ汾AllowSynchronousIO Ĭǿ - // true ͬ IO ,Getе.ֱֻӴжȡԶģͰ󶨡 + //ASP.NET Core 3.0 之前的版本,AllowSynchronousIO 默认是开启的 + //设置 true :允许同步 IO 操作,这样允许接收Get请求中的请求体数据.但只能直接从流中读取,不能自动模型绑定。 option.AllowSynchronousIO = true; }); - //Formύѡ + //配置Form表单提交选项 builder.Services.Configure(options => { //options.BufferBody = true; @@ -80,43 +80,43 @@ namespace HttpClientStudy.WebApp options.MultipartHeadersLengthLimit = int.MaxValue; }); - //Swagger + //配置Swagger builder.Services.AddSwaggerGen(setup => { - #region Swaggerĵ - //nameΪSwaggerUISwaggerEndpointе{documentName} - //߱뱣һ£쳣 - setup.SwaggerDoc(name: "v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "HttpClientѧϰ", Version = "1" }); + #region 定义Swagger文档 + //name参数即为SwaggerUI中SwaggerEndpoint方法参数中的{documentName} + //两者必须保持一致,否则异常 + setup.SwaggerDoc(name: "v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "HttpClient学习", Version = "第1版" }); #endregion - #region xmlע + #region 包含xml注释 var xmlCommentFiles = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "HttpClientStudy.*.xml", System.IO.SearchOption.TopDirectoryOnly); foreach (var xmlFile in xmlCommentFiles) { - //includeControllerXmlCommentsǷÿϵxmlע + //includeControllerXmlComments参数:是否启用控制器上的xml注释 setup.IncludeXmlComments(filePath: xmlFile, includeControllerXmlComments: true); setup.UseInlineDefinitionsForEnums(); } #endregion - #region ýӿAuthȨť + #region 放置接口Auth授权按钮 setup.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { - Description = "BearerTokenBearer {Token}", + Description = "请输入带有Bearer的Token:Bearer {Token}", - //jwtĬϵIJ + //jwt默认的参数名称 Name = "Authorization", - //jwtĬϴ Authorization Ϣλã˴Ϊͷ + //jwt默认存放 Authorization 信息的位置:此处为请求头中 In = ParameterLocation.Header, - //֤ͣ˴ʹApi Key + //验证类型:此处使用Api Key Type = SecuritySchemeType.ApiKey }); #endregion - #region ָӦ÷Χ + #region 指定方案应用范围 setup.AddSecurityRequirement(new OpenApiSecurityRequirement { { @@ -133,11 +133,11 @@ namespace HttpClientStudy.WebApp }); #endregion - //ע + //启用数据注解 setup.EnableAnnotations(); }); - //CORS + //配置CORS跨域 builder.Services.AddCors(option => { option.AddPolicy("AllowAll", builder => @@ -146,8 +146,8 @@ namespace HttpClientStudy.WebApp }); }); - //֤ - builder.Services //֤ܹ + //认证 + builder.Services //认证基础架构 .AddAuthentication(authOption => { authOption.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; @@ -156,18 +156,18 @@ namespace HttpClientStudy.WebApp authOption.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme; authOption.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) - //Cookie֤ + //Cookie认证 .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option => { - option.Cookie.Name = ".eds.editor.cookie.authentication.oa2";//ô洢û¼ϢûTokenϢCookie - option.Cookie.HttpOnly = true;//ô洢û¼ϢûTokenϢCookie޷ͨͻű(JavaScript)ʵ - option.ExpireTimeSpan = TimeSpan.FromDays(3);// ʱ - option.SlidingExpiration = true;// ǷڹʱʱԶ + option.Cookie.Name = ".eds.editor.cookie.authentication.oa2";//设置存储用户登录信息(用户Token信息)的Cookie名称 + option.Cookie.HttpOnly = true;//设置存储用户登录信息(用户Token信息)的Cookie,无法通过客户端浏览器脚本(如JavaScript等)访问到 + option.ExpireTimeSpan = TimeSpan.FromDays(3);// 过期时间 + option.SlidingExpiration = true;// 是否在过期时间过半的时候,自动延期 option.LoginPath = "/Account/Login"; option.LogoutPath = "/Account/LoginOut"; //option.AccessDeniedPath = "/Account/Login"; }) - //֤ + //认证 .AddJwtBearer(option => { option.TokenValidationParameters = new TokenValidationParameters @@ -178,28 +178,28 @@ namespace HttpClientStudy.WebApp ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("0123456789abcdefghigklmnopqrstdf41sadfweqtdfghsdfgsdfweqr")), - //ʱ䣬ܵЧʱʱjwtĹʱ + //缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间 ClockSkew = TimeSpan.FromSeconds(0) }; }); - //Ȩ + //授权 builder.Services.AddAuthorization(); - // + //健康检查 builder.Services.AddHealthChecks(); - //ͨ + //普通类 - builder.Services.AddScoped(provider => new Account() { Id = 0, Name = "עʾ", Password = "123456", Role = "IoC" }); + builder.Services.AddScoped(provider => new Account() { Id = 0, Name = "服务注入示例", Password = "123456", Role = "IoC" }); builder.Services.AddScoped(); #endregion var app = builder.Build(); - #region Httpܵ + #region 配置Http管道 - //ʱͳм + //耗时统计中间件 app.UseMiddleware(); app.MapHealthChecks("api/health"); diff --git a/HttpClientStudy.WebApp/appsettings.Development.json b/HttpClientStudy.WebApp/appsettings.Development.json index 3e1a225..364756f 100644 --- a/HttpClientStudy.WebApp/appsettings.Development.json +++ b/HttpClientStudy.WebApp/appsettings.Development.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "LogLevel": { "Default": "Information", diff --git a/HttpClientStudy.WebApp/appsettings.json b/HttpClientStudy.WebApp/appsettings.json index 3061efe..1885915 100644 --- a/HttpClientStudy.WebApp/appsettings.json +++ b/HttpClientStudy.WebApp/appsettings.json @@ -1,4 +1,4 @@ -{ +{ "urls": "http://localhost:5189", "Logging": { "LogLevel": { diff --git a/HttpClientStudy.WebClient/Program.cs b/HttpClientStudy.WebClient/Program.cs index 02fa311..8c16987 100644 --- a/HttpClientStudy.WebClient/Program.cs +++ b/HttpClientStudy.WebClient/Program.cs @@ -1,8 +1,8 @@ -using System.Diagnostics; +using System.Diagnostics; using HttpClientStudy.Core.Utilities; -//WebApi +//启动WebApi程序 StartupUtility.StartWebApiProject(); var builder = WebApplication.CreateBuilder(args); @@ -27,26 +27,26 @@ app.UseAuthorization(); app.MapControllers(); -#region ˳ʱرWebAPI +#region 退出时,关闭WebAPI进程 /* - * 1˳ִлƣ̫ɿֻ˳ʱִС - * 2˳ ctl+c - * 3˳ ڴй©رս̣رǵWebAPIʱʱرʱ˳쳣˳ + * 1、退出执行机制,本身不太可靠,只有正常退出时才执行。 + * 2、正常退出,比如 ctl+c 操作 + * 3、非正常退出:比如 程序崩溃、内存泄漏、关闭进程,特别是调试WebAPI时,如果启动时打开了浏览器,则关闭浏览器时退出属于异常退出。 */ -// ȡ IHostApplicationLifetime ʵ +// 获取 IHostApplicationLifetime 实例 var applicationLifetime = app.Services.GetRequiredService(); -// עӦóֹͣ¼رWebApi +// 注册应用程序停止事件:关闭WebApi applicationLifetime.ApplicationStopping.Register(() => { - //ע⣺ Visual Studio 2022 ԤУִʱ쳣ʽӦԤBug + //注意:在 Visual Studio 2022 预览版中,执行时异常;正式版中正常。应该是预案版的Bug StartupUtility.ExitWebApiProject(); }); applicationLifetime.ApplicationStopped.Register(() => { - Console.WriteLine("ֹͣ"); + Console.WriteLine("程序已停止"); }); #endregion diff --git a/HttpClientStudy.WebClient/appsettings.Development.json b/HttpClientStudy.WebClient/appsettings.Development.json index 0c208ae..b0bacf4 100644 --- a/HttpClientStudy.WebClient/appsettings.Development.json +++ b/HttpClientStudy.WebClient/appsettings.Development.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "LogLevel": { "Default": "Information", diff --git a/HttpClientStudy.WebClient/appsettings.json b/HttpClientStudy.WebClient/appsettings.json index 10f68b8..2230277 100644 --- a/HttpClientStudy.WebClient/appsettings.json +++ b/HttpClientStudy.WebClient/appsettings.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "LogLevel": { "Default": "Information",