Ejemplo n.º 1
0
        public ResponseWrapper(RequestDelegate next,
                               ILogger <ResponseWrapper> logger,
                               IApiResponseBuilder responseBuilder,
                               IConfiguration configuration)
        {
            this.next            = next;
            this.logger          = logger;
            this.responseBuilder = responseBuilder;

            this.includePath = configuration.GetValue(CubesConstants.Config_HostWrapPath, "/api/");
            this.excludePath = configuration.GetValue(CubesConstants.Config_HostWrapPathExclude, "");
        }
Ejemplo n.º 2
0
        public ResponseWrapper(RequestDelegate next,
                               IApiResponseBuilder responseBuilder,
                               IConfiguration configuration,
                               JsonSerializerSettings jsonSerializerSettings)
        {
            this.next                   = next;
            this.responseBuilder        = responseBuilder;
            this.jsonSerializerSettings = jsonSerializerSettings;

            this.includePath = configuration.GetValue(CubesConstants.Config_HostWrapPath, "/api/");
            this.excludePath = configuration.GetValue(CubesConstants.Config_HostWrapPathExclude, "");
        }
        public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder app,
                                                                    IApiResponseBuilder responseBuilder,
                                                                    ILoggerFactory loggerFactory)
        {
            var logger = loggerFactory.CreateLogger("Cubes.Web.CustomExceptionHandler");

            app.UseExceptionHandler(appError =>
            {
                appError.Run(async context =>
                {
                    context.Response.StatusCode  = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var contextFeature = context.Features.Get <IExceptionHandlerFeature>();
                    if (contextFeature != null)
                    {
                        var ex = contextFeature.Error;

                        var frame      = new StackTrace(ex, true).GetFrame(0);
                        var details    = new StringBuilder();
                        var methodInfo = $"{frame.GetMethod().DeclaringType.FullName}.{frame.GetMethod().Name}()";
                        var fileInfo   = $"{Path.GetFileName(frame.GetFileName())}, line {frame.GetFileLineNumber()}";
                        details
                        .Append(ex.GetType().Name)
                        .Append(": ")
                        .AppendLine(ex.Message);
                        details
                        .Append(methodInfo)
                        .Append(" in ")
                        .AppendLine(fileInfo);

                        var apiResponse = responseBuilder.Create()
                                          .HasErrors()
                                          .WithStatusCode(context.Response.StatusCode)
                                          .WithMessage("An unhandled exception occurred while processing the request.")
                                          .WithResponse(new
                        {
                            Details     = details.ToString(),
                            RequestInfo = $"{context.Request.Path} [{context.Request.Method}]"
                        });
                        await context
                        .Response
                        .WriteAsync(apiResponse.ToString());
                    }
                });
            });
            return(app);
        }
        public static IApplicationBuilder UseCubesApi(this IApplicationBuilder app,
                                                      IConfiguration configuration,
                                                      IWebHostEnvironment env,
                                                      IApiResponseBuilder responseBuilder,
                                                      ILoggerFactory loggerFactory)
        {
            var enableCompression = configuration.GetValue <bool>(CubesConstants.Config_HostEnableCompression, true);

            if (enableCompression)
            {
                app.UseResponseCompression();
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseCustomExceptionHandler(responseBuilder, loggerFactory);
            }

            var corsPolicies = configuration
                               .GetCorsPolicies()
                               .Select(p => p.PolicyName)
                               .ToList();

            foreach (var policy in corsPolicies)
            {
                app.UseCors(policy);
            }

            return(app
                   .UseHomePage()
                   .UseAdminPage(configuration, loggerFactory)
                   .UseCubesSwagger()
                   .UseStaticContent(configuration, loggerFactory)
                   .UseCubesMiddleware(loggerFactory, responseBuilder)
                   .UseResponseWrapper());
        }
Ejemplo n.º 5
0
 public ApiCaller()
 {
     _responseBuilder = new ApiResponseBuilder();
 }
        public static IApplicationBuilder UseCubesMiddleware(this IApplicationBuilder app,
                                                             ILoggerFactory loggerFactory,
                                                             IApiResponseBuilder responseBuilder)
        {
            app.Use(async(ctx, next) =>
            {
                // Prepare
                var logger      = loggerFactory.CreateLogger(CUBES_MIDDLEWARE_LOGGER);
                var requestID   = Guid.NewGuid().ToString("N");
                var requestInfo = $"{ctx.Request.Method} {ctx.Request.Path}{ctx.Request.Query.AsString()}";

                // Add to headers
                ctx.Request.Headers.Add(CUBES_HEADER_REQUEST_ID, new[] { requestID });

                // Provide context information
                var ctxProvider     = ctx.RequestServices.GetService <IContextProvider>();
                var context         = new Context(requestID, requestInfo);
                ctxProvider.Current = context;

                var watcher = Stopwatch.StartNew();
                ctx.Response.OnStarting(() =>
                {
                    watcher.Stop();
                    ctx.Response.Headers.Add(CUBES_HEADER_REQUEST_ID, new[] { requestID });
                    return(Task.FromResult(0));
                });

                await next.Invoke();

                // Just in case...
                watcher.Stop();

                var httpContextAccessor = ctx.RequestServices.GetService <IHttpContextAccessor>();

                // Inform user
                logger.LogInformation("{IP} [{startedAt}] \"{info}\", {statusCode}, {elapsed} ms",
                                      httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(),
                                      context.StartedAt,
                                      context.SourceInfo,
                                      ctx.Response.StatusCode,
                                      watcher.ElapsedMilliseconds);
            });

            app.UseStatusCodePages(async context =>
            {
                context.HttpContext.Response.ContentType = "application/json";
                var apiResponse = responseBuilder.Create()
                                  .HasErrors()
                                  .WithStatusCode(context.HttpContext.Response.StatusCode)
                                  .WithMessage($"Invalid request, status code: {context.HttpContext.Response.StatusCode}")
                                  .WithResponse(new
                {
                    Details = $"URL: {context.HttpContext.Request.Path}, Method: {context.HttpContext.Request.Method}"
                });
                await context
                .HttpContext
                .Response
                .WriteAsync(apiResponse.ToString());
            });

            return(app);
        }
Ejemplo n.º 7
0
        public static IApplicationBuilder UseCubesMiddleware(this IApplicationBuilder app,
                                                             ILoggerFactory loggerFactory,
                                                             IApiResponseBuilder responseBuilder,
                                                             IMetrics metrics,
                                                             JsonSerializerSettings serializerSettings)
        {
            app.Use(async(ctx, next) =>
            {
                var endpoint = ctx.GetEndpoint();

                // Prepare
                var logger      = loggerFactory.CreateLogger(CUBES_MIDDLEWARE_LOGGER);
                var requestID   = Guid.NewGuid().ToString("N");
                var requestInfo = $"{ctx.Request.Method} {ctx.Request.Path}{ctx.Request.Query.AsString()}";

                // Add to headers
                ctx.Request.Headers.Add(CUBES_HEADER_REQUEST_ID, new[] { requestID });

                // Provide context information
                var ctxProvider     = ctx.RequestServices.GetService <IContextProvider>();
                var context         = new Context(requestID, requestInfo);
                ctxProvider.Current = context;

                var watch = Stopwatch.StartNew();
                ctx.Response.OnStarting(() =>
                {
                    watch.Stop();
                    ctx.Response.Headers.Add(CUBES_HEADER_REQUEST_ID, new[] { requestID });
                    return(Task.FromResult(0));
                });

                await next.Invoke();

                // Just in case...
                watch.Stop();

                var httpContextAccessor = ctx.RequestServices.GetService <IHttpContextAccessor>();

                // Inform user
                var level =
                    ctx.Response.StatusCode >= 400 && ctx.Response.StatusCode < 500 ? LogLevel.Warning :
                    ctx.Response.StatusCode >= 500 ? LogLevel.Error : LogLevel.Information;

                logger.Log(level, "{IP} [{startedAt}] \"{info}\", {statusCode}, {elapsed} ms",
                           httpContextAccessor.HttpContext.Connection.RemoteIpAddress,
                           context.StartedAt,
                           context.SourceInfo,
                           ctx.Response.StatusCode,
                           watch.ElapsedMilliseconds);

                // Metrics
                var path = (endpoint as RouteEndpoint)?.RoutePattern?.RawText ?? ctx.Request.Path;
                metrics
                .GetCounter(CubesCoreMetrics.CubesCoreApiCallsCount)
                .WithLabels(ctx.Request.Method, path)
                .Inc();
                metrics
                .GetHistogram(CubesCoreMetrics.CubesCoreApiCallsDuration)
                .WithLabels(ctx.Request.Method, path)
                .Observe(watch.Elapsed.TotalSeconds);
            });

            app.UseStatusCodePages(async context =>
            {
                context.HttpContext.Response.ContentType = "application/json";
                var apiResponse = responseBuilder.Create()
                                  .WithStatusCode(context.HttpContext.Response.StatusCode)
                                  .WithMessage($"Invalid request, status code: {context.HttpContext.Response.StatusCode}")
                                  .WithData(new
                {
                    Details = $"URL: {context.HttpContext.Request.Path}, Method: {context.HttpContext.Request.Method}"
                });
                await context
                .HttpContext
                .Response
                .WriteAsync(apiResponse.AsJson(serializerSettings));
            });

            return(app);
        }
Ejemplo n.º 8
0
        public static IApplicationBuilder UseCubesApi(this IApplicationBuilder app,
                                                      IConfiguration configuration,
                                                      IWebHostEnvironment env,
                                                      IApiResponseBuilder responseBuilder,
                                                      ILoggerFactory loggerFactory,
                                                      IMetrics metrics,
                                                      JsonSerializerSettings serializerSettings)
        {
            var enableCompression = configuration.GetValue <bool>(CubesConstants.Config_HostEnableCompression, true);

            if (enableCompression)
            {
                app.UseResponseCompression();
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseCustomExceptionHandler(responseBuilder, loggerFactory, serializerSettings);
            }

            var corsPolicies = configuration
                               .GetCorsPolicies()
                               .Select(p => p.PolicyName)
                               .ToList();

            foreach (var policy in corsPolicies)
            {
                app.UseCors(policy);
            }

            // Based on:
            // https://gunnarpeipman.com/aspnet-core-health-checks/
            // https://www.youtube.com/watch?v=bdgtYbGYsK0
            // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks
            var options = new HealthCheckOptions
            {
                AllowCachingResponses = false,
                ResponseWriter        = async(context, result) =>
                {
                    context.Response.ContentType = MediaTypeNames.Application.Json;
                    var response = new
                    {
                        Status = result.Status.ToString(),
                        Checks = result.Entries.Select(e => new
                        {
                            Component = e.Key,
                            e.Value.Description,
                            Status = e.Value.Status.ToString(),
                            e.Value.Duration
                        }).ToList(),
                        Duration = result.TotalDuration
                    };

                    await context.Response.WriteAsync(response.AsJson());
                }
            };
            var hcEndpoint = configuration.GetValue(CubesConstants.Config_HostHealthCheckEndpoint, "/healthcheck");

            if (!hcEndpoint.StartsWith("/"))
            {
                hcEndpoint = "/" + hcEndpoint;
            }
            app.UseHealthChecks(hcEndpoint, options);

            var metricsEndpoint = configuration.GetValue(CubesConstants.Config_HostMetricsEndpoint, "/metrics");

            if (!metricsEndpoint.StartsWith("/"))
            {
                metricsEndpoint = "/" + metricsEndpoint;
            }
            return(app
                   .UseHomePage()
                   .UseAdminPage(configuration, loggerFactory)
                   .UseCubesSwagger()
                   .UseStaticContent(configuration, loggerFactory)
                   .UseMetricServer(metricsEndpoint)
                   .UseCubesMiddleware(loggerFactory, responseBuilder, metrics, serializerSettings)
                   // TODO Add UseCubesApplications to provide a "hook" for application while setting the pipeline
                   .UseResponseWrapper());
        }