parent
eb1c5c75e8
commit
01fccdc2ba
|
@ -3,7 +3,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\ln.http.api\ln.http.api.csproj" />
|
||||
|
||||
<PackageReference Include="ln.http" Version="0.3-*" />
|
||||
<PackageReference Include="ln.http" />
|
||||
<!--ProjectReference Include="..\..\ln.http\ln.http\ln.http.csproj" /-->
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace ln.http.api
|
|||
|
||||
public static HttpResponse Content(this HttpResponse response, JSONValue json)
|
||||
{
|
||||
response.SetHeader("Content-type", "application/json");
|
||||
response.ContentType("application/json");
|
||||
response.ContentWriter.Write(json.ToString());
|
||||
response.ContentWriter.Flush();
|
||||
return response;
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using ln.http.websocket;
|
||||
using ln.json;
|
||||
using ln.json.mapping;
|
||||
using ln.logging;
|
||||
using ln.type;
|
||||
|
||||
namespace ln.http.api
|
||||
{
|
||||
public class JSONEventWebSocketResponse : JSONWebSocketResponse, IDisposable
|
||||
{
|
||||
static List<JSONEventWebSocketResponse> activeWebSockets = new List<JSONEventWebSocketResponse>();
|
||||
public static void SendUpdates()
|
||||
{
|
||||
if (Monitor.TryEnter(activeWebSockets, 100))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (JSONEventWebSocketResponse websocket in activeWebSockets)
|
||||
websocket.SendUpdate();
|
||||
} catch (Exception e)
|
||||
{
|
||||
Logging.Log(e);
|
||||
} finally
|
||||
{
|
||||
Monitor.Exit(activeWebSockets);
|
||||
}
|
||||
} else {
|
||||
Logging.Log(LogLevel.WARNING, "JSONEventWebSocketResponse: SendUpdates(): failed to enter critical section");
|
||||
}
|
||||
}
|
||||
|
||||
public string DefaultTargetName { get; set; }
|
||||
|
||||
Dictionary<string, Target> publishedTargets = new Dictionary<string, Target>();
|
||||
|
||||
|
||||
public JSONEventWebSocketResponse()
|
||||
{
|
||||
OnWebSocketReceivedJSON += JSONReceived;
|
||||
OnWebSocketStateChanged += (WebSocketResponse websocket, WebSocketState newState) => {
|
||||
switch (newState)
|
||||
{
|
||||
case WebSocketState.CLOSED:
|
||||
lock (activeWebSockets)
|
||||
activeWebSockets.Remove(this);
|
||||
break;
|
||||
case WebSocketState.OPEN:
|
||||
lock (activeWebSockets)
|
||||
activeWebSockets.Add(this);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public JSONEventWebSocketResponse(string defaultTargetName)
|
||||
:this()
|
||||
{
|
||||
DefaultTargetName = defaultTargetName;
|
||||
}
|
||||
|
||||
public JSONEventWebSocketResponse(object defaultInstance)
|
||||
:this()
|
||||
{
|
||||
AddTarget(null, defaultInstance, defaultInstance.GetType().GetCustomAttribute<ESTargetAttribute>()?.PublishPublicMembers ?? false, out Target target);
|
||||
DefaultTargetName = target.Name;
|
||||
}
|
||||
public JSONEventWebSocketResponse(object defaultInstance, bool publishPublicMembers)
|
||||
:this()
|
||||
{
|
||||
AddTarget(null, defaultInstance, publishPublicMembers, out Target target);
|
||||
DefaultTargetName = target.Name;
|
||||
}
|
||||
|
||||
public void AddTarget(object instance) => AddTarget(null, instance, instance.GetType().GetCustomAttribute<ESTargetAttribute>()?.PublishPublicMembers ?? false);
|
||||
public void AddTarget(object instance, bool publishPublicMembers) => AddTarget(null, instance, publishPublicMembers);
|
||||
public void AddTarget(string targetName, object instance, bool publishPublicMembers) => AddTarget(targetName, instance, publishPublicMembers, out Target target);
|
||||
public void AddTarget(string targetName, object instance, bool publishPublicMembers, out Target target)
|
||||
{
|
||||
target = new Target(targetName, instance, publishPublicMembers);
|
||||
publishedTargets.Add(target.Name, target);
|
||||
}
|
||||
|
||||
|
||||
void JSONReceived(JSONWebSocketResponse sender, JSONValue json)
|
||||
{
|
||||
if (json is JSONObject jsonMessage)
|
||||
{
|
||||
string eventName = jsonMessage["event"].ToNative().ToString();
|
||||
string targetName = jsonMessage["target"].ToNative().ToString();
|
||||
JSONValue jsonValue = jsonMessage["value"];
|
||||
|
||||
Target target = publishedTargets[targetName ?? DefaultTargetName];
|
||||
|
||||
switch (eventName)
|
||||
{
|
||||
case "action":
|
||||
target.Call(jsonValue);
|
||||
break;
|
||||
case "set":
|
||||
target.SetProperty(jsonValue);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SendUpdate()
|
||||
{
|
||||
JSONObject jsonUpdateValue = new JSONObject();
|
||||
JSONObject jsonUpdateMessage = new JSONObject()
|
||||
.Add("event", "update")
|
||||
.Add("value", jsonUpdateValue);
|
||||
|
||||
foreach (Target target in publishedTargets.Values)
|
||||
jsonUpdateValue.Add(target.Name, target.CreateUpdateValue());
|
||||
|
||||
Send(jsonUpdateMessage);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (activeWebSockets)
|
||||
activeWebSockets.Remove(this);
|
||||
}
|
||||
|
||||
public class Target
|
||||
{
|
||||
public string Name { get; }
|
||||
public object Instance { get; }
|
||||
|
||||
Dictionary<string,MethodInfo> publishedMethods = new Dictionary<string, MethodInfo>();
|
||||
Dictionary<string,PropertyInfo> publishedProperties = new Dictionary<string, PropertyInfo>();
|
||||
Dictionary<string,FieldInfo> publishedFields = new Dictionary<string, FieldInfo>();
|
||||
|
||||
|
||||
public Target(object instance, bool publishPublicMembers) :this(instance.GetType().Name, instance, publishPublicMembers) { }
|
||||
public Target(object instance) :this(instance.GetType().Name, instance, false) { }
|
||||
public Target(string targetName, object instance) : this(targetName, instance, false) { }
|
||||
public Target(string targetName, object instance, bool publishPublicMembers)
|
||||
{
|
||||
Instance = instance;
|
||||
Name = targetName ?? Instance.GetType().Name;
|
||||
|
||||
Initialize(publishPublicMembers);
|
||||
}
|
||||
|
||||
|
||||
void Initialize(bool publishPublicMembers)
|
||||
{
|
||||
foreach (MethodInfo methodInfo in Instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
ESMethodAttribute methodAttribute = methodInfo.GetCustomAttribute<ESMethodAttribute>();
|
||||
if ((methodAttribute != null) || (publishPublicMembers && methodInfo.IsPublic))
|
||||
publishedMethods.Add(methodAttribute?.Name ?? methodInfo.Name, methodInfo);
|
||||
}
|
||||
|
||||
foreach (FieldInfo fieldInfo in Instance.GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
ESPropertyAttribute propertyAttribute = fieldInfo.GetCustomAttribute<ESPropertyAttribute>();
|
||||
if ((propertyAttribute != null) || (publishPublicMembers && fieldInfo.IsPublic))
|
||||
publishedFields.Add(propertyAttribute?.Name ?? fieldInfo.Name, fieldInfo);
|
||||
}
|
||||
|
||||
foreach (PropertyInfo propertyInfo in Instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
ESPropertyAttribute propertyAttribute = propertyInfo.GetCustomAttribute<ESPropertyAttribute>();
|
||||
if ((propertyAttribute != null) || (publishPublicMembers && propertyInfo.GetMethod.IsPublic))
|
||||
publishedProperties.Add(propertyAttribute?.Name ?? propertyInfo.Name, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONValue CreateUpdateValue()
|
||||
{
|
||||
JSONObject jsonUpdate = new JSONObject();
|
||||
|
||||
foreach (PropertyInfo propertyInfo in publishedProperties.Values)
|
||||
if (propertyInfo.CanRead)
|
||||
jsonUpdate.Add(propertyInfo.Name, JSONMapper.DefaultMapper.ToJson(propertyInfo.GetValue(Instance)));
|
||||
|
||||
foreach (FieldInfo fieldInfo in publishedFields.Values)
|
||||
jsonUpdate.Add(fieldInfo.Name, JSONMapper.DefaultMapper.ToJson(fieldInfo.GetValue(Instance)));
|
||||
|
||||
return jsonUpdate;
|
||||
}
|
||||
|
||||
public void Call(JSONValue callValue)
|
||||
{
|
||||
string methodName;
|
||||
MethodInfo methodInfo;
|
||||
object[] arguments;
|
||||
|
||||
if (callValue is JSONString jsonMethodName)
|
||||
{
|
||||
methodName = jsonMethodName.Value;
|
||||
methodInfo = publishedMethods[methodName];
|
||||
arguments = new object[0];
|
||||
}
|
||||
else if (callValue is JSONObject callObject)
|
||||
{
|
||||
methodName = callObject["method"].ToNative().ToString();
|
||||
methodInfo = publishedMethods[methodName];
|
||||
if (!JSONMapper.DefaultMapper.MapMethodParameters(methodInfo, callObject["arguments"], out arguments))
|
||||
throw new ArgumentException(nameof(callValue));
|
||||
}
|
||||
else
|
||||
throw new ArgumentOutOfRangeException(nameof(callValue));
|
||||
|
||||
methodInfo.Invoke(Instance, arguments);
|
||||
}
|
||||
|
||||
public void Call(string methodName, object[] arguments)
|
||||
{
|
||||
MethodInfo methodInfo = publishedMethods[methodName];
|
||||
methodInfo.Invoke(Instance, arguments);
|
||||
}
|
||||
|
||||
public void SetProperty(JSONValue jsonValue)
|
||||
{
|
||||
if (jsonValue is JSONObject setObject)
|
||||
{
|
||||
string propertyName = setObject["name"].ToNative().ToString();
|
||||
if (publishedProperties.TryGetValue(propertyName, out PropertyInfo propertyInfo) && propertyInfo.CanWrite)
|
||||
propertyInfo.SetValue(Instance, Cast.To(setObject["value"].ToNative(), propertyInfo.PropertyType));
|
||||
else if (publishedFields.TryGetValue(propertyName, out FieldInfo fieldInfo) && !fieldInfo.IsInitOnly)
|
||||
fieldInfo.SetValue(Instance, Cast.To(setObject["value"].ToNative(), fieldInfo.FieldType));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using ln.http.websocket;
|
||||
using ln.json;
|
||||
using ln.logging;
|
||||
|
||||
namespace ln.http.api
|
||||
{
|
||||
|
||||
public delegate void WebSocketReceivedJSON(JSONWebSocketResponse sender, JSONValue json);
|
||||
|
||||
public class JSONWebSocketResponse : WebSocketResponse
|
||||
{
|
||||
public event WebSocketReceivedJSON OnWebSocketReceivedJSON;
|
||||
|
||||
public JSONWebSocketResponse(){ }
|
||||
|
||||
public override void Received(string textMessage)
|
||||
{
|
||||
base.Received(textMessage);
|
||||
try
|
||||
{
|
||||
JSONValue json = JSONParser.Parse(textMessage);
|
||||
try {
|
||||
OnWebSocketReceivedJSON?.Invoke(this, json);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Logging.Log(ex);
|
||||
}
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
Logging.Log(LogLevel.ERROR, "JSONWebSocketResponse: received unparsable json message: {0}", textMessage);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(JSONValue json) => Send(json.ToString());
|
||||
}
|
||||
}
|
|
@ -51,12 +51,14 @@ namespace ln.http.api
|
|||
return httpResponse;
|
||||
|
||||
if (result is JSONValue jsonResult)
|
||||
return defaultResponseFactory().Content(jsonResult);
|
||||
return defaultResponseFactory()
|
||||
.ContentType("application/json")
|
||||
.Content(jsonResult);
|
||||
|
||||
if (JSONMapper.DefaultMapper.Serialize(result, out JSONValue json))
|
||||
return defaultResponseFactory()
|
||||
.ContentType("application/json")
|
||||
.Content(result.ToString())
|
||||
.Content(json.ToString())
|
||||
;
|
||||
|
||||
return HttpResponse.InternalServerError().Content("Method result could not be serialized");
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace ln.http.api
|
||||
{
|
||||
public class ESMethodAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace ln.http.api
|
||||
{
|
||||
public class ESPropertyAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool ReadOnly { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ln.http.api
|
||||
{
|
||||
public class ESTargetAttribute : Attribute
|
||||
{
|
||||
public bool PublishPublicMembers { get; set; }
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>0.0.4</Version>
|
||||
<Version>0.0.6</Version>
|
||||
<Authors>Harald Wolff-Thobaben</Authors>
|
||||
<Company>l--n.de</Company>
|
||||
<Description>Framework to create REST like APIs</Description>
|
||||
|
@ -13,8 +13,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ln.http" Version="0.3" />
|
||||
<PackageReference Include="ln.json" Version="1.0" />
|
||||
<PackageReference Include="ln.http" Version="0.4.1" />
|
||||
<PackageReference Include="ln.json" Version="1.0.7" />
|
||||
<PackageReference Include="ln.type" Version="0.1.6" />
|
||||
<!--ProjectReference Include="..\..\ln.http\ln.http\ln.http.csproj" /-->
|
||||
</ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue