ln.http/ln.http/HttpEndpointController.cs

181 lines
7.9 KiB
C#

using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using ln.json;
using ln.json.mapping;
using ln.protocols.helper;
using ln.type;
namespace ln.http
{
public abstract class HttpEndpointController : HttpRouter
{
public HttpEndpointController()
{
Initialize();
}
void Initialize()
{
foreach (MethodInfo methodInfo in GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
foreach (MapAttribute mapAttribute in methodInfo.GetCustomAttributes<MapAttribute>())
{
MappedEndpoint mappedEndpoint = CreateMapping(mapAttribute, methodInfo);
Map(mappedEndpoint.HttpMethod, mappedEndpoint.Path, mappedEndpoint.Route);
}
}
}
MappedEndpoint CreateMapping(MapAttribute mapAttribute, MethodInfo methodInfo)
{
if (methodInfo.ReturnType == typeof(void))
return new MappedEndpoint.VoidEndpoint(this, mapAttribute, methodInfo);
return new MappedEndpoint.NonVoidEndpoint(this, mapAttribute, methodInfo);
}
public abstract class MappedEndpoint
{
public HttpMethod HttpMethod { get; }
public string Path { get; }
private HttpEndpointController EndpointController;
public MethodInfo MethodInfo { get; }
private ParameterInfo[] _parameterInfos;
private HttpArgumentSourceAttribute[] _argumentSourceAttributes;
private Type _returnType;
public MappedEndpoint(HttpEndpointController endpointController, MapAttribute mapAttribute, MethodInfo methodInfo)
{
EndpointController = endpointController;
HttpMethod = mapAttribute.Method;
Path = mapAttribute.Path;
MethodInfo = methodInfo;
_parameterInfos = MethodInfo.GetParameters();
_returnType = MethodInfo.ReturnType;
_argumentSourceAttributes = _parameterInfos
.Select((pi) => pi.GetCustomAttribute<HttpArgumentSourceAttribute>()).ToArray();
}
public abstract bool Route(HttpContext httpContext);
bool TryApplyParameters(HttpContext httpContext, out object[] parameters)
{
parameters = new object[_parameterInfos.Length];
for (int n = 0; n < _parameterInfos.Length; n++)
{
ParameterInfo parameterInfo = _parameterInfos[n];
if (parameterInfo.ParameterType.Equals(typeof(HttpContext)))
parameters[n] = httpContext;
else if (parameterInfo.ParameterType.Equals(typeof(HttpRequest)))
parameters[n] = httpContext.Request;
else if (parameterInfo.ParameterType.Equals(typeof(HttpPrincipal)))
parameters[n] = httpContext.AuthenticatedPrincipal;
else if (TryFindArgumentByName(
httpContext,
_argumentSourceAttributes[n]?.ArgumentSource ?? HttpArgumentSource.AUTO,
_argumentSourceAttributes[n]?.ArgumentName ?? _parameterInfos[n].Name,
out string parameterValue
))
{
if (!Cast.To(parameterValue, _parameterInfos[n].ParameterType, out parameters[n]))
{
parameters[n] = TypeDescriptor
.GetConverter(parameterInfo.ParameterType)
.ConvertFromInvariantString(parameterValue);
}
}
else if (_argumentSourceAttributes[n].ArgumentSource == HttpArgumentSource.CONTENT)
{
if (httpContext.Request.Headers.TryGetValue("Content-Type", out string contentType) &&
contentType.Equals("application/json"))
{
using (TextReader reader = httpContext.Request.ContentStream.TextReader())
parameters[n] = JSONMapper.DefaultMapper.FromJson(JSONParser.Parse(reader), _parameterInfos[n].ParameterType);
}
}
else if (_parameterInfos[n].HasDefaultValue)
{
parameters[n] = _parameterInfos[n].DefaultValue;
}
else
return false;
}
return true;
}
bool TryFindArgumentByName(HttpContext httpContext, HttpArgumentSource argumentSource, string parameterName, out string parameterValue)
{
if (((argumentSource & HttpArgumentSource.PARAMETER) == HttpArgumentSource.PARAMETER) && httpContext.Request.TryGetParameter(parameterName, out parameterValue))
return true;
else if (((argumentSource & HttpArgumentSource.HEADER) == HttpArgumentSource.HEADER) &&
(httpContext.Request.Headers.TryGetValue(parameterName, out parameterValue)))
return true;
else if (((argumentSource & HttpArgumentSource.QUERY) == HttpArgumentSource.QUERY) &&
(httpContext.Request.Query.TryGetValue(parameterName, out parameterValue)))
return true;
parameterValue = null;
return false;
}
private object InvokeMethod(HttpContext httpContext)
{
if (!TryApplyParameters(httpContext, out object[] parameters))
return HttpResponse.InternalServerError().Content("could not apply parameters");
else
return MethodInfo.Invoke(EndpointController, parameters);
}
public class NonVoidEndpoint : MappedEndpoint
{
public NonVoidEndpoint(HttpEndpointController endpointController, MapAttribute mapAttribute, MethodInfo methodInfo)
: base(endpointController, mapAttribute, methodInfo)
{
}
public override bool Route(HttpContext httpContext)
{
object returnedValue = InvokeMethod(httpContext);
if (returnedValue is HttpResponse httpResponse)
httpContext.Response = httpResponse;
else if ((returnedValue is JSONValue jsonResult) || (JSONMapper.DefaultMapper.Serialize(returnedValue, out jsonResult)))
httpContext.Response = HttpResponse.Default(httpContext.Request.Method)
.Content(jsonResult);
else
httpContext.Response = HttpResponse.InternalServerError().Content("Method result could not be serialized");
return true;
}
}
public class VoidEndpoint : MappedEndpoint
{
public VoidEndpoint(HttpEndpointController endpointController, MapAttribute mapAttribute, MethodInfo methodInfo)
: base(endpointController, mapAttribute, methodInfo)
{
}
public override bool Route(HttpContext httpContext)
{
object returnedValue = InvokeMethod(httpContext);
if (returnedValue is HttpResponse httpResponse)
httpContext.Response = httpResponse;
else
httpContext.Response ??= HttpResponse.Default(httpContext.Request.Method);
return true;
}
}
}
}
}