Skip to content

Commit c73aa1d

Browse files
committed
fix api ui, add versions
1 parent af6bc86 commit c73aa1d

12 files changed

Lines changed: 411 additions & 71 deletions

.github/workflows/nuget-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Publish
1919
run: |
2020
version=`git describe --tags`
21-
dotnet build --configuration release -p:PackageVersion=$version
21+
dotnet build --configuration release -p:Version=$version
2222
dotnet nuget push bin/release/Simcu.SimApi.$version.nupkg -k ${NUGET_APIKEY} -s https://www.nuget.org/api/v2/package
2323
env:
2424
NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }}

Attributes/SimApiDocAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class SimApiDocAttribute : SwaggerOperationAttribute
1616
/// <param name="name">接口名称</param>
1717
public SimApiDocAttribute(string tag, string name)
1818
{
19-
Tags = new[] { tag };
19+
Tags = [tag];
2020
Summary = name;
2121
// Consumes = new[] {"application/json"};
2222
// Produces = new[] {"application/json"};

Communications/SimApiBaseResponse.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public class SimApiBaseResponse(int code = 200, string message = "成功")
1313
public int Code { get; set; } = code;
1414
public string Message { get; set; } = message;
1515

16+
public SimApiBaseResponse() : this(200, "成功")
17+
{
18+
}
19+
1620
/// <summary>
1721
/// 默认错误代码对应提示信息
1822
/// </summary>

Configurations/SimApiOptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ public class SimApiOptions
7474
public bool EnableLowerUrl { get; set; } = true;
7575

7676

77+
/// <summary>
78+
/// 应用可以通过 /versions 显示出应用版本和SimApi包版本
79+
/// default: true
80+
/// </summary>
81+
public bool EnableVersionUrl { get; set; } = true;
82+
83+
84+
7785
/// <summary>
7886
/// 启用格式化的 Console Logger
7987
/// default: false

Controllers/SimApiBaseController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace SimApi.Controllers;
1414
/// 2. 报错返回
1515
/// 3. 错误回馈页面
1616
/// </summary>
17+
///
18+
[Consumes("application/json")]
19+
[Produces("application/json")]
1720
public class SimApiBaseController : Controller
1821
{
1922
/// <summary>

Controllers/SimApiCommonController.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
using Microsoft.AspNetCore.Mvc;
1+
using System.Collections.Generic;
2+
using Microsoft.AspNetCore.Mvc;
23
using SimApi.Attributes;
34
using SimApi.Communications;
45
using SimApi.Helpers;
56

67
namespace SimApi.Controllers;
8+
79
public class SimApiCommonController(SimApiAuth auth) : SimApiBaseController
810
{
911
/// <summary>
@@ -25,7 +27,7 @@ public SimApiBaseResponse ExceptionHandler(int code)
2527
[HttpPost, SimApiDoc("认证", "检测登陆")]
2628
public SimApiBaseResponse<string> CheckLogin()
2729
{
28-
ErrorWhenNull(LoginInfo, 401,"未登录");
30+
ErrorWhenNull(LoginInfo, 401, "未登录");
2931
return new SimApiBaseResponse<string>
3032
{
3133
Data = LoginInfo.Id
@@ -50,7 +52,20 @@ public SimApiBaseResponse Logout()
5052
return new SimApiBaseResponse();
5153
}
5254

53-
[HttpPost,SimApiAuth]
55+
[HttpPost, HttpGet]
56+
public SimApiBaseResponse<Dictionary<string, string>> Versions()
57+
{
58+
return new SimApiBaseResponse<Dictionary<string, string>>()
59+
{
60+
Data = new Dictionary<string, string>
61+
{
62+
{ "SimApi", SimApiUtil.SimApiVersion },
63+
{ "App", SimApiUtil.AppVersion }
64+
}
65+
};
66+
}
67+
68+
[HttpPost, SimApiAuth]
5469
public SimApiBaseResponse<SimApiLoginItem> UserInfo()
5570
{
5671
return new SimApiBaseResponse<SimApiLoginItem>(LoginInfo);

Helpers/SimApiAesUtil.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System;
2+
using System.IO;
3+
using System.Security.Cryptography;
4+
using System.Text;
5+
6+
namespace SimApi.Helpers;
7+
8+
public static class SimApiAesUtil
9+
{
10+
// 密钥长度:256位 (32字节)
11+
private const int KeySize = 256;
12+
13+
// 块大小:128位 (16字节)
14+
private const int BlockSize = 128;
15+
16+
// 加密模式 - 重命名以避免与枚举类型冲突
17+
private const CipherMode AesCipherMode = CipherMode.CBC;
18+
19+
// 填充模式 - 重命名以避免与枚举类型冲突
20+
private const PaddingMode AesPaddingMode = PaddingMode.PKCS7;
21+
22+
/// <summary>
23+
/// AES 加密
24+
/// </summary>
25+
/// <param name="plainText">明文</param>
26+
/// <param name="key">字符串密钥(将被处理为256位)</param>
27+
/// <returns>加密后的Base64字符串(包含IV)</returns>
28+
public static string Encrypt(string plainText, string key)
29+
{
30+
if (string.IsNullOrEmpty(plainText))
31+
throw new ArgumentNullException(nameof(plainText));
32+
if (string.IsNullOrEmpty(key))
33+
throw new ArgumentNullException(nameof(key));
34+
35+
// 处理密钥为指定长度
36+
var keyBytes = ProcessKey(key);
37+
// 生成随机IV
38+
var iv = GenerateRandomIv();
39+
40+
using var aes = CreateAesProvider(keyBytes, iv);
41+
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
42+
using var ms = new MemoryStream();
43+
// 先写入IV,解密时需要用到
44+
ms.Write(iv, 0, iv.Length);
45+
46+
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
47+
using (var sw = new StreamWriter(cs))
48+
{
49+
sw.Write(plainText);
50+
}
51+
52+
return Convert.ToBase64String(ms.ToArray());
53+
}
54+
55+
/// <summary>
56+
/// AES 解密
57+
/// </summary>
58+
/// <param name="cipherText">加密后的Base64字符串</param>
59+
/// <param name="key">字符串密钥(与加密时相同)</param>
60+
/// <returns>解密后的明文</returns>
61+
public static string Decrypt(string cipherText, string key)
62+
{
63+
if (string.IsNullOrEmpty(cipherText))
64+
throw new ArgumentNullException(nameof(cipherText));
65+
if (string.IsNullOrEmpty(key))
66+
throw new ArgumentNullException(nameof(key));
67+
68+
var cipherBytes = Convert.FromBase64String(cipherText);
69+
70+
// 从加密数据中提取IV
71+
var iv = new byte[BlockSize / 8];
72+
Array.Copy(cipherBytes, 0, iv, 0, iv.Length);
73+
74+
// 处理密钥为指定长度
75+
var keyBytes = ProcessKey(key);
76+
77+
using var aes = CreateAesProvider(keyBytes, iv);
78+
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
79+
using var ms = new MemoryStream(cipherBytes, iv.Length, cipherBytes.Length - iv.Length);
80+
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
81+
using var sr = new StreamReader(cs);
82+
return sr.ReadToEnd();
83+
}
84+
85+
/// <summary>
86+
/// 处理密钥为指定长度(256位)
87+
/// </summary>
88+
private static byte[] ProcessKey(string key)
89+
{
90+
// 使用SHA256哈希处理密钥,确保得到32字节(256位)的密钥
91+
return SHA256.HashData(Encoding.UTF8.GetBytes(key));
92+
}
93+
94+
/// <summary>
95+
/// 生成随机初始化向量
96+
/// </summary>
97+
private static byte[] GenerateRandomIv()
98+
{
99+
using var aes = Aes.Create();
100+
aes.BlockSize = BlockSize;
101+
aes.GenerateIV();
102+
return aes.IV;
103+
}
104+
105+
/// <summary>
106+
/// 创建并配置AES加密服务提供器
107+
/// </summary>
108+
private static Aes CreateAesProvider(byte[] key, byte[] iv)
109+
{
110+
var aes = Aes.Create();
111+
aes.KeySize = KeySize;
112+
aes.BlockSize = BlockSize;
113+
aes.Mode = AesCipherMode;
114+
aes.Padding = AesPaddingMode;
115+
aes.Key = key;
116+
aes.IV = iv;
117+
return aes;
118+
}
119+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Microsoft.OpenApi.Models;
6+
using Swashbuckle.AspNetCore.SwaggerGen;
7+
using SimApi.Attributes;
8+
9+
namespace SimApi.Helpers
10+
{
11+
public class SimApiAuthOperationFilter : IOperationFilter
12+
{
13+
public void Apply(OpenApiOperation operation, OperationFilterContext context)
14+
{
15+
// 检查接口或控制器是否标记了 [SimApiAuth] 特性
16+
var requiresAuth =
17+
// 方法上有 [SimApiAuth]
18+
context.MethodInfo.GetCustomAttributes<SimApiAuthAttribute>(true).Any()
19+
||
20+
// 控制器上有 [SimApiAuth](继承到所有方法)
21+
context.MethodInfo.DeclaringType?.GetCustomAttributes<SimApiAuthAttribute>(true).Any() == true;
22+
if (requiresAuth)
23+
{
24+
// 添加授权要求:关联步骤 2 中定义的 "SimApiAuth" 安全方案
25+
operation.Security = new List<OpenApiSecurityRequirement>
26+
{
27+
new OpenApiSecurityRequirement
28+
{
29+
{
30+
new OpenApiSecurityScheme
31+
{
32+
Reference = new OpenApiReference
33+
{
34+
Type = ReferenceType.SecurityScheme,
35+
Id = "SimApiAuth" // 必须与 AddSecurityDefinition 的第一个参数一致
36+
}
37+
},
38+
[] // 无需指定作用域(scope)时留空
39+
}
40+
}
41+
};
42+
}
43+
// 未标记 [SimApiAuth] 的接口:不添加安全要求,Swagger 不显示锁图标
44+
}
45+
}
46+
}

Helpers/SimApiUtil.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Linq;
4+
using System.Reflection;
45
using System.Security.Cryptography;
56
using System.Text;
67
using System.Text.Encodings.Web;
@@ -30,6 +31,53 @@ public static class SimApiUtil
3031
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
3132
};
3233

34+
public static string SimApiVersion
35+
{
36+
get
37+
{
38+
// 这里使用当前类(属于 NuGet 包)的程序集
39+
var assembly = typeof(SimApiUtil).Assembly;
40+
41+
// 优先获取 AssemblyInformationalVersion(通常对应 NuGet 包版本,可能包含预发布标签)
42+
var informationalVersion =
43+
assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
44+
if (!string.IsNullOrEmpty(informationalVersion))
45+
{
46+
return informationalVersion;
47+
}
48+
49+
// 若不存在,则获取 AssemblyVersion(编译时版本)
50+
var version = assembly.GetName().Version?.ToString();
51+
return version ?? "Unknown";
52+
}
53+
}
54+
55+
public static string AppVersion
56+
{
57+
get
58+
{
59+
// 获取外层应用的入口程序集(通常是启动项目的程序集)
60+
var entryAssembly = Assembly.GetEntryAssembly();
61+
if (entryAssembly == null)
62+
{
63+
// 特殊场景(如单元测试、某些宿主环境)下,入口程序集可能为 null,可尝试获取调用栈中的上层程序集
64+
entryAssembly = Assembly.GetCallingAssembly(); // 或 Assembly.GetExecutingAssembly() 视场景调整
65+
}
66+
67+
// 优先获取应用的 AssemblyInformationalVersion
68+
var informationalVersion = entryAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
69+
?.InformationalVersion;
70+
if (!string.IsNullOrEmpty(informationalVersion))
71+
{
72+
return informationalVersion;
73+
}
74+
75+
// 若不存在,则获取 AssemblyVersion
76+
var version = entryAssembly.GetName().Version?.ToString();
77+
return version ?? "Unknown";
78+
}
79+
}
80+
3381
/// <summary>
3482
/// 当前秒级时间戳
3583
/// </summary>

0 commit comments

Comments
 (0)