ln.http.api/ln.http.api/WebApiController.cs

159 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using ln.http.api.attributes;
using ln.http.exceptions;
using ln.http.router;
using ln.json;
using ln.json.mapping;
namespace ln.http.api
{
public abstract class WebApiController : SimpleRouter
{
Dictionary<string,EndpointMethodCache> methodCaches = new Dictionary<string, EndpointMethodCache>();
public WebApiController()
{
Initialize();
}
void Initialize()
{
foreach (MethodInfo methodInfo in GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
foreach (EndpointAttribute endpointAttribute in methodInfo.GetCustomAttributes<EndpointAttribute>())
{
if (endpointAttribute != null)
{
EndpointMethodCache methodCache = GetOrCreateMethodCache(endpointAttribute.Route);
methodCache.Add(endpointAttribute.Method, methodInfo);
}
}
}
}
HttpResponse MapRequest(HttpRequest request, MethodInfo methodInfo)
{
MapArguments(request, methodInfo, out object[] arguments);
object result = methodInfo.Invoke(this, arguments);
if (!methodInfo.ReturnType.Equals(typeof(HttpResponse)))
{
if (!typeof(JSONValue).IsAssignableFrom(methodInfo.ReturnType))
{
if (!JSONMapper.DefaultMapper.Serialize(result, out JSONValue json))
return HttpResponse.InternalServerError().Content("Method result could not be serialized");
result = json;
}
return HttpResponse.OK().Content((JSONValue)result);
}
return HttpResponse.OK().Content(result.ToString());
}
void MapArguments(HttpRequest request, MethodInfo methodInfo,out object[] arguments)
{
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
arguments = new object[parameterInfos.Length];
JSONValue jsonContent = null;
if (request.GetRequestHeader("content-type", "").Equals("application/json"))
{
jsonContent = JSONParser.Parse(request.ContentReader.ReadToEnd());
}
for (int n=0;n<arguments.Length;n++)
{
ParameterInfo parameterInfo = parameterInfos[n];
ArgumentSourceAttribute sourceAttribute = parameterInfo.GetCustomAttribute<ArgumentSourceAttribute>() ?? ArgumentSourceAttribute.Default;
if (!FindArgumentByName(sourceAttribute, request, jsonContent, parameterInfo.Name, out object value))
{
if (parameterInfo.HasDefaultValue)
{
arguments[n] = parameterInfo.DefaultValue;
} else
throw new BadRequestException();
} else {
try
{
if (value is JSONValue jsonValue)
{
if (!JSONMapper.DefaultMapper.Deserialize(jsonValue, parameterInfo.ParameterType, out arguments[n]))
throw new BadRequestException();
} else
{
arguments[n] = Convert.ChangeType(value, parameterInfo.ParameterType);
}
} catch (FormatException)
{
throw new BadRequestException();
}
}
}
}
bool FindArgumentByName(ArgumentSourceAttribute sourceAttribute, HttpRequest request, JSONValue jsonContent, string parameterName, out object value)
{
parameterName = sourceAttribute.SourceName ?? parameterName;
if (((sourceAttribute.Source & ArgumentSource.PARAMETER) == ArgumentSource.PARAMETER) && request.ContainsParameter(parameterName))
{
value = request.GetParameter(parameterName);
return true;
} else if (((sourceAttribute.Source & ArgumentSource.CONTENT) == ArgumentSource.CONTENT) && (jsonContent is JSONObject jsonObject) && (jsonObject.ContainsKey(parameterName)))
{
value = jsonObject[parameterName];
return true;
}
value = null;
return false;
}
EndpointMethodCache GetOrCreateMethodCache(string route)
{
if (!methodCaches.TryGetValue(route, out EndpointMethodCache methodCache))
{
methodCache = new EndpointMethodCache();
methodCaches.Add(route, methodCache);
AddSimpleRoute(route, (request) => {
MethodInfo methodInfo = methodCache[Enum.Parse<HttpMethod>(request.Method)];
return MapRequest(request, methodInfo);
});
}
return methodCache;
}
class EndpointMethodCache
{
Dictionary<HttpMethod,MethodInfo> cache = new Dictionary<HttpMethod, MethodInfo>();
public EndpointMethodCache()
{
}
public MethodInfo this[HttpMethod httpMethod]
{
get => cache[httpMethod];
set => cache[httpMethod] = value;
}
public void Add(HttpMethod httpMethod, MethodInfo methodInfo) => cache.Add(httpMethod, methodInfo);
public void Remove(HttpMethod httpMethod, MethodInfo methodInfo) => cache.Remove(httpMethod);
}
}
}