159 lines
5.7 KiB
C#
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);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|