public LightNodeServerMiddleware(AppFunc next, LightNodeOptions options, Assembly[] hostAssemblies) { this.next = next; this.useOtherMiddleware = options.UseOtherMiddleware; this.engine = new LightNodeServer(options); this.engine.RegisterHandler(hostAssemblies); }
public Task Execute(LightNodeOptions options, OperationContext context) { int index = -1; Func <Task> invokeRecursive = null; invokeRecursive = () => { index += 1; if (filters.Length != index) { // chain next filter return(filters[index].Invoke(context, invokeRecursive)); } else { // execute operation return(ExecuteOperation(options, context)); } }; return(invokeRecursive()); }
/// <summary> /// Owin よみこみ /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { // 各種有効化 app.UseRequestScopeContext(); // Context の取得 app.UseFileServer(); // FileServer 用の読み込みだよ (Owin からFileアクセス許可を許容するために必要) // api 処理 app.Map("/api", builder => { var option = new LightNodeOptions(AcceptVerbs.Get | AcceptVerbs.Post, new JsonNetContentFormatter()) { ParameterEnumAllowsFieldNameParse = true, // If you want to use enums human readable display on Swagger, set to true ErrorHandlingPolicy = ErrorHandlingPolicy.ReturnInternalServerErrorIncludeErrorDetails, OperationMissingHandlingPolicy = OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails, }; // LightNode つかうよ builder.UseLightNode(option); }); // page 処理 app.Map("/pages", builder => { // LightNode つかうにゃ builder.UseLightNode(new LightNodeOptions(AcceptVerbs.Get, new JsonNetContentFormatter())); }); // Swagger くみこむにゃん app.Map("/swagger", builder => { var xmlName = "LightNodeSelfHost.xml"; var xmlPath = AppDomain.CurrentDomain.BaseDirectory + @"bin\" + xmlName; builder.UseLightNodeSwagger(new SwaggerOptions("LightNodeSelfHost", "/api") { XmlDocumentPath = xmlPath, IsEmitEnumAsString = true // Enumを文字列で並べたいならtrueに }); }); }
public LightNodeServer(LightNodeOptions options) { this.options = options; }
public static IAppBuilder UseLightNode(this IAppBuilder app, LightNodeOptions options) { return app.Use(typeof(LightNodeServerMiddleware), options); }
public static IAppBuilder UseLightNode(this IAppBuilder app, LightNodeOptions options, params Assembly[] hostAssemblies) { return app.Use(typeof(LightNodeServerMiddleware), options, hostAssemblies); }
public LightNodeServerMiddleware(AppFunc next, LightNodeOptions options) : this(next, options, AppDomain.CurrentDomain.GetAssemblies()) { }
public static void RegisterOptions(LightNodeOptions options) { LightNodeServer.options = options; }
async Task ExecuteOperation(LightNodeOptions options, OperationContext context) { // prepare var handler = this; var environment = context.Environment; var methodParameters = context.Parameters; bool isVoid = true; object result = null; switch (handler.handlerBodyType) { case HandlerBodyType.Action: handler.methodActionBody(environment, methodParameters); break; case HandlerBodyType.Func: isVoid = false; result = handler.methodFuncBody(environment, methodParameters); break; case HandlerBodyType.AsyncAction: var actionTask = handler.methodAsyncActionBody(environment, methodParameters); await actionTask.ConfigureAwait(false); break; case HandlerBodyType.AsyncFunc: isVoid = false; var funcTask = handler.methodAsyncFuncBody(environment, methodParameters); await funcTask.ConfigureAwait(false); var extractor = taskResultExtractors[funcTask.GetType()]; result = extractor(funcTask); break; default: throw new InvalidOperationException("critical:register code is broken"); } if (!isVoid) { // append header var responseHeader = environment["owin.ResponseHeaders"] as IDictionary <string, string[]>; var encoding = context.ContentFormatter.Encoding; responseHeader["Content-Type"] = new[] { context.ContentFormatter.MediaType + ((encoding == null) ? "" : "; charset=" + encoding.WebName) }; environment.EmitOK(); var responseStream = environment["owin.ResponseBody"] as Stream; if (options.StreamWriteOption == StreamWriteOption.DirectWrite) { context.ContentFormatter.Serialize(new UnclosableStream(responseStream), result); } else { using (var buffer = new MemoryStream()) { context.ContentFormatter.Serialize(new UnclosableStream(buffer), result); responseHeader["Content-Length"] = new[] { buffer.Position.ToString() }; buffer.Position = 0; if (options.StreamWriteOption == StreamWriteOption.BufferAndWrite) { buffer.CopyTo(responseStream); // not CopyToAsync } else { await buffer.CopyToAsync(responseStream).ConfigureAwait(false); } } } return; } else { environment.EmitNoContent(); return; } }
public OperationHandler(LightNodeOptions options, Type classType, MethodInfo methodInfo) { this.ClassName = classType.Name; this.MethodName = methodInfo.Name; this.Arguments = methodInfo.GetParameters() .Select(x => new ParameterInfoSlim(x)) .ToArray(); this.ReturnType = methodInfo.ReturnType; this.filters = options.Filters .Concat(classType.GetCustomAttributes <LightNodeFilterAttribute>(true)) .Concat(methodInfo.GetCustomAttributes <LightNodeFilterAttribute>(true)) .OrderBy(x => x.Order) .ToArray(); this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast <Attribute>() .ToLookup(x => x.GetType()); foreach (var argument in this.Arguments) { if (!TypeBinder.IsAllowType(argument.ParameterType)) { throw new InvalidOperationException(string.Format("parameter is not allowed, class:{0} method:{1} paramName:{2} paramType:{3}", classType.Name, methodInfo.Name, argument.Name, argument.ParameterType.FullName)); } } // prepare lambda parameters var envArg = Expression.Parameter(typeof(IDictionary <string, object>), "environment"); var envBind = Expression.Bind(typeof(LightNodeContract).GetProperty("Environment"), envArg); var args = Expression.Parameter(typeof(object[]), "args"); var parameters = methodInfo.GetParameters() .Select((x, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), x.ParameterType)) .ToArray(); // Task or Task<T> if (typeof(Task).IsAssignableFrom(this.ReturnType)) { // (object[] args) => new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda <Func <IDictionary <string, object>, object[], Task> >( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); if (this.ReturnType.IsGenericType && this.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)) { this.handlerBodyType = HandlerBodyType.AsyncFunc; this.methodAsyncFuncBody = lambda.Compile(); lock (taskResultExtractors) { if (!taskResultExtractors.ContainsKey(this.ReturnType)) { // (object task) => (object)((Task<>).Result) var taskParameter = Expression.Parameter(typeof(object), "task"); var resultLambda = Expression.Lambda <Func <object, object> >( Expression.Convert( Expression.Property( Expression.Convert(taskParameter, this.ReturnType), "Result"), typeof(object)), taskParameter); var compiledResultLambda = resultLambda.Compile(); taskResultExtractors[this.ReturnType] = compiledResultLambda; } } } else { this.handlerBodyType = HandlerBodyType.AsyncAction; this.methodAsyncActionBody = lambda.Compile(); } } else if (this.ReturnType == typeof(void)) // of course void { // (object[] args) => { new X().M((T1)args[0], (T2)args[1])... } var lambda = Expression.Lambda <Action <IDictionary <string, object>, object[]> >( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); this.handlerBodyType = HandlerBodyType.Action; this.methodActionBody = lambda.Compile(); } else // return T { // (object[] args) => (object)new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda <Func <IDictionary <string, object>, object[], object> >( Expression.Convert( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters) , typeof(object)), envArg, args); this.handlerBodyType = HandlerBodyType.Func; this.methodFuncBody = lambda.Compile(); } }
internal static object[] BindParameter(IDictionary <string, object> environment, LightNodeOptions options, ValueProvider valueProvider, ParameterInfoSlim[] arguments) { var methodParameters = new object[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { var item = arguments[i]; var _values = valueProvider.GetValue(item.Name); var value = _values as string; var values = _values as List <string>; var isEmpty = _values == null; if (isEmpty && !item.ParameterTypeIsArray) { if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { environment.EmitBadRequest(); environment.EmitStringMessage("Lack of Parameter:" + item.Name); return(null); } } else if (!item.ParameterTypeIsArray) { var conv = TypeBinder.GetConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (conv == null) { throw new InvalidOperationException("critical:register code is broken"); } object pValue; if (conv(value ?? values[0], out pValue)) { methodParameters[i] = pValue; continue; } else if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { environment.EmitBadRequest(); environment.EmitStringMessage("Mismatch Parameter Type:" + item.Name); return(null); } } var arrayConv = TypeBinder.GetArrayConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (arrayConv == null) { throw new InvalidOperationException("critical:register code is broken"); } methodParameters[i] = arrayConv((values != null) ? values : (value != null) ? new[] { value } : Enumerable.Empty <string>()); continue; } return(methodParameters); }
public Task Execute(LightNodeOptions options, OperationContext context) { int index = -1; Func<Task> invokeRecursive = null; invokeRecursive = () => { index += 1; if (filters.Length != index) { // chain next filter return filters[index].Invoke(context, invokeRecursive); } else { // execute operation return ExecuteOperation(options, context); } }; return invokeRecursive(); }
internal static object[] BindParameter(IDictionary<string, object> environment, LightNodeOptions options, ValueProvider valueProvider, ParameterInfoSlim[] arguments) { var methodParameters = new object[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { var item = arguments[i]; var _values = valueProvider.GetValue(item.Name); var value = _values as string; var values = _values as List<string>; var isEmpty = _values == null; if (isEmpty && !item.ParameterTypeIsArray) { if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { environment.EmitBadRequest(); environment.EmitStringMessage("Lack of Parameter:" + item.Name); return null; } } else if (!item.ParameterTypeIsArray) { var conv = TypeBinder.GetConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (conv == null) throw new InvalidOperationException("critical:register code is broken"); object pValue; if (conv(value ?? values[0], out pValue)) { methodParameters[i] = pValue; continue; } else if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { environment.EmitBadRequest(); environment.EmitStringMessage("Mismatch Parameter Type:" + item.Name); return null; } } var arrayConv = TypeBinder.GetArrayConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (arrayConv == null) throw new InvalidOperationException("critical:register code is broken"); methodParameters[i] = arrayConv((values != null) ? values : (value != null) ? new[] { value } : Enumerable.Empty<string>()); continue; } return methodParameters; }
public OperationHandler(LightNodeOptions options, Type classType, MethodInfo methodInfo) { this.ClassName = classType.Name; this.MethodName = methodInfo.Name; this.Arguments = methodInfo.GetParameters() .Select(x => new ParameterInfoSlim(x)) .ToArray(); this.ReturnType = methodInfo.ReturnType; this.filters = options.Filters .Concat(classType.GetCustomAttributes<LightNodeFilterAttribute>(true)) .Concat(methodInfo.GetCustomAttributes<LightNodeFilterAttribute>(true)) .OrderBy(x => x.Order) .ToArray(); this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast<Attribute>() .ToLookup(x => x.GetType()); foreach (var argument in this.Arguments) { if (!TypeBinder.IsAllowType(argument.ParameterType)) { throw new InvalidOperationException(string.Format("parameter is not allowed, class:{0} method:{1} paramName:{2} paramType:{3}", classType.Name, methodInfo.Name, argument.Name, argument.ParameterType.FullName)); } } // prepare lambda parameters var envArg = Expression.Parameter(typeof(IDictionary<string, object>), "environment"); var envBind = Expression.Bind(typeof(LightNodeContract).GetProperty("Environment"), envArg); var args = Expression.Parameter(typeof(object[]), "args"); var parameters = methodInfo.GetParameters() .Select((x, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), x.ParameterType)) .ToArray(); // Task or Task<T> if (typeof(Task).IsAssignableFrom(this.ReturnType)) { // (object[] args) => new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<IDictionary<string, object>, object[], Task>>( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); if (this.ReturnType.IsGenericType && this.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { this.handlerBodyType = HandlerBodyType.AsyncFunc; this.methodAsyncFuncBody = lambda.Compile(); lock (taskResultExtractors) { if (!taskResultExtractors.ContainsKey(this.ReturnType)) { // (object task) => (object)((Task<>).Result) var taskParameter = Expression.Parameter(typeof(object), "task"); var resultLambda = Expression.Lambda<Func<object, object>>( Expression.Convert( Expression.Property( Expression.Convert(taskParameter, this.ReturnType), "Result"), typeof(object)), taskParameter); var compiledResultLambda = resultLambda.Compile(); taskResultExtractors[this.ReturnType] = compiledResultLambda; } } } else { this.handlerBodyType = HandlerBodyType.AsyncAction; this.methodAsyncActionBody = lambda.Compile(); } } else if (this.ReturnType == typeof(void)) // of course void { // (object[] args) => { new X().M((T1)args[0], (T2)args[1])... } var lambda = Expression.Lambda<Action<IDictionary<string, object>, object[]>>( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); this.handlerBodyType = HandlerBodyType.Action; this.methodActionBody = lambda.Compile(); } else // return T { // (object[] args) => (object)new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<IDictionary<string, object>, object[], object>>( Expression.Convert( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters) , typeof(object)), envArg, args); this.handlerBodyType = HandlerBodyType.Func; this.methodFuncBody = lambda.Compile(); } }
async Task ExecuteOperation(LightNodeOptions options, OperationContext context) { // prepare var handler = this; var environment = context.Environment; var methodParameters = context.Parameters; bool isVoid = true; object result = null; switch (handler.handlerBodyType) { case HandlerBodyType.Action: handler.methodActionBody(environment, methodParameters); break; case HandlerBodyType.Func: isVoid = false; result = handler.methodFuncBody(environment, methodParameters); break; case HandlerBodyType.AsyncAction: var actionTask = handler.methodAsyncActionBody(environment, methodParameters); await actionTask.ConfigureAwait(false); break; case HandlerBodyType.AsyncFunc: isVoid = false; var funcTask = handler.methodAsyncFuncBody(environment, methodParameters); await funcTask.ConfigureAwait(false); var extractor = taskResultExtractors[funcTask.GetType()]; result = extractor(funcTask); break; default: throw new InvalidOperationException("critical:register code is broken"); } if (!isVoid) { // append header var responseHeader = environment["owin.ResponseHeaders"] as IDictionary<string, string[]>; var encoding = context.ContentFormatter.Encoding; responseHeader["Content-Type"] = new[] { context.ContentFormatter.MediaType + ((encoding == null) ? "" : "; charset=" + encoding.WebName) }; environment.EmitOK(); var responseStream = environment["owin.ResponseBody"] as Stream; if (options.StreamWriteOption == StreamWriteOption.DirectWrite) { context.ContentFormatter.Serialize(new UnclosableStream(responseStream), result); } else { using (var buffer = new MemoryStream()) { context.ContentFormatter.Serialize(new UnclosableStream(buffer), result); responseHeader["Content-Length"] = new[] { buffer.Position.ToString() }; buffer.Position = 0; if (options.StreamWriteOption == StreamWriteOption.BufferAndWrite) { buffer.CopyTo(responseStream); // not CopyToAsync } else { await buffer.CopyToAsync(responseStream).ConfigureAwait(false); } } } return; } else { environment.EmitNoContent(); return; } }