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",