chore: LCEngineRequestContext insteads of Request.
parent
367c57b00f
commit
4f0d7e9e9f
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
public class LCEngineFunctionParamAttribute : Attribute {
|
||||
public string ParamName {
|
||||
get;
|
||||
}
|
||||
|
||||
public LCEngineFunctionParamAttribute(string paramName) {
|
||||
if (string.IsNullOrEmpty(paramName)) {
|
||||
throw new ArgumentNullException(nameof(paramName));
|
||||
}
|
||||
ParamName = paramName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,9 +25,9 @@ namespace LeanCloud.Engine {
|
|||
|
||||
string classHookName = GetClassHookName(className, hookName);
|
||||
if (ClassHooks.TryGetValue(classHookName, out MethodInfo mi)) {
|
||||
Dictionary<string, object> dict = LCEngine.Decode(body);
|
||||
Dictionary<string, object> data = LCEngine.Decode(body);
|
||||
|
||||
LCObjectData objectData = LCObjectData.Decode(dict["object"] as Dictionary<string, object>);
|
||||
LCObjectData objectData = LCObjectData.Decode(data["object"] as Dictionary<string, object>);
|
||||
objectData.ClassName = className;
|
||||
LCObject obj = LCObject.Create(className);
|
||||
obj.Merge(objectData);
|
||||
|
@ -39,24 +39,22 @@ namespace LeanCloud.Engine {
|
|||
obj.DisableAfterHook();
|
||||
}
|
||||
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
LCUser user = null;
|
||||
if (dict.TryGetValue("user", out object userObj) &&
|
||||
if (data.TryGetValue("user", out object userObj) &&
|
||||
userObj != null) {
|
||||
user = new LCUser();
|
||||
user.Merge(LCObjectData.Decode(userObj as Dictionary<string, object>));
|
||||
LCEngineRequestContext.CurrentUser = user;
|
||||
}
|
||||
|
||||
LCClassHookRequest req = new LCClassHookRequest {
|
||||
Object = obj,
|
||||
CurrentUser = user
|
||||
};
|
||||
|
||||
LCObject result = await LCEngine.Invoke(mi, req) as LCObject;
|
||||
LCObject result = await LCEngine.Invoke(mi, new object[] { obj }) as LCObject;
|
||||
if (result != null) {
|
||||
return LCCloud.Encode(result);
|
||||
}
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ using System.Text.Json;
|
|||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using LeanCloud.Storage.Internal.Codec;
|
||||
using LeanCloud.Storage;
|
||||
using LeanCloud.Storage.Internal.Codec;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
[ApiController]
|
||||
|
@ -32,26 +32,18 @@ namespace LeanCloud.Engine {
|
|||
LCLogger.Debug(body.ToString());
|
||||
|
||||
if (Functions.TryGetValue(funcName, out MethodInfo mi)) {
|
||||
LCUser currentUser = null;
|
||||
if (Request.Headers.TryGetValue("x-lc-session", out StringValues session)) {
|
||||
currentUser = await LCUser.BecomeWithSessionToken(session);
|
||||
}
|
||||
LCCloudFunctionRequest req = new LCCloudFunctionRequest {
|
||||
Meta = new LCCloudFunctionRequestMeta {
|
||||
RemoteAddress = LCEngine.GetIP(Request)
|
||||
},
|
||||
Params = LCEngine.Decode(body),
|
||||
SessionToken = session.ToString(),
|
||||
User = currentUser
|
||||
};
|
||||
object result = await LCEngine.Invoke(mi, req);
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
object[] ps = ParseParameters(mi, body);
|
||||
object result = await LCEngine.Invoke(mi, ps.ToArray());
|
||||
|
||||
if (result != null) {
|
||||
return new Dictionary<string, object> {
|
||||
{ "result", result }
|
||||
};
|
||||
}
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
@ -64,29 +56,44 @@ namespace LeanCloud.Engine {
|
|||
LCLogger.Debug(body.ToString());
|
||||
|
||||
if (Functions.TryGetValue(funcName, out MethodInfo mi)) {
|
||||
LCUser currentUser = null;
|
||||
if (Request.Headers.TryGetValue("x-lc-session", out StringValues session)) {
|
||||
currentUser = await LCUser.BecomeWithSessionToken(session);
|
||||
}
|
||||
LCCloudRPCRequest req = new LCCloudRPCRequest {
|
||||
Meta = new LCCloudFunctionRequestMeta {
|
||||
RemoteAddress = LCEngine.GetIP(Request)
|
||||
},
|
||||
Params = LCDecoder.Decode(LCEngine.Decode(body)),
|
||||
SessionToken = session.ToString(),
|
||||
User = currentUser
|
||||
};
|
||||
object result = await LCEngine.Invoke(mi, req);
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
object[] ps = ParseParameters(mi, body);
|
||||
object result = await LCEngine.Invoke(mi, ps);
|
||||
|
||||
if (result != null) {
|
||||
return new Dictionary<string, object> {
|
||||
{ "result", LCCloud.Encode(result) }
|
||||
};
|
||||
}
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static object[] ParseParameters(MethodInfo mi, JsonElement body) {
|
||||
Dictionary<string, object> parameters = LCEngine.Decode(body);
|
||||
List<object> ps = new List<object>();
|
||||
|
||||
if (mi.GetParameters().Length > 0) {
|
||||
if (Array.Exists(mi.GetParameters(),
|
||||
p => p.GetCustomAttribute<LCEngineFunctionParamAttribute>() != null)) {
|
||||
// 如果包含 LCEngineFunctionParamAttribute 的参数,则按照配对方式传递参数
|
||||
foreach (ParameterInfo pi in mi.GetParameters()) {
|
||||
LCEngineFunctionParamAttribute attr = pi.GetCustomAttribute<LCEngineFunctionParamAttribute>();
|
||||
if (attr != null) {
|
||||
string paramName = attr.ParamName;
|
||||
ps.Add(parameters[paramName]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ps.Add(LCDecoder.Decode(LCEngine.Decode(body)));
|
||||
}
|
||||
}
|
||||
|
||||
return ps.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,12 @@ namespace LeanCloud.Engine {
|
|||
LCEngine.CheckHookKey(Request);
|
||||
|
||||
if (UserHooks.TryGetValue(LCEngine.OnSMSVerified, out MethodInfo mi)) {
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
Dictionary<string, object> dict = LCEngine.Decode(body);
|
||||
return await Invoke(mi, dict);
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
@ -42,10 +44,12 @@ namespace LeanCloud.Engine {
|
|||
LCEngine.CheckHookKey(Request);
|
||||
|
||||
if (UserHooks.TryGetValue(LCEngine.OnEmailVerified, out MethodInfo mi)) {
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
Dictionary<string, object> dict = LCEngine.Decode(body);
|
||||
return await Invoke(mi, dict);
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
@ -60,10 +64,12 @@ namespace LeanCloud.Engine {
|
|||
LCEngine.CheckHookKey(Request);
|
||||
|
||||
if (UserHooks.TryGetValue(LCEngine.OnLogin, out MethodInfo mi)) {
|
||||
LCEngine.InitRequestContext(Request);
|
||||
|
||||
Dictionary<string, object> dict = LCEngine.Decode(body);
|
||||
return await Invoke(mi, dict);
|
||||
}
|
||||
return default;
|
||||
return body;
|
||||
} catch (Exception e) {
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
@ -73,14 +79,10 @@ namespace LeanCloud.Engine {
|
|||
LCObjectData objectData = LCObjectData.Decode(dict["object"] as Dictionary<string, object>);
|
||||
objectData.ClassName = "_User";
|
||||
|
||||
LCObject obj = LCObject.Create("_User");
|
||||
obj.Merge(objectData);
|
||||
LCObject user = LCObject.Create("_User");
|
||||
user.Merge(objectData);
|
||||
|
||||
LCUserHookRequest req = new LCUserHookRequest {
|
||||
CurrentUser = obj as LCUser
|
||||
};
|
||||
|
||||
return await LCEngine.Invoke(mi, req) as LCObject;
|
||||
return await LCEngine.Invoke(mi, new object[] { user }) as LCObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ using System.Text.Json;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using LeanCloud.Common;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using LeanCloud.Common;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCEngine {
|
||||
|
@ -202,6 +203,27 @@ namespace LeanCloud.Engine {
|
|||
LCLogger.Debug($"{key} : {Environment.GetEnvironmentVariable(key)}");
|
||||
}
|
||||
|
||||
internal static async Task<object> Invoke(MethodInfo mi, object[] parameters) {
|
||||
try {
|
||||
if (mi.ReturnType == typeof(Task) ||
|
||||
(mi.ReturnType.IsGenericType && mi.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))) {
|
||||
Task task = mi.Invoke(null, parameters) as Task;
|
||||
await task;
|
||||
return task.GetType().GetProperty("Result")?.GetValue(task);
|
||||
}
|
||||
return mi.Invoke(null, parameters);
|
||||
} catch (TargetInvocationException e) {
|
||||
Exception ex = e.InnerException;
|
||||
if (ex is LCException lcEx) {
|
||||
throw new Exception(JsonConvert.SerializeObject(new Dictionary<string, object> {
|
||||
{ "code", lcEx.Code },
|
||||
{ "message", lcEx.Message }
|
||||
}));
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task<object> Invoke(MethodInfo mi, object request) {
|
||||
try {
|
||||
object[] ps = null;
|
||||
|
@ -234,6 +256,16 @@ namespace LeanCloud.Engine {
|
|||
return dict;
|
||||
}
|
||||
|
||||
internal static void InitRequestContext(HttpRequest request) {
|
||||
LCEngineRequestContext.Init();
|
||||
|
||||
LCEngineRequestContext.RemoteAddress = GetIP(request);
|
||||
|
||||
if (request.Headers.TryGetValue("x-lc-session", out StringValues session)) {
|
||||
LCEngineRequestContext.SessionToken = session;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetIP(HttpRequest request) {
|
||||
if (request.Headers.TryGetValue("x-real-ip", out StringValues ip)) {
|
||||
return ip.ToString();
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCEngineRequestContext {
|
||||
public const string RemoteAddressKey = "remoteAddressKey";
|
||||
public const string SessionTokenKey = "sessionToken";
|
||||
public const string CurrentUserKey = "currentUser";
|
||||
|
||||
private static ThreadLocal<Dictionary<string, object>> requestContext = new ThreadLocal<Dictionary<string, object>>();
|
||||
|
||||
public static void Init() {
|
||||
if (requestContext.IsValueCreated) {
|
||||
requestContext.Value.Clear();
|
||||
}
|
||||
requestContext.Value = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public static void Set(string key, object value) {
|
||||
if (!requestContext.IsValueCreated) {
|
||||
requestContext.Value = new Dictionary<string, object>();
|
||||
}
|
||||
requestContext.Value[key] = value;
|
||||
}
|
||||
|
||||
public static object Get(string key) {
|
||||
if (!requestContext.IsValueCreated) {
|
||||
return null;
|
||||
}
|
||||
return requestContext.Value[key];
|
||||
}
|
||||
|
||||
public static string RemoteAddress {
|
||||
get {
|
||||
object remoteAddress = Get(RemoteAddressKey);
|
||||
if (remoteAddress != null) {
|
||||
return remoteAddress as string;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
Set(RemoteAddressKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SessionToken {
|
||||
get {
|
||||
object sessionToken = Get(SessionTokenKey);
|
||||
if (sessionToken != null) {
|
||||
return sessionToken as string;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
Set(SessionTokenKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static LCUser CurrentUser {
|
||||
get {
|
||||
object currentUser = Get(CurrentUserKey);
|
||||
if (currentUser != null) {
|
||||
return currentUser as LCUser;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
Set(CurrentUserKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCClassHookRequest {
|
||||
public LCObject Object {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public LCUser CurrentUser {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCCloudFunctionRequestMeta {
|
||||
public string RemoteAddress {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public class LCCloudFunctionRequest {
|
||||
public LCCloudFunctionRequestMeta Meta {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Dictionary<string, object> Params {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public LCUser User {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string SessionToken {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCCloudRPCRequest {
|
||||
public LCCloudFunctionRequestMeta Meta {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public object Params {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public LCUser User {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string SessionToken {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.Engine {
|
||||
public class LCUserHookRequest {
|
||||
public LCUser CurrentUser {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ namespace Storage.Test {
|
|||
[SetUp]
|
||||
public void SetUp() {
|
||||
LCLogger.LogDelegate += Utils.Print;
|
||||
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||
//LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||
LCApplication.Initialize("8ijVI3gBAnPGynW0rVfh5gHP-gzGzoHsz", "265r8JSHhNYpV0qIJBvUWrQY", "https://8ijvi3gb.lc-cn-n1-shared.com");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
@ -24,7 +25,7 @@ namespace Storage.Test {
|
|||
{ "name", "world" }
|
||||
});
|
||||
TestContext.WriteLine(response["result"]);
|
||||
Assert.AreEqual(response["result"], "hello, world");
|
||||
Assert.AreEqual(response["result"], "Hello, world!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -41,5 +42,35 @@ namespace Storage.Test {
|
|||
Assert.NotNull(item.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RPCObject() {
|
||||
LCQuery<LCObject> query = new LCQuery<LCObject>("Todo");
|
||||
LCObject todo = await query.Get("6052cd87b725a143ea83dbf8");
|
||||
object result = await LCCloud.RPC("getTodo", todo);
|
||||
LCObject obj = result as LCObject;
|
||||
TestContext.WriteLine(obj.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RPCObjects() {
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object> {
|
||||
{ "limit", 20 }
|
||||
};
|
||||
List<object> result = await LCCloud.RPC("getTodos", parameters) as List<object>;
|
||||
IEnumerable<LCObject> todos = result.Cast<LCObject>();
|
||||
foreach (LCObject todo in todos) {
|
||||
TestContext.WriteLine(todo.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RPCObjectMap() {
|
||||
Dictionary<string, object> result = await LCCloud.RPC("getTodoMap") as Dictionary<string, object>;
|
||||
foreach (KeyValuePair<string, object> kv in result) {
|
||||
LCObject todo = kv.Value as LCObject;
|
||||
TestContext.WriteLine(todo.ObjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,15 +55,23 @@ namespace LeanCloud.Storage {
|
|||
}
|
||||
|
||||
public static object Encode(object parameters) {
|
||||
if (parameters == null) {
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
if (parameters is LCObject lcObj) {
|
||||
return EncodeLCObject(lcObj);
|
||||
} else if (parameters is IList<LCObject> list) {
|
||||
}
|
||||
|
||||
if (parameters is IList<LCObject> list) {
|
||||
List<object> l = new List<object>();
|
||||
foreach (LCObject obj in list) {
|
||||
l.Add(EncodeLCObject(obj));
|
||||
}
|
||||
return l;
|
||||
} else if (parameters is IDictionary<string, LCObject> dict) {
|
||||
}
|
||||
|
||||
if (parameters is IDictionary<string, LCObject> dict) {
|
||||
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string, LCObject> item in dict) {
|
||||
d[item.Key] = EncodeLCObject(item.Value);
|
||||
|
@ -75,9 +83,11 @@ namespace LeanCloud.Storage {
|
|||
}
|
||||
|
||||
static object EncodeLCObject(LCObject obj) {
|
||||
obj.ApplyCustomProperties();
|
||||
Dictionary<string, object> dict = LCObjectData.Encode(obj.data);
|
||||
dict["__type"] = "Object";
|
||||
foreach (KeyValuePair<string, object> kv in obj.estimatedData) {
|
||||
dict[kv.Key] = kv.Value;
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,11 @@ namespace LeanCloud.Storage {
|
|||
ApplyOperation(IgnoreHooksKey, op);
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<string> UpdatedKeys {
|
||||
get {
|
||||
public ReadOnlyCollection<string> GetUpdatedKeys() {
|
||||
if (this["_updatedKeys"] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (this["_updatedKeys"] as List<object>)
|
||||
.Cast<string>()
|
||||
.ToList()
|
||||
|
@ -55,4 +58,3 @@ namespace LeanCloud.Storage {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -494,7 +494,7 @@ namespace LeanCloud.Storage {
|
|||
data.CreatedAt = objectData.CreatedAt != null ? objectData.CreatedAt : data.CreatedAt;
|
||||
data.UpdatedAt = objectData.UpdatedAt != null ? objectData.UpdatedAt : data.UpdatedAt;
|
||||
// 先将本地的预估数据直接替换
|
||||
ApplyCustomProperties();
|
||||
data.CustomPropertyDict = estimatedData;
|
||||
// 再将服务端的数据覆盖
|
||||
foreach (KeyValuePair<string, object> kv in objectData.CustomPropertyDict) {
|
||||
string key = kv.Key;
|
||||
|
@ -509,10 +509,6 @@ namespace LeanCloud.Storage {
|
|||
isNew = false;
|
||||
}
|
||||
|
||||
public void ApplyCustomProperties() {
|
||||
data.CustomPropertyDict = estimatedData;
|
||||
}
|
||||
|
||||
void RebuildEstimatedData() {
|
||||
estimatedData = new Dictionary<string, object>();
|
||||
foreach (KeyValuePair<string, object> kv in data.CustomPropertyDict) {
|
||||
|
|
Loading…
Reference in New Issue