Beispiel #1
0
        public OperationHandler(ILightNodeOptions options, Type classType, MethodInfo methodInfo)
        {
            this.ClassName  = classType.Name;
            this.MethodName = methodInfo.Name;
            this.Arguments  = methodInfo.GetParameters()
                              .Select(x => new ParameterInfoSlim(x))
                              .ToArray();
            this.ParameterNames = Arguments.Select(x => x.Name).ToList().AsReadOnly();
            this.ReturnType     = methodInfo.ReturnType;

            this.filters = options.Filters
                           .Concat(classType.GetCustomAttributes <LightNodeFilterAttribute>(true))
                           .Concat(methodInfo.GetCustomAttributes <LightNodeFilterAttribute>(true))
                           .OrderBy(x => x.Order)
                           .ToArray();

            var operationOption = methodInfo.GetCustomAttributes <OperationOptionAttribute>(true).FirstOrDefault()
                                  ?? classType.GetCustomAttributes <OperationOptionAttribute>(true).FirstOrDefault();

            this.AcceptVerb = (operationOption != null && operationOption.AcceptVerbs != null)
                ? operationOption.AcceptVerbs.Value
                : options.DefaultAcceptVerb;

            var verbSpecifiedAttr = methodInfo.GetCustomAttributes <HttpVerbAttribtue>(true);

            if (verbSpecifiedAttr.Any())
            {
                this.AcceptVerb = verbSpecifiedAttr.Aggregate((AcceptVerbs)0, (x, y) => x | y.AcceptVerbs);
            }

            this.ForceUseFormatter = (operationOption != null && operationOption.ContentFormatter != null)
                ? operationOption.ContentFormatter
                : null;
            var formatterChoiceBase = new[] { options.DefaultFormatter }.Concat(options.SpecifiedFormatters).Where(x => x != null).ToArray();

            this.optionFormatters           = formatterChoiceBase;
            this.formatterByExt             = formatterChoiceBase.SelectMany(x => (x.Ext ?? "").Split('|'), (fmt, ext) => new { fmt, ext }).ToLookup(x => x.ext, x => x.fmt, StringComparer.OrdinalIgnoreCase);
            this.formatterByMediaType       = formatterChoiceBase.ToLookup(x => x.MediaType, StringComparer.OrdinalIgnoreCase);
            this.formatterByContentEncoding = formatterChoiceBase.ToLookup(x => x.ContentEncoding, StringComparer.OrdinalIgnoreCase);

            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();
            }
        }
Beispiel #2
0
        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(HttpContext httpContext, ILightNodeOptions options, IOperationCoordinator coordinator, 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
                    {
                        coordinator.OnProcessInterrupt(options, httpContext, InterruptReason.ParameterBindMissing, "Lack of Parameter:" + item.Name);
                        options.Logger.ParameterBindMissing(OperationMissingKind.LackOfParameter, item.Name);
                        if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException)
                        {
                            throw new ParameterMissingException(OperationMissingKind.LackOfParameter, item.Name);
                        }
                        else
                        {
                            httpContext.EmitBadRequest();
                            if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails)
                            {
                                httpContext.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
                    {
                        coordinator.OnProcessInterrupt(options, httpContext, InterruptReason.ParameterBindMissing, "Mismatch ParameterType:" + item.Name);
                        options.Logger.ParameterBindMissing(OperationMissingKind.MissmatchParameterType, item.Name);
                        if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException)
                        {
                            throw new ParameterMissingException(OperationMissingKind.MissmatchParameterType, item.Name);
                        }
                        else
                        {
                            httpContext.EmitBadRequest();
                            if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails)
                            {
                                httpContext.EmitStringMessage("Mismatch ParameterType:" + 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 } : (IList <string>) new string[0]);
                continue;
            }

            return(methodParameters);
        }
        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);
        }