Beispiel #1
0
        /// <summary>
        /// Enabled CODE Framework Open API support.
        /// </summary>
        /// <param name="appBuilder"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseOpenApiHandler(this IApplicationBuilder appBuilder, bool supportOpenApiJson = true, string openApiJsonRoute = "openapi.json", OpenApiInfo info = null)
        {
            if (SwaggerRoutes == null)
            {
                SwaggerRoutes = new List <string>();
                SwaggerRoutes.Add("/swagger"); // We assume swagger API is used when OpenAPI features are on. This makes sure that the general handler does not eat up other service routes
                SwaggerRoutes.Add("/swagger/index.html");
            }

            var serviceConfig = ServiceHandlerConfiguration.Current;

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

            appBuilder.UseEndpoints(endpoints =>
            {
                appBuilder.MapWhen(
                    context =>
                {
                    var requestPath      = context.Request.Path.ToString().Trim().ToLowerInvariant();
                    var openApiFullRoute = !string.IsNullOrEmpty(openApiJsonRoute) ? openApiJsonRoute : "/openapi.json";
                    if (!openApiFullRoute.StartsWith("/"))
                    {
                        openApiFullRoute = "/" + openApiFullRoute;
                    }
                    openApiFullRoute = openApiFullRoute.Trim().ToLowerInvariant();
                    return(requestPath == openApiFullRoute);
                },
                    builder =>
                {
                    // Build up route mapping
                    builder.UseRouter(routeBuilder =>
                    {
                        var openApiFullRoute = !string.IsNullOrEmpty(openApiJsonRoute) ? openApiJsonRoute : "/openapi.json";
                        if (!openApiFullRoute.StartsWith("/"))
                        {
                            openApiFullRoute = "/" + openApiFullRoute;
                        }
                        routeBuilder.MapVerb("GET", openApiFullRoute, GetOpenApiJson(serviceConfig.Services, info));
                        SwaggerRoutes.Add(openApiFullRoute);

                        // TODO: Add openapi.yaml support?
                    });
                });
            });

            return(appBuilder);
        }
Beispiel #2
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);
        }