using Newtonsoft.Json; using System; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; using VCommon.Json; using VCommon.Logging; using VCommon.VOpenApi.Json; namespace VCommon.VOpenApi { [DebuggerDisplay("ApiBind {RawRoute}")] public class ApiBind { private static readonly JsonReturnValueSerializer ReturnValueSerializer = new JsonReturnValueSerializer(); private readonly Type _interfaceType; private readonly Func _preCompiledInvoke; public string RawRoute { get; } public Type ApiResourceType { get; } public MethodInfo ApiMethod { get; } public Type InputParamType { get; } public string ResourceName { get; } public string Name { get; } public ApiBind(MethodInfo apiMethod, string resourceName, string name, Type interfaceType, string rawRoute) { _interfaceType = interfaceType; ApiMethod = apiMethod; ResourceName = resourceName; Name = name; ApiResourceType = apiMethod.DeclaringType; var parameters = ApiMethod.GetParameters(); if (1 == parameters.Length) { InputParamType = parameters[0].ParameterType; } RawRoute = rawRoute; //Compile method invoke reduce time { var hasParam = InputParamType != null; var hasReturn = apiMethod.ReturnType != typeof(void); var pInstance = Expression.Parameter(typeof(object), "instance"); var unboxInstance = Expression.Convert(pInstance, interfaceType); var pInput = Expression.Parameter(typeof(object), "input"); MethodCallExpression invoke; if (hasParam) { var unboxInput = Expression.Convert(pInput, InputParamType); invoke = Expression.Call(unboxInstance, apiMethod, unboxInput); } else { invoke = Expression.Call(unboxInstance, apiMethod); } if (hasParam) { if (hasReturn) { var cvr = Expression.Convert(invoke, typeof(object)); var lam = Expression.Lambda>(cvr, pInstance, pInput); _preCompiledInvoke = lam.Compile(); } else { var lam = Expression.Lambda>(invoke, pInstance, pInput); var del = lam.Compile(); _preCompiledInvoke = (instance, input) => { del(instance, input); return null; }; } } else { if (hasReturn) { var cvr = Expression.Convert(invoke, typeof(object)); var lam = Expression.Lambda>(cvr, pInstance); var del = lam.Compile(); _preCompiledInvoke = (instance, _) => del(instance); } else { var lam = Expression.Lambda>(invoke, pInstance); var del = lam.Compile(); _preCompiledInvoke = (instance, _) => { del(instance); return null; }; } } } } public async Task Invoke(Func resolveFunc, Stream inputStream, Stream outputStream) { object inputParam = null; if (null != InputParamType) { //TODO: PACT? Scheme check? var inputReader = new StreamReader(inputStream, Encoding.UTF8); var input = await inputReader.ReadToEndAsync(); try { inputParam = VJsonSerializer.Deserialize(input, InputParamType); } catch (JsonReaderException exception) { throw new VApiArgumentException("参数错误:" + exception.Message); } catch (JsonSerializationException exception) { throw new VApiArgumentException($"参数错误:{exception.Message}{exception.InnerException?.Message}"); } catch (Exception exception) { Logger.Error("ApiBind: Error on parsing input", new { inputJson = input, exception }); throw new VApiArgumentException("参数错误"); } } var inst = resolveFunc(_interfaceType); var returnValue = _preCompiledInvoke(inst, inputParam); var output = ReturnValueSerializer.SerializeObject(returnValue); var bufOutput = Encoding.UTF8.GetBytes(output); await outputStream.WriteAsync(bufOutput, 0, bufOutput.Length); } } }