Compare commits

..
7 Commits
Author SHA1 Message Date
xrain 9dc4ef7bce http client 增加了DebugLog 2025-12-18 16:17:18 +08:00
xrain d5eb7fe081 分页修改了,不在使用单独的分页返回,httpclient 不需要simaporesponse的包装,报错直接抛出异常 2025-12-18 14:06:11 +08:00
xrain b04a7c86d7 signQuery body defaultNull 2025-12-18 13:05:05 +08:00
xrain 8759b593db SimApiHttpClientAddServer 2025-12-18 11:28:47 +08:00
xrainandGitHub 5a46a026a7 Update nuget-publish.yml
fix ci
2025-12-18 09:13:19 +08:00
xrain 90124b6e67 update v10 2025-12-18 09:08:35 +08:00
xrain 2499912204 update package 2025-11-09 19:41:37 +08:00
10 changed files with 101 additions and 78 deletions
+3 -3
View File
@@ -10,11 +10,11 @@ jobs:
name: Publish Project to Nuget name: Publish Project to Nuget
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v4
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v5
with: with:
dotnet-version: "9.0.305" dotnet-version: "10.x.x"
- name: Publish - name: Publish
run: | run: |
version=`git describe --tags` version=`git describe --tags`
+2 -10
View File
@@ -46,23 +46,15 @@ public class SimApiBaseResponse(int code = 200, string message = "成功")
} }
/// <summary> /// <summary>
/// 动态内容分页 /// 分页内容返回
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class SimApiBasePageResponse<T>() : SimApiBaseResponse public class PageResponse<T>
{ {
public T? List { get; set; } public T? List { get; set; }
public int Page { get; set; } = 1; public int Page { get; set; } = 1;
public int Count { get; set; } = 20; public int Count { get; set; } = 20;
public int Total { get; set; } public int Total { get; set; }
public SimApiBasePageResponse(T list, int page, int count, int total) : this()
{
List = list;
Page = page;
Count = count;
Total = total;
}
} }
/// <summary> /// <summary>
+31 -9
View File
@@ -4,20 +4,22 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using SimApi.Communications; using SimApi.Communications;
using SimApi.Exceptions;
namespace SimApi.Helpers; namespace SimApi.Helpers;
public class SimApiHttpClient(string? appId, string appKey) public class SimApiHttpClient(string? appId, string appKey, bool debug = false)
{ {
public string Server { get; init; } = string.Empty;
public string SignName { get; init; } = "sign"; public string SignName { get; init; } = "sign";
public string TimestampName { get; init; } = "timestamp"; public string TimestampName { get; init; } = "timestamp";
public string NonceName { get; init; } = "nonce"; public string NonceName { get; init; } = "nonce";
public string? AppIdName { get; init; } = "appId"; public string? AppIdName { get; init; } = "appId";
public string[] SignFields { get; init; } = []; public string[] SignFields { get; init; } = [];
public T? SignQuery<T>(string url, object? body = null, Dictionary<string, string>? queries = null)
public T? SignQuery<T>(string url, object body, Dictionary<string, string>? queries = null)
{ {
url = Server + url;
var queryUrl = SignFields.Aggregate(string.Empty, var queryUrl = SignFields.Aggregate(string.Empty,
(current, signField) => current + $"{signField}={queries?[signField]}&"); (current, signField) => current + $"{signField}={queries?[signField]}&");
if (!string.IsNullOrEmpty(AppIdName)) if (!string.IsNullOrEmpty(AppIdName))
@@ -35,13 +37,12 @@ public class SimApiHttpClient(string? appId, string appKey)
.Aggregate(path, (current, q) => current + $"&{q.Key}={q.Value}"); .Aggregate(path, (current, q) => current + $"&{q.Key}={q.Value}");
} }
var http = new HttpClient(); return Query<T>(path, body);
var resp = http.PostAsJsonAsync(path, body).Result;
return resp.Content.ReadFromJsonAsync<T>().Result;
} }
public T? AesQuery<T>(string url, object body) public T? AesQuery<T>(string url, object body)
{ {
url = Server + url;
if (!string.IsNullOrEmpty(AppIdName)) if (!string.IsNullOrEmpty(AppIdName))
{ {
url += $"?{AppIdName}={appId}"; url += $"?{AppIdName}={appId}";
@@ -51,9 +52,7 @@ public class SimApiHttpClient(string? appId, string appKey)
{ {
Data = SimApiAesUtil.Encrypt(SimApiUtil.Json(body), appKey) Data = SimApiAesUtil.Encrypt(SimApiUtil.Json(body), appKey)
}; };
var http = new HttpClient(); return Query<T>(url, req);
var resp = http.PostAsJsonAsync(url, req).Result;
return resp.Content.ReadFromJsonAsync<T>().Result;
} }
public T? AesSignQuery<T>(string url, object body, Dictionary<string, string>? queries = null) public T? AesSignQuery<T>(string url, object body, Dictionary<string, string>? queries = null)
@@ -64,4 +63,27 @@ public class SimApiHttpClient(string? appId, string appKey)
}; };
return SignQuery<T>(url, req, queries); return SignQuery<T>(url, req, queries);
} }
private T? Query<T>(string url, object? req)
{
var http = new HttpClient();
if (debug)
{
Console.WriteLine($"[HTTPCLIENT请求] {url}\n{SimApiUtil.Json(req)}\n");
}
var resp = http.PostAsJsonAsync(url, req).Result;
if (debug)
{
Console.WriteLine($"[HTTPCLIENT响应] {resp.Content.ReadAsStringAsync().Result}\n");
}
var res = resp.Content.ReadFromJsonAsync<SimApiBaseResponse<T>>().Result;
if (res == null)
{
throw new SimApiException(500, "请求发生错误");
}
return res.Code != 200 ? throw new SimApiException(res.Code, res.Message) : res.Data;
}
} }
+6 -6
View File
@@ -9,7 +9,7 @@
<Description>AspNetCore一个方便的API文档,捕获异常,统一输入输出的API类库</Description> <Description>AspNetCore一个方便的API文档,捕获异常,统一输入输出的API类库</Description>
<PackageId>Simcu.SimApi</PackageId> <PackageId>Simcu.SimApi</PackageId>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -17,14 +17,14 @@
<Folder Include="Exceptions\"/> <Folder Include="Exceptions\"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.21" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.8.22" />
<PackageReference Include="Hangfire.Console" Version="1.4.3"/> <PackageReference Include="Hangfire.Console" Version="1.4.3"/>
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.12.0"/> <PackageReference Include="Hangfire.Redis.StackExchange" Version="1.12.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.10" /> <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.1" />
<PackageReference Include="Minio" Version="6.0.5" /> <PackageReference Include="Minio" Version="7.0.0" />
<PackageReference Include="MQTTnet" Version="5.0.1.1416"/> <PackageReference Include="MQTTnet" Version="5.0.1.1416"/>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="9.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="10.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.0.1" />
</ItemGroup> </ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<MonoDevelop> <MonoDevelop>
+1 -1
View File
@@ -9,7 +9,7 @@ using Hangfire.Redis.StackExchange;
using SimApi.Helpers; using SimApi.Helpers;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using SimApi.Middlewares; using SimApi.Middlewares;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
+2 -1
View File
@@ -1,10 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.OpenApi;
using SimApi.Attributes; using SimApi.Attributes;
namespace SimApi.SwaggerFilters; namespace SimApi.SwaggerFilters;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection; using System.Reflection;
@@ -1,27 +1,26 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Text.Json.Nodes;
using Microsoft.OpenApi.Any;
namespace SimApi.SwaggerFilters; namespace SimApi.SwaggerFilters;
public class GlobalDynamicObjectSchemaFilter : ISchemaFilter public class GlobalDynamicObjectSchemaFilter : ISchemaFilter
{ {
public void Apply(OpenApiSchema schema, SchemaFilterContext context) public void Apply(IOpenApiSchema schema, SchemaFilterContext context)
{ {
if (!IsDynamicObjectType(context.Type)) return; if (!IsDynamicObjectType(context.Type)) return;
schema.AdditionalPropertiesAllowed = true; var oaSchema = schema as OpenApiSchema;
schema.AdditionalProperties = new OpenApiSchema oaSchema.AdditionalPropertiesAllowed = true;
oaSchema.AdditionalProperties = new OpenApiSchema
{ {
Type = "object", // 表示 value 可以是任意类型(兼容所有类型) Type = JsonSchemaType.Object, // 表示 value 可以是任意类型(兼容所有类型)
Nullable = true
}; };
// 2. 覆盖默认示例,使用包含多种类型的示例 // 2. 覆盖默认示例,使用包含多种类型的示例
schema.Example = CreateMultiTypeExample(); oaSchema.Example = CreateMultiTypeExample();
} }
// 判断是否为需要处理的“动态对象”类型 // 判断是否为需要处理的“动态对象”类型
@@ -33,30 +32,39 @@ public class GlobalDynamicObjectSchemaFilter : ISchemaFilter
{ {
return true; return true;
} }
if (type == typeof(object)) if (type == typeof(object))
{ {
return true; return true;
} }
return type.Name.Contains("AnonymousType") && type.Namespace == null; return type.Name.Contains("AnonymousType") && type.Namespace == null;
} }
// 创建包含多种类型的示例(覆盖默认的 string 示例) // 创建包含多种类型的示例(覆盖默认的 string 示例)
private OpenApiObject CreateMultiTypeExample() private JsonNode CreateMultiTypeExample()
{ {
return new OpenApiObject return new JsonObject
{ {
["stringProp"] = new OpenApiString("example string"), // 字符串 // 字符串类型:JsonValue.Create 包装字符串
["numberProp"] = new OpenApiInteger(123), // 数字 ["stringProp"] = JsonValue.Create("example string"),
["boolProp"] = new OpenApiBoolean(true), // 布尔值 // 数字类型:支持 int/long/double 等,JsonValue 自动适配
["objectProp"] = new OpenApiObject // 嵌套对象 ["numberProp"] = JsonValue.Create(123),
// 布尔类型
["boolProp"] = JsonValue.Create(true),
// 嵌套对象:JsonObject 对应 OpenApiObject
["objectProp"] = new JsonObject
{ {
["nestedKey"] = new OpenApiString("nested value") ["nestedKey"] = JsonValue.Create("nested value")
}, },
["arrayProp"] = new OpenApiArray // 数组 // 数组类型:JsonArray 对应 OpenApiArray
["arrayProp"] = new JsonArray
{ {
new OpenApiInteger(1), JsonValue.Create(1), // 数组内数字
new OpenApiString("two") JsonValue.Create("two") // 数组内字符串
} },
// 可选:添加 null 值示例(若需要)
["nullProp"] = null
}; };
} }
} }
+17 -21
View File
@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using SimApi.Attributes; using SimApi.Attributes;
@@ -18,28 +18,24 @@ namespace SimApi.SwaggerFilters
|| ||
// 控制器上有 [SimApiAuth](继承到所有方法) // 控制器上有 [SimApiAuth](继承到所有方法)
context.MethodInfo.DeclaringType?.GetCustomAttributes<SimApiAuthAttribute>(true).Any() == true; context.MethodInfo.DeclaringType?.GetCustomAttributes<SimApiAuthAttribute>(true).Any() == true;
if (requiresAuth) if (!requiresAuth) return;
// 关键修复:正确构造 OpenApiSecuritySchemeReference(匹配键类型要求)
var securitySchemeRef = new OpenApiSecuritySchemeReference(
referenceId: "SimApiAuth", // 必须与 AddSecurityDefinition 的 ID 一致
hostDocument: null,
externalResource: null
);
// 给 Security 赋值(确保键类型是 OpenApiSecuritySchemeReference
operation.Security ??= new List<OpenApiSecurityRequirement>();
operation.Security.Add(new OpenApiSecurityRequirement
{ {
// 添加授权要求:关联步骤 2 中定义的 "SimApiAuth" 安全方案
operation.Security = new List<OpenApiSecurityRequirement>
{ {
new OpenApiSecurityRequirement securitySchemeRef,
{ []
{ }
new OpenApiSecurityScheme });
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "SimApiAuth" // 必须与 AddSecurityDefinition 的第一个参数一致
}
},
[] // 无需指定作用域(scope)时留空
}
}
};
}
// 未标记 [SimApiAuth] 的接口:不添加安全要求,Swagger 不显示锁图标
} }
} }
} }
@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
+10 -6
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using SimApi.Attributes; using SimApi.Attributes;
using SimApi.ModelBinders; using SimApi.ModelBinders;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
@@ -67,20 +67,24 @@ public class SimApiSignOperationFilter(IServiceProvider serviceProvider) : IOper
signParameters.AddRange(keyProvider.SignFields.Where(x => x != keyProvider.AppIdName) signParameters.AddRange(keyProvider.SignFields.Where(x => x != keyProvider.AppIdName)
.Select(f => (f, string.Empty, true))); .Select(f => (f, string.Empty, true)));
operation.Parameters ??= new List<IOpenApiParameter>();
foreach (var (name, description, required) in signParameters) foreach (var (name, description, required) in signParameters)
{ {
if (operation.Parameters?.Any(p => p.Name == name) == true) if (operation.Parameters?.Any(p => p.Name == name) == true)
{ {
var tmp = operation.Parameters?.FirstOrDefault(p => p.Name == name); var tmp = operation.Parameters?.FirstOrDefault(p => p.Name == name);
if (tmp != null) if (tmp is OpenApiParameter concreteParam)
{ {
tmp.Required = required; // 重新赋值只读属性(通过实例化新对象覆盖,或直接修改具体类的可写属性)
tmp.Description = description; concreteParam.Required = required; // OpenApiParameter 的 Required 有 setter
concreteParam.Description = description;
} }
continue; continue;
} }
operation.Parameters ??= new List<OpenApiParameter>(); operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter operation.Parameters.Add(new OpenApiParameter
{ {
Name = name, Name = name,
@@ -89,7 +93,7 @@ public class SimApiSignOperationFilter(IServiceProvider serviceProvider) : IOper
Required = required, Required = required,
Schema = new OpenApiSchema Schema = new OpenApiSchema
{ {
Type = "string" // 签名相关参数通常为字符串类型 Type = JsonSchemaType.String // 签名相关参数通常为字符串类型
} }
}); });
} }