/// <summary>
        /// Ctor
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="routeData"></param>
        /// <param name="methodContext"></param>
        public ServiceHandler(HttpContext httpContext, RouteData routeData, MethodInvocationContext methodContext)
        {
            HttpContext  = httpContext;
            HttpRequest  = httpContext.Request;
            HttpResponse = httpContext.Response;

            RouteData     = routeData;
            MethodContext = methodContext;

            ServiceInstanceConfiguration = MethodContext.InstanceConfiguration;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Hook up routed maps to service handlers.
        /// </summary>
        /// <param name="appBuilder"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseServiceHandler(
            this IApplicationBuilder appBuilder)
        {
            var serviceConfig = ServiceHandlerConfiguration.Current;

            foreach (var serviceInstanceConfig in serviceConfig.Services)
            {
                // conditionally route to service handler based on RouteBasePath
                appBuilder.MapWhen(
                    context =>
                {
                    var requestPath = context.Request.Path.ToString().ToLower();
                    var servicePath = serviceInstanceConfig.RouteBasePath.ToLower();
                    bool matched    =
                        requestPath == servicePath ||
                        requestPath.StartsWith(servicePath.Replace("//", "/") + "/");

                    return(matched);
                },
                    builder =>
                {
                    if (serviceConfig.Cors.UseCorsPolicy)
                    {
                        builder.UseCors(serviceConfig.Cors.CorsPolicyName);
                    }

                    // Build up route mapping
                    builder.UseRouter(routeBuilder =>
                    {
                        // Get Service interface = assuming first interface def is service interface
                        var interfaces = serviceInstanceConfig.ServiceType.GetInterfaces();
                        if (interfaces.Length < 1)
                        {
                            throw new NotSupportedException(Resources.HostedServiceRequiresAnInterface);
                        }

                        // Loop through service methods and cache the method info, parameter info, and RestAttribute
                        // in a MethodInvocationContext so we don't have to do this for each method call
                        foreach (var method in serviceInstanceConfig.ServiceType.GetMethods(
                                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.DeclaredOnly))
                        {
                            // find service contract
                            var interfaceMethod = interfaces[0].GetMethod(method.Name);
                            if (interfaceMethod == null)
                            {
                                continue;
                            }

                            var restAttribute = GetRestAttribute(interfaceMethod);
                            if (restAttribute == null)
                            {
                                continue;
                            }

                            var relativeRoute = restAttribute.Route;
                            if (relativeRoute == null)
                            {
                                // if no route assume we use the method name
                                // Note: string.Empty is a valid route!
                                relativeRoute = method.Name;
                            }



                            // figure out the full route we pass the ASP.NET Core Route Manager
                            string fullRoute =
                                (serviceInstanceConfig.RouteBasePath + "/" + relativeRoute).Replace("//", "/");

                            if (fullRoute.StartsWith("/"))
                            {
                                fullRoute = fullRoute.Substring(1);
                            }

                            // Cache reflection and context data
                            var methodContext = new MethodInvocationContext(method, serviceConfig, serviceInstanceConfig);

                            var roles = restAttribute.AuthorizationRoles;
                            if (roles != null)
                            {
                                methodContext.AuthorizationRoles = roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                            }


                            // This code is what triggers the SERVICE METHOD EXECUTION
                            // via a delegate that is called when the route is matched
                            Func <HttpRequest, HttpResponse, RouteData, Task> exec =
                                async(req, resp, routeData) =>
                            {
                                // ReSharper disable once AccessToModifiedClosure
                                var handler = new ServiceHandler(req.HttpContext, routeData, methodContext);
                                await handler.ProcessRequest();
                            };

                            routeBuilder.MapVerb(restAttribute.Method.ToString(), fullRoute, exec);
                            routeBuilder.MapVerb("OPTIONS", fullRoute, async(req, resp, route) =>
                            {
                                resp.StatusCode = StatusCodes.Status204NoContent;
                            });
                        }
                    });

                    // TODO: Should move into a separate
                    // builder.UseMiddleware<ServiceHandlerMiddleware>();
                });
            }

            return(appBuilder);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Enabled CODE Framework service hosting
        /// </summary>
        /// <param name="appBuilder"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseServiceHandler(this IApplicationBuilder appBuilder)
        {
            var serviceConfig = ServiceHandlerConfiguration.Current;

            if (serviceConfig.Cors.UseCorsPolicy)
            {
                appBuilder.UseCors(serviceConfig.Cors.CorsPolicyName);
            }

            // Endpoints require routing, so we make sure it is there
            appBuilder.UseRouting();

            appBuilder.UseEndpoints(endpoints =>
            {
                foreach (var serviceInstanceConfig in serviceConfig.Services)
                {
                    // conditionally route to service handler based on RouteBasePath
                    appBuilder.MapWhen(
                        context =>
                    {
                        var requestPath = context.Request.Path.ToString().ToLower();
                        if (SwaggerRoutes != null && SwaggerRoutes.Contains(requestPath))
                        {
                            return(false);                                                                                 // We make sure we are not accidently eating up a configured swagger/openapi route
                        }
                        var servicePath = serviceInstanceConfig.RouteBasePath.ToLower();
                        var matched     = requestPath == servicePath || requestPath.StartsWith(servicePath.Replace("//", "/") + "/");
                        return(matched);
                    },
                        builder =>
                    {
                        //if (serviceConfig.Cors.UseCorsPolicy)
                        //    builder.UseCors(serviceConfig.Cors.CorsPolicyName);

                        // Build up route mapping
                        builder.UseRouter(routeBuilder =>
                        {
                            // Get Service interface = assuming first interface def is service interface
                            var interfaces = serviceInstanceConfig.ServiceType.GetInterfaces();
                            if (interfaces.Length < 1)
                            {
                                throw new NotSupportedException(Resources.HostedServiceRequiresAnInterface);
                            }

                            // Loop through service methods and cache the propertyInfo info, parameter info, and RestAttribute
                            // in a MethodInvocationContext so we don't have to do this for each propertyInfo call
                            foreach (var method in serviceInstanceConfig.ServiceType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.DeclaredOnly))
                            {
                                // find service contract
                                var interfaceMethod = interfaces[0].GetMethod(method.Name);
                                if (interfaceMethod == null)
                                {
                                    continue;                                             // Should never happen, but doesn't hurt to check
                                }
                                var restAttribute = GetRestAttribute(interfaceMethod);
                                if (restAttribute == null)
                                {
                                    continue;                                           // This should never happen since GetRestAttribute() above returns a default attribute if none is attached
                                }
                                var relativeRoute = restAttribute.Route;
                                if (relativeRoute == null)
                                {
                                    // If no route is defined, we either build a route out of name and other attributes, or we use the propertyInfo name as the last resort.
                                    // Note: string.Empty is a valid route (and also a valid name). Only null values indicate that the setting has not been set!

                                    relativeRoute = restAttribute.Name ?? method.Name;

                                    // We also have to take a look at the parameter(s) - there should be only one - to build the route
                                    var parameters = method.GetParameters();
                                    if (parameters.Length > 0)
                                    {
                                        var parameterType       = parameters[0].ParameterType;
                                        var parameterProperties = parameterType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
                                        var inlineParameters    = GetSortedInlineParameterNames(parameterProperties);
                                        foreach (var inlineParameter in inlineParameters)
                                        {
                                            relativeRoute += $"/{{{inlineParameter}}}";
                                        }
                                    }
                                }

                                if (relativeRoute.StartsWith("/"))
                                {
                                    relativeRoute = relativeRoute.Substring(1);
                                }

                                // Figure out the full route we pass the ASP.NET Core Route Manager
                                var fullRoute = (serviceInstanceConfig.RouteBasePath + "/" + relativeRoute).Replace("//", "/");
                                if (fullRoute.StartsWith("/"))
                                {
                                    fullRoute = fullRoute.Substring(1);
                                }

                                // Cache reflection and context data
                                var methodContext = new MethodInvocationContext(method, serviceConfig, serviceInstanceConfig);

                                var roles = restAttribute.AuthorizationRoles;
                                if (roles != null)
                                {
                                    methodContext.AuthorizationRoles = roles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                                }

                                // This code is what triggers the SERVICE METHOD EXECUTION via a delegate that is called when the route is matched
                                Func <HttpRequest, HttpResponse, RouteData, Task> exec =
                                    async(req, resp, routeData) =>
                                {
                                    // ReSharper disable once AccessToModifiedClosure
                                    var handler = new ServiceHandler(req.HttpContext, routeData, methodContext);
                                    await handler.ProcessRequest();
                                };

                                routeBuilder.MapVerb("OPTIONS", fullRoute, async(req, resp, route) =>
                                {
                                    resp.StatusCode = StatusCodes.Status204NoContent;
                                    await Task.CompletedTask;
                                });

                                routeBuilder.MapVerb(restAttribute.Method.ToString(), fullRoute, exec);
                            }
                        });
                    });
                }
            });

            return(appBuilder);
        }