Esempio n. 1
0
        private Task HandleResponse(string body, HttpContext context, int statusCode)
        {
            var bodyObject  = ConvertBody(body);
            var apiResponse = responseBuilder.Create()
                              .WithStatusCode(statusCode)
                              .WithResponse(bodyObject);

            var wrappedBody = apiResponse.ToString();

            context.Response.ContentType   = "application/json";
            context.Response.ContentLength = wrappedBody != null?Encoding.UTF8.GetByteCount(wrappedBody) : 0;

            return(context.Response.WriteAsync(wrappedBody));
        }
Esempio n. 2
0
        // https://stackoverflow.com/a/47183053/3410871
        public async Task Invoke(HttpContext context)
        {
            // Honor include and exclude paths
            if (ShouldSkip(context))
            {
                await this.next(context);

                return;
            }

            // Keep a reference to original body
            var originalBody = context.Response.Body;

            try
            {
                await using var memStream = new MemoryStream();
                // Replace stream for actual calls
                context.Response.Body = memStream;

                // Continue the pipeline
                await next(context);

                // Check if we are about to return JSON data
                var responseContentType = context.Response.ContentType ?? String.Empty;
                if (ContentTypeIsHandled(responseContentType))
                {
                    // Memory stream now hold the response data
                    // Reset position to read data stored in response stream
                    memStream.Position = 0;
                    var responseBody = await new StreamReader(memStream).ReadToEndAsync();
                    var data         = JsonConvert.DeserializeObject <dynamic>(responseBody);

                    // Create wrapper response and convert to JSON
                    var apiResponse = responseBuilder
                                      .Create()
                                      .WithStatusCode(context.Response.StatusCode)
                                      .WithData((object)data);

                    // Copy wrapped response to original body
                    var buffer = Encoding.UTF8.GetBytes(apiResponse.AsJson(jsonSerializerSettings));
                    context.Response.ContentLength = null; // buffer.Length;
                    await using var output         = new MemoryStream(buffer);
                    await output.CopyToAsync(originalBody);
                }
                else
                {
                    // No need to wrap, just copy original stream
                    memStream.Position = 0;
                    await memStream.CopyToAsync(originalBody);
                }
            }
            finally
            {
                // Finally, reset the stream for downstream calls
                context.Response.Body = originalBody;
            }
        }
        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 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);
        }
Esempio n. 5
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);
        }