Compare commits

...
7 Commits
Author SHA1 Message Date
xrain e53048a980 update option 2024-12-23 22:11:00 +08:00
xrain bbc2806995 add origin response attribute 2024-12-23 20:34:38 +08:00
xrain 404a638d5e add filter 2024-12-23 20:26:21 +08:00
xrain e27d5e1c77 fix data 2024-09-08 14:42:51 +08:00
xrain 6af68da3eb timeout rpc supported 2024-09-05 15:06:44 +08:00
xrain 7dc2ac6a86 add headers 2024-09-05 12:59:57 +08:00
xrain 8f6b853b6c fix rpc ex , add RpcError Method 2024-08-25 00:11:00 +08:00
10 changed files with 146 additions and 49 deletions
+8
View File
@@ -0,0 +1,8 @@
using System;
namespace SimApi.Attributes;
[AttributeUsage(AttributeTargets.Method)]
public class OriginResponseAttribute : Attribute
{
}
+2 -9
View File
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using SimApi.Helpers;
namespace SimApi.Communications;
@@ -36,15 +37,7 @@ public class SimApiBaseResponse(int code = 200, string message = "成功")
/// <returns></returns>
public override string ToString()
{
return JsonSerializer.Serialize(this, new JsonSerializerOptions
{
IgnoreReadOnlyProperties = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters =
{
new JsonStringEnumConverter()
}
});
return SimApiUtil.Json(this);
}
}
+5
View File
@@ -36,6 +36,11 @@ public class SimApiOptions
/// default: true
/// </summary>
public bool EnableSimApiException { get; set; } = true;
/// <summary>
/// 启用返回结果拦截
/// </summary>
public bool EnableSimApiResponseFilter { get; set; } = true;
/// <summary>
/// 开启S3兼容的存储系统。
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using SimApi.Attributes;
using SimApi.Communications;
namespace SimApi.Helpers;
public class SimApiResponseFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.ActionDescriptor.EndpointMetadata.Any(meta => meta is OriginResponseAttribute))
{
return;
}
context.Result = context.Result switch
{
// 检查结果是否为 null
null => new OkObjectResult(new SimApiBaseResponse()),
ObjectResult { Value: SimApiBaseResponse simApiBaseResponse } =>
new OkObjectResult(simApiBaseResponse),
ObjectResult objectResult => new OkObjectResult(new SimApiBaseResponse<object>(objectResult.Value!)),
EmptyResult => new OkObjectResult(new SimApiBaseResponse()),
_ => context.Result
};
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
+4 -7
View File
@@ -1,5 +1,5 @@
using System;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using SimApi.Communications;
@@ -27,12 +27,11 @@ public class SimApiExceptionMiddleware(RequestDelegate next, ILogger<SimApiExcep
switch (context.Response.StatusCode)
{
case 200:
break;
case 404:
throw new SimApiException(context.Response.StatusCode, "请求的接口不存在");
case 301:
case 302:
break;
case 404:
throw new SimApiException(context.Response.StatusCode, "请求的接口不存在");
default:
throw new SimApiException(context.Response.StatusCode);
}
@@ -42,7 +41,6 @@ public class SimApiExceptionMiddleware(RequestDelegate next, ILogger<SimApiExcep
response = string.IsNullOrEmpty(ex.Message)
? new SimApiBaseResponse(ex.Code)
: new SimApiBaseResponse(ex.Code, ex.Message);
ErrorResponse(context, response);
}
catch (Exception ex)
@@ -61,9 +59,8 @@ public class SimApiExceptionMiddleware(RequestDelegate next, ILogger<SimApiExcep
/// <param name="response"></param>
private static void ErrorResponse(HttpContext context, SimApiBaseResponse response)
{
if (context.Response.HasStarted) return;
context.Response.StatusCode = 200;
context.Response.Headers.Append("Content-Type", "application/json");
context.Response.WriteAsync(response.ToString());
context.Response.WriteAsync(response.ToString()).Wait();
}
}
-2
View File
@@ -19,9 +19,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="Helpers\"/>
<Folder Include="Communications\"/>
<Folder Include="Middlewares\"/>
<Folder Include="Exceptions\"/>
</ItemGroup>
<ItemGroup>
+29
View File
@@ -1,4 +1,8 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using SimApi.Helpers;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
@@ -7,6 +11,7 @@ using SimApi.Middlewares;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SimApi.Attributes;
using SimApi.CoceSdk;
using SimApi.Configurations;
using SimApi.Logger;
@@ -53,6 +58,19 @@ public static class SimApiExtensions
if (simApiOptions.EnableSynapse)
{
builder.AddSingleton<Synapse>();
//自动依赖注入
var stackTrace = new StackTrace();
var callingMethod = stackTrace.GetFrame(stackTrace.FrameCount - 1)?.GetMethod();
var assembly = callingMethod?.DeclaringType?.Assembly;
var types = assembly?.GetTypes() ?? [];
foreach (var type in types)
{
var methodsWithSynapse = type.GetMethods()
.Where(m => m.GetCustomAttribute<SynapseRpcAttribute>() != null ||
m.GetCustomAttribute<SynapseEventAttribute>() != null);
if (!methodsWithSynapse.Any()) continue;
builder.AddScoped(type);
}
}
// 使用SimApiDoc
@@ -192,6 +210,12 @@ public static class SimApiExtensions
builder.AddSingleton<SimApiStorage>();
}
if (simApiOptions.EnableSimApiResponseFilter)
{
builder.AddControllers(opt => opt.Filters.Add<SimApiResponseFilter>())
.AddJsonOptions(opt => opt.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase);
}
builder.AddSingleton(simApiOptions);
return builder;
}
@@ -211,6 +235,11 @@ public static class SimApiExtensions
builder.Services.GetService<SimApiStorage>();
}
if (options.EnableSimApiResponseFilter)
{
logger.LogInformation("开始配置SimApiResponseFilter...");
}
if (options.EnableCoceSdk)
{
logger.LogInformation("开始配置CoceAppSdk...\nApi入口: {ApiUrl}\nAuth入口:{AuthUrl}n\nAppId: {AppId}",
+18 -9
View File
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Packets;
using SimApi.Communications;
using SimApi.Helpers;
@@ -38,7 +40,8 @@ public partial class Synapse
Client!.SubscribeAsync(rcSubOpts).Wait();
}
private string? FireRpc(string app, string action, object? param)
private string? FireRpc(string app, string action, object? param, Dictionary<string, string>? headers = null,
int? timeout = null)
{
string paramJson;
if (param is string strParam)
@@ -54,23 +57,29 @@ public partial class Synapse
var messageId = Guid.NewGuid().ToString();
var tcs = new TaskCompletionSource<string>();
ResponseCompletionSources.Add(messageId, tcs);
var message = new MqttApplicationMessageBuilder()
var messageBuilder = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(paramJson)
.WithResponseTopic($"{Options.AppName},{Options.AppId}")
.WithContentType(messageId)
.WithRetainFlag(false)
.Build();
.WithResponseTopic($"{Options.AppName},{Options.AppId},{messageId}")
.WithContentType("application/json")
.WithRetainFlag(false);
foreach (var h in headers ?? new Dictionary<string, string>())
{
messageBuilder.WithUserProperty(h.Key, h.Value);
}
var message = messageBuilder.Build();
if (!Client!.IsConnected) return null;
Client.PublishAsync(message, CancellationToken.None).Wait();
logger.LogDebug(
"Synapse RPC Client Request: ({PropsMessageId}) {OptionsAppName} -> {Action}@{App}\n{ParamJson}", messageId,
Options.AppName, action, app, paramJson);
"Synapse RPC Client Request: ({PropsMessageId}) {OptionsAppName} -> {Action}@{App}\n{ParamJson}\nHeaders: {Headers}",
messageId, Options.AppName, action, app, paramJson, JsonSerializer.Serialize(headers));
string response;
try
{
if (tcs.Task.Wait(Options.RpcTimeout * 1000))
timeout ??= Options.RpcTimeout;
if (tcs.Task.Wait(timeout.Value * 1000))
{
response = tcs.Task.Result;
logger.LogDebug(
+24 -15
View File
@@ -28,8 +28,7 @@ public partial class Synapse
var appInfo = e.ApplicationMessage.ResponseTopic.Split(",");
logger.LogDebug(
"Synapse RPC Server Receive: ({BasicPropertiesMessageId}) {BasicPropertiesReplyTo} -> {BasicPropertiesType}@{OptionsAppName}\n{S}",
e.ApplicationMessage.ContentType, appInfo[0], action, Options.AppName,
reqBody);
appInfo[2], appInfo[0], action, Options.AppName, reqBody);
SimApiBaseResponse res;
var method = RpcRegistry.FirstOrDefault(x => x.Key == action);
if (method == null)
@@ -44,17 +43,27 @@ public partial class Synapse
{
var methodParams = mt!.GetParameters();
object? ret;
if (methodParams.Length == 0)
switch (methodParams.Length)
{
ret = mt.Invoke(callClass, []);
}
else
{
var pt = mt.GetParameters()[0].ParameterType;
var param = pt == typeof(string)
? [reqBody]
: new[] { JsonSerializer.Deserialize(reqBody, pt, SimApiUtil.JsonOption) };
ret = mt.Invoke(callClass, param);
case 1:
var pt = mt.GetParameters()[0].ParameterType;
var param = pt == typeof(string)
? [reqBody]
: new[] { JsonSerializer.Deserialize(reqBody, pt, SimApiUtil.JsonOption) };
ret = mt.Invoke(callClass, param);
break;
case 2:
var headerData =
e.ApplicationMessage.UserProperties.ToDictionary(x => x.Name, x => x.Value);
var pt2 = mt.GetParameters()[0].ParameterType;
var param2 = pt2 == typeof(string)
? [reqBody]
: new[] { JsonSerializer.Deserialize(reqBody, pt2, SimApiUtil.JsonOption), headerData };
ret = mt.Invoke(callClass, param2);
break;
default:
ret = mt.Invoke(callClass, []);
break;
}
res = new SimApiBaseResponse<object?>
@@ -71,7 +80,7 @@ public partial class Synapse
}
else
{
logger.LogError("Synapse RPC 方法异常: {Err}\n{Stack}", ex.Message,ex.StackTrace);
logger.LogError("Synapse RPC 方法异常: {Err}\n{Stack}", ex.Message, ex.StackTrace);
res = new SimApiBaseResponse(500, ex.Message);
}
}
@@ -83,7 +92,7 @@ public partial class Synapse
}
var returnJson = JsonSerializer.Serialize((object)res, SimApiUtil.JsonOption);
var reply = $"{Options.SysName}/{appInfo[0]}/rpc/client/{appInfo[1]}/{e.ApplicationMessage.ContentType}";
var reply = $"{Options.SysName}/{appInfo[0]}/rpc/client/{appInfo[1]}/{appInfo[2]}";
var message = new MqttApplicationMessageBuilder()
.WithTopic(reply)
.WithPayload(returnJson)
@@ -93,7 +102,7 @@ public partial class Synapse
Client.PublishAsync(message, CancellationToken.None).Wait();
logger.LogDebug(
"Synapse Rpc Server Return: ({BasicPropertiesMessageId}) {BasicPropertiesType}@{OptionsAppName} -> {BasicPropertiesReplyTo}\n{ReturnJson}",
e.ApplicationMessage.ContentType, action, Options.AppName, appInfo[0], returnJson);
appInfo[2], action, Options.AppName, appInfo[0], returnJson);
return Task.CompletedTask;
};
+22 -7
View File
@@ -87,9 +87,12 @@ public partial class Synapse(SimApiOptions simApiOptions, ILogger<Synapse> logge
/// <param name="appName"></param>
/// <param name="method"></param>
/// <param name="param"></param>
/// <param name="headers"></param>
/// <param name="timeout"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public SimApiBaseResponse<T> Rpc<T>(string appName, string method, dynamic? param = null)
public SimApiBaseResponse<T> Rpc<T>(string appName, string method, dynamic? param = null,
Dictionary<string, string>? headers = null, int? timeout = null)
{
var res = new SimApiBaseResponse(500, "Synapse Rpc Client Disabled!");
if (Options.DisableRpcClient)
@@ -98,7 +101,7 @@ public partial class Synapse(SimApiOptions simApiOptions, ILogger<Synapse> logge
}
else
{
var data = FireRpc(appName, method, param);
var data = FireRpc(appName, method, param, headers, timeout);
res = JsonSerializer.Deserialize<SimApiBaseResponse<T>>(data, SimApiUtil.JsonOption);
}
@@ -111,10 +114,13 @@ public partial class Synapse(SimApiOptions simApiOptions, ILogger<Synapse> logge
/// <param name="appName"></param>
/// <param name="method"></param>
/// <param name="param"></param>
/// <param name="headers"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public SimApiBaseResponse<object> Rpc(string appName, string method, dynamic? param = null)
public SimApiBaseResponse<object> Rpc(string appName, string method, dynamic? param = null,
Dictionary<string, string>? headers = null, int? timeout = null)
{
return Rpc<object>(appName, method, param);
return Rpc<object>(appName, method, param, headers, timeout);
}
@@ -141,7 +147,7 @@ public partial class Synapse(SimApiOptions simApiOptions, ILogger<Synapse> logge
}
/// <summary>
///
/// 发送一个事件x
/// </summary>
/// <param name="eventName"></param>
/// <param name="param"></param>
@@ -271,10 +277,19 @@ public partial class Synapse(SimApiOptions simApiOptions, ILogger<Synapse> logge
var callClass = sp.CreateScope().ServiceProvider.GetRequiredService(tmp.Class!);
var mt = callClass.GetType().GetMethod(tmp.Method);
if (mt!.GetParameters().Length > 1)
if (mt!.GetParameters().Length > 2)
{
logger.LogError(
"Synapse Rpc Register Error: Only one or none parameter supported. {Key} -> {Method}@{Class}",
"Synapse Rpc Register Error: Only 1,2 or none parameter supported. {Key} -> {Method}@{Class}",
tmp.Key, tmp.Method, tmp.Class.Name);
continue;
}
if (mt.GetParameters().Length == 2 &&
mt.GetParameters()[1].ParameterType != typeof(Dictionary<string, string>))
{
logger.LogError(
"Synapse Rpc Register Error: RpcMethod Parameter 2 must be Dictionary<string, string>. {Key} -> {Method}@{Class}",
tmp.Key, tmp.Method, tmp.Class.Name);
continue;
}