using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TOOHUCardAPI.Data; using TOOHUCardAPI.Data.Services; using MethodMap = System.Collections.Generic.Dictionary; namespace TOOHUCardAPI.Controllers { public class MethodBasedController : ControllerBase { public delegate Task EndpointHandler(string requestBody); /** * The game uses a single endpoint for player data. * The object they send has a method field that's used to decide what's being done * So let's use a single entry point and redirect based on that */ [HttpPost] public async Task EntryPoint([FromBody] object bodyObj) { string body = JsonConvert.SerializeObject(bodyObj); JObject request = JObject.Parse(body); string method = request["method"].ToString(); try { return await InvokeEndpointHandlerForMethod(this, method, body); } catch (TooHooException e) { return Ok(e.response); } } protected Task InvokeEndpointHandlerForMethod(object _this, string method, string body) { MethodMap registeredEndpointHandlers = GetMethodMapForType(); if (registeredEndpointHandlers.ContainsKey(method)) { return ((EndpointHandler) registeredEndpointHandlers[method] .CreateDelegate(typeof(EndpointHandler), _this))(body); } throw new MissingEndpointHandlerException(method); } private MethodMap GetMethodMapForType() { var methodMapByType = MethodMapService.MethodMapByType; if (!methodMapByType.ContainsKey(typeof(T))) { methodMapByType[typeof(T)] = RegisterEndpointHandlers(); } return methodMapByType[typeof(T)]; } private static MethodMap RegisterEndpointHandlers() { MethodInfo[] methods = typeof(T).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); return methods .Aggregate(new MethodMap(), (handlers, m) => { Attribute attr = m.GetCustomAttribute(typeof(EndpointHandlerAttribute), false); if (attr != null) { EndpointHandlerAttribute e = (EndpointHandlerAttribute) attr; handlers.Add(e.Method, m); } return handlers; }); } } public class MissingEndpointHandlerException : Exception { public MissingEndpointHandlerException(string method) : base($"Handler for [{method}] is either missing or incorrectly setup.") { } } }