Пример #1
0
        /// <summary>
        /// Returns the OpenAPI representation of the given <see cref="ApiDataModel" />.
        /// </summary>
        /// <param name="httpContext">The <see cref="HttpContext" />.</param>
        /// <param name="openApiDocument">The <see cref="OpenApiDocument" />, as built by the <see cref="OpenApiDocumentBuilder" />.</param>
        /// <param name="options">The options to configure the OpenAPI document.</param>
        /// <returns>An OpenAPI representation.</returns>
        public PlainTextResult Invoke(
            HttpContext httpContext,
            OpenApiDocument openApiDocument,
            ApiOperationContext context,
            IOptions <OpenApiOptions> options)
        {
            var openApiOptions = options.Value;

            // If the document does not have any servers defined explicitly by client application we will push
            // a default one that is the currently running server
            if (openApiDocument.Servers.Count == 0)
            {
                var baseUri = httpContext.GetBlueprintBaseUri();

                openApiDocument.Servers.Add(new OpenApiServer
                {
                    Url = baseUri,
                });
            }

            var httpRequest = httpContext.Request;

            // If we believe this is a hit from a browser then serve up the documentation using Refit. This can be overriden
            // by passing a json query string to force the JSON response.
            if (httpRequest.Headers["Accept"].ToString().Contains("text/html") && httpRequest.Query.ContainsKey("json") == false)
            {
                var baseUri           = httpContext.GetBlueprintBaseUri();
                var refitHtmlDocument = @$ "<!DOCTYPE html>
<html>
<head>
    <title>{openApiDocument.Info.Title}</title>
Пример #2
0
        public ValueTask <object> Handle(IncrementCountCommand operation, ApiOperationContext apiOperationContext)
        {
            if (operation.Max != -1 && counter >= operation.Max)
            {
                logger.LogWarning("Reached max count");

                return(default);
Пример #3
0
        /// <summary>
        /// Pushes the given <see cref="IBackgroundTask" /> that has been wrapped in an <see cref="BackgroundTaskEnvelope" />
        /// to a <see cref="IApiOperationExecutor" />.
        /// </summary>
        /// <param name="taskEnvelope">The task to be executed.</param>
        /// <param name="configureSpan">An action that will be called with an <see cref="IApmSpan" /> for the service-specific
        /// provider to add tags.</param>
        /// <param name="token">A cancellation token that indicates this method should be aborted.</param>
        /// <returns>A <see cref="Task" /> representing the execution of the given task.</returns>
        public async Task Execute(
            BackgroundTaskEnvelope taskEnvelope,
            Action <IApmSpan> configureSpan,
            CancellationToken token)
        {
            Guard.NotNull(nameof(taskEnvelope), taskEnvelope);

            using var nestedContainer = this._rootServiceProvider.CreateScope();

            var apiContext = new ApiOperationContext(
                nestedContainer.ServiceProvider,
                this._apiOperationExecutor.DataModel,
                taskEnvelope.Task,
                token);

            using var span = this._apmTool.StartOperation(
                      apiContext.Descriptor,
                      SpanKinds.Consumer,
                      taskEnvelope.ApmContext);

            configureSpan(span);

            apiContext.ApmSpan = span;

            var result = await this._apiOperationExecutor.ExecuteAsync(apiContext);

            if (result is UnhandledExceptionOperationResult unhandledExceptionOperationResult)
            {
                span.RecordException(unhandledExceptionOperationResult.Exception);

                unhandledExceptionOperationResult.Rethrow();
            }
        }
Пример #4
0
            public OkResult Handle(ApiOperationContext context, IDependency dependency)
            {
                Context    = context;
                Dependency = dependency;

                return(new OkResult(nameof(InlineHandle)));
            }
        private static async Task PopulateResourceEventData(
            IResourceEventRepository resourceEventRepository,
            ApiOperationContext context,
            ResourceEvent resourceEvent)
        {
            // Get the latest after creation or update (cannot, obviously, get for a deleted record)
            if (resourceEvent.ChangeType != ResourceEventChangeType.Deleted)
            {
                resourceEvent.Data = await GetByIdAsync(context, resourceEvent.SelfQuery);
            }

            if (resourceEvent.ChangeType == ResourceEventChangeType.Updated)
            {
                var previousResource =
                    await resourceEventRepository.GetCurrentDataAsync(resourceEvent.Href, resourceEvent.ResourceType);

                // There are cases where the old resource will not exist because resources being saved was a new
                // introduction after being in prod for over a year
                if (previousResource != null)
                {
                    resourceEvent.ChangedValues = ObjectComparer.GetPreviousValues(
                        previousResource,
                        resourceEvent.Data);
                }
            }
        }
Пример #6
0
            public OkResult Handle(ApiOperationContext context, IDependency dependency, IUserAuthorisationContext user)
            {
                Context    = context;
                Dependency = dependency;
                User       = user;

                return(new OkResult(nameof(InlineHandle)));
            }
        /// <inheritdoc />
        public Task ExecuteAsync(ApiOperationContext context, ValidationFailedOperationResult result)
        {
            var validationProblemDetails = new ValidationProblemDetails(result.Errors);

            return(this._okResultOperationExecutor.WriteContentAsync(
                       context.GetHttpContext(),
                       validationProblemDetails.Status.Value,
                       validationProblemDetails));
        }
Пример #8
0
 public static void WriteSuccess(IAuditor auditor, ApiOperationContext context)
 {
     auditor.Write(new AuditItem(
                       Activity.Current?.Id ?? "no-activity-id",
                       true,
                       "Success",
                       GetUserId(context),
                       context.Operation));
 }
Пример #9
0
 public static void WriteFailure(IAuditor auditor, ApiOperationContext context, Exception e)
 {
     auditor.Write(new AuditItem(
                       Activity.Current?.Id ?? "no-activity-id",
                       false,
                       e.Message,
                       GetUserId(context),
                       context.Operation));
 }
Пример #10
0
            public async Task RouteAsync(HttpContext httpContext)
            {
                var endpoint    = httpContext.GetEndpoint();
                var routeData   = httpContext.GetRouteData();
                var httpRequest = httpContext.Request;

                var operation = endpoint.Metadata.GetMetadata <ApiOperationDescriptor>();

                // If we have an activity set the DisplayName to the operation type
                if (Activity.Current != null)
                {
                    Activity.Current.DisplayName = operation.Name;
                }

                var httpFeatureData = operation.GetFeatureData <HttpOperationFeatureData>();

                if (httpFeatureData.HttpMethod != httpRequest.Method)
                {
                    this._logger.LogInformation(
                        "Request {Method} {Url} does not match required HTTP method {RequiredMethod}",
                        httpRequest.Method,
                        httpRequest.GetDisplayUrl(),
                        httpFeatureData.HttpMethod);

                    httpContext.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;

                    return;
                }

                using var nestedContainer = this._rootServiceProvider.CreateScope();

                var apiContext = new ApiOperationContext(
                    nestedContainer.ServiceProvider,
                    this._apiOperationExecutor.DataModel,
                    operation,
                    httpContext.RequestAborted)
                {
                    ClaimsIdentity = httpContext.User.Identity as ClaimsIdentity,
                };

                apiContext.SetHttpFeatureContext(new HttpFeatureContext
                {
                    HttpContext = httpContext,
                    RouteData   = routeData,
                });

                var request = httpContext.Request;
                var baseUri = $"{request.Scheme}://{this._httpOptions.Value.PublicHost ?? request.Host.Value}{request.PathBase}/{this._basePath}";

                httpContext.SetBaseUri(baseUri);

                var result = await this._apiOperationExecutor.ExecuteAsync(apiContext);

                // We want to immediately execute the result to allow it to write to the HTTP response
                await result.ExecuteAsync(apiContext);
            }
Пример #11
0
        public static async Task HandleAsync(
            IResourceEventRepository resourceEventRepository,
            IApiLinkGenerator apiLinkGenerator,
            ApiOperationContext context,
            OperationResult result)
        {
            if (result is OkResult okResult)
            {
                var innerResult = okResult.Content;

                if (innerResult is ResourceEvent resourceEvent)
                {
                    var logger = context.ServiceProvider.GetRequiredService <ILogger <ResourceEventHandlerMiddlewareBuilder> >();

                    void AddMetadata(string k, object v) => resourceEvent.Metadata[k] = v;

                    context.UserAuthorisationContext?.PopulateMetadata(AddMetadata);

                    resourceEvent.CorrelationId = context.Activity?.Id;
                    resourceEvent.Operation     = context.Operation;

                    var metadataProviders = context.ServiceProvider.GetServices <IContextMetadataProvider>();

                    foreach (var p in metadataProviders)
                    {
                        await p.PopulateMetadataAsync(context, AddMetadata);
                    }

                    // If we do not already have Data use the "SelfQuery" to populate using a nested query if possible
                    if (resourceEvent.Data == null && resourceEvent.SelfQuery != null)
                    {
                        await TryPopulateResourceEventDataAsync(context, resourceEvent);
                    }

                    var selfLink = context.DataModel.GetLinkFor(resourceEvent.ResourceType, "self");

                    if (selfLink == null)
                    {
                        logger.LogWarning(
                            "No self link exists. Href property of the resource event of type {ResourceType} will not be populated",
                            resourceEvent.ResourceType);
                    }
                    else
                    {
                        resourceEvent.Href = apiLinkGenerator.CreateUrl(selfLink, resourceEvent.Data ?? resourceEvent.SelfQuery);
                    }

                    if (resourceEvent.Data != null)
                    {
                        await TryPopulateChangedValuesAsync(resourceEventRepository, resourceEvent);
                    }

                    await resourceEventRepository.AddAsync(resourceEvent);
                }
            }
        }
Пример #12
0
 private static async Task TryPopulateResourceEventDataAsync(
     ApiOperationContext context,
     ResourceEvent resourceEvent)
 {
     // Get the latest after creation or update (cannot, obviously, get for a deleted record)
     if (resourceEvent.ChangeType != ResourceEventChangeType.Deleted)
     {
         resourceEvent.Data = await GetByIdAsync(context, resourceEvent.SelfQuery);
     }
 }
        private static async ValueTask <object> AddResourceLinksAsync(
            ILogger <LinkGeneratorMiddlewareBuilder> logger,
            IApiLinkGenerator apiLinkGenerator,
            IEnumerable <IResourceLinkGenerator> generators,
            ApiOperationContext context,
            object resource)
        {
            if (resource is ILinkableResource linkableResource)
            {
                await AddLinksAsync(logger, apiLinkGenerator, generators, context, linkableResource);
            }

            var enumerableResult = resource as IEnumerable <object>;

            if (resource is IPagedApiResource pagedResult)
            {
                enumerableResult = pagedResult.GetEnumerable();
            }

            if (enumerableResult != null)
            {
                if (enumerableResult is IQueryable <object> )
                {
                    // We need to ensure we are now dealing with a non-deferred result, else the
                    // links will be added, but the deferred result is still returned and so
                    // changes are lost.
                    //
                    // This needs to be what we then actually return from the middleware after the
                    // links have been added
                    enumerableResult = enumerableResult.ToList();
                    resource         = enumerableResult;
                }

                foreach (var obj in enumerableResult)
                {
                    if (obj is ILinkableResource apiResourceItem)
                    {
                        await AddLinksAsync(logger, apiLinkGenerator, generators, context, apiResourceItem);
                    }
                    else
                    {
                        // If we cannot add any links because not a `LinkableResource` then break early
                        // as we assume all entries are the same type
                        if (logger.IsEnabled(LogLevel.Trace))
                        {
                            logger.LogTrace("Resource type is not a LinkableResource. resource_type={0}", obj.GetType().Name);
                        }

                        break;
                    }
                }
            }

            return(resource);
        }
Пример #14
0
 /// <summary>
 /// Creates a new instance of <see cref="BodyParserContext"/>.
 /// </summary>
 /// <param name="operationContext">The operation context.</param>
 /// <param name="httpContext">
 /// The <see cref="Microsoft.AspNetCore.Http.HttpContext"/> for the current operation.
 /// </param>
 /// <param name="instance">The instance that needs to be populated.</param>
 /// <param name="bodyType">The type the body should be read as.</param>
 public BodyParserContext(
     ApiOperationContext operationContext,
     HttpContext httpContext,
     object instance,
     Type bodyType)
 {
     this.OperationContext = operationContext ?? throw new ArgumentNullException(nameof(httpContext));
     this.HttpContext      = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
     this.Instance         = instance ?? throw new ArgumentNullException(nameof(instance));
     this.BodyType         = bodyType ?? throw new ArgumentNullException(nameof(bodyType));
 }
Пример #15
0
        /// <inheritdoc />
        public async Task <OperationResult> ExecuteAsync(ApiOperationContext context)
        {
            var result = await this._executor.ExecuteAsync(context);

            if (result is UnhandledExceptionOperationResult e)
            {
                e.Rethrow();
            }

            return(result);
        }
Пример #16
0
        /// <summary>
        /// Gets the <see cref="RouteContext" /> that has been registered with this <see cref="ApiOperationContext" />.
        /// </summary>
        /// <param name="context">The context to load from.</param>
        /// <returns>The <see cref="RouteContext"/> registered.</returns>
        /// <exception cref="InvalidOperationException">If no <see cref="RouteContext"/> has been registered.</exception>
        /// <seealso cref="SetHttpFeatureContext" />
        public static HttpFeatureContext GetHttpFeatureContext(this ApiOperationContext context)
        {
            if (context.Data.TryGetValue(nameof(HttpFeatureContext), out var value))
            {
                return((HttpFeatureContext)value);
            }

            throw new InvalidOperationException(
                      $"A HttpFeatureContext instance does not exist on this {nameof(ApiOperationContext)}. To use HTTP-specific features a HttpContext must exist, which " +
                      $"can be accomplished by using {nameof(EndpointRouteBuilderExtensions.MapBlueprintApi)} in your ASP.NET Core web application");
        }
        public static async Task AddLinksAsync(
            IApiLinkGenerator apiLinkGenerator,
            IEnumerable <IResourceLinkGenerator> registeredGenerators,
            ApiOperationContext context,
            OperationResult result)
        {
            if (result is OkResult okResult)
            {
                var logger = context.ServiceProvider.GetRequiredService <ILogger <LinkGeneratorMiddlewareBuilder> >();

                okResult.Content = await AddResourceLinksAsync(logger, apiLinkGenerator, registeredGenerators, context, okResult.Content);
            }
        }
        public ValueTask <object> Handle(T operation, ApiOperationContext apiOperationContext)
        {
            WasCalled       = true;
            OperationPassed = operation;
            ContextPassed   = apiOperationContext;

            if (ToThrow != null)
            {
                throw ToThrow;
            }

            return(new ValueTask <object>(ResultToReturn));
        }
        private static Task <ExecutionAllowed> IsAuthorisedAsync(ApiOperationContext operationContext)
        {
            if (operationContext.UserAuthorisationContext == null)
            {
                return(_userUnauthenticated);
            }

            if (!operationContext.UserAuthorisationContext.IsActive)
            {
                return(_userInactive);
            }

            return(ExecutionAllowed.YesTask);
        }
        public static async Task HandleAsync(
            IResourceEventRepository resourceEventRepository,
            IApiLinkGenerator apiLinkGenerator,
            ApiOperationContext context,
            OperationResult result)
        {
            if (result is OkResult okResult)
            {
                var innerResult = okResult.Content;

                if (innerResult is ResourceEvent resourceEvent)
                {
                    var logger = context.ServiceProvider.GetRequiredService <ILogger <ResourceEventHandlerMiddlewareBuilder> >();

                    logger.LogDebug("ResourceEvent found. Loading resource. resource_type={0}", resourceEvent.ResourceType);

                    void AddMetadata(string k, object v) => resourceEvent.Metadata[k] = v;

                    context.UserAuthorisationContext?.PopulateMetadata(AddMetadata);

                    resourceEvent.CorrelationId = Activity.Current?.Id;
                    resourceEvent.Operation     = context.Operation;

                    var metadataProviders = context.ServiceProvider.GetServices <IContextMetadataProvider>();

                    foreach (var p in metadataProviders)
                    {
                        await p.PopulateMetadataAsync(context, AddMetadata);
                    }

                    var selfLink = context.DataModel.GetLinkFor(resourceEvent.ResourceType, "self");

                    if (selfLink == null)
                    {
                        logger.LogWarning(
                            "No self link exists. Link and payload will not be populated. resource_type={0}",
                            resourceEvent.ResourceType.Name);

                        return;
                    }

                    resourceEvent.Href = apiLinkGenerator.CreateUrl(selfLink, resourceEvent.SelfQuery);

                    await PopulateResourceEventData(resourceEventRepository, context, resourceEvent);

                    await resourceEventRepository.AddAsync(resourceEvent);
                }
            }
        }
        /// <inheritdoc />
        public Task PopulateMetadataAsync(ApiOperationContext context, Action <string, object> add)
        {
            var request = context.GetHttpContext().Request;

            add("IpAddress", request.GetClientIpAddress());

            if (request.Headers.ContainsKey("User-Agent"))
            {
                add(
                    "UserAgent",
                    string.Join(" ", request.Headers["User-Agent"].ToString()));
            }

            return(Task.CompletedTask);
        }
Пример #22
0
        /// <inheritdoc />
        public override async Task ExecuteAsync(ApiOperationContext context)
        {
            await base.ExecuteAsync(context);

            var httpContext = context.GetHttpContext();
            var response    = httpContext.Response;

            response.ContentType = this.ContentType;

            using var httpResponseStreamWriter = new HttpResponseStreamWriter(response.Body, Encoding.UTF8);

            await httpResponseStreamWriter.WriteAsync(this._content);

            await httpResponseStreamWriter.FlushAsync();
        }
        public Task AddClassValidationResultsAsync(object value, ApiOperationContext apiOperationContext, ValidationFailures results)
        {
            var validationContext = new ValidationContext(value, null, null);

            if (value is IValidatableObject validatableObject)
            {
                var validationResults = validatableObject.Validate(validationContext);

                foreach (var validationResult in validationResults)
                {
                    results.AddFailure(validationResult);
                }
            }

            return(Task.CompletedTask);
        }
Пример #24
0
        /// <inheritdoc />
        public Task ExecuteAsync(ApiOperationContext context, UnhandledExceptionOperationResult result)
        {
            var httpContext    = context.GetHttpContext();
            var problemDetails = this.ToProblemDetails(result.Exception);

            if (context.Activity != null)
            {
                problemDetails.AddExtension("traceId", context.Activity?.TraceId.ToString());
                problemDetails.AddExtension("traceParentId", context.Activity?.ParentId);
            }

            return(this._okResultOperationExecutor.WriteContentAsync(
                       httpContext,
                       problemDetails.Status,
                       problemDetails));
        }
        private static async ValueTask AddLinksAsync(
            ILogger logger,
            IApiLinkGenerator apiLinkGenerator,
            IEnumerable <IResourceLinkGenerator> generators,
            ApiOperationContext context,
            ILinkableResource result)
        {
            foreach (var resourceLinkGenerator in generators)
            {
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.LogTrace("Generating links. generator={0} result_type={1}", resourceLinkGenerator.GetType().Name, result.GetType().Name);
                }

                await resourceLinkGenerator.AddLinksAsync(apiLinkGenerator, context, result);
            }
        }
        /// <inheritdoc />
        public Task ExecuteAsync(ApiOperationContext context, UnhandledExceptionOperationResult result)
        {
            var httpContext    = context.GetHttpContext();
            var problemDetails = this.ToProblemDetails(result.Exception);

            var traceId = context.ApmSpan?.TraceId;

            if (traceId != null)
            {
                problemDetails.AddExtension("traceId", traceId);
            }

            return(this._okResultOperationExecutor.WriteContentAsync(
                       httpContext,
                       problemDetails.Status.Value,
                       problemDetails));
        }
Пример #27
0
        /// <summary>
        /// Sets the HTTP status code and adds any custom headers that have been added to <see cref="Headers" />.
        /// </summary>
        /// <param name="context">The context of the operation this result is for.</param>
        /// <returns>A <see cref="Task" /> representing the execution of this method.</returns>
        public override Task ExecuteAsync(ApiOperationContext context)
        {
            var httpContext = context.GetHttpContext();
            var response    = httpContext.Response;

            response.StatusCode = (int)this.StatusCode;

            if (this.Headers != null)
            {
                foreach (var h in this.Headers)
                {
                    response.Headers.Add(h);
                }
            }

            return(Task.CompletedTask);
        }
        private Task <ExecutionAllowed> IsAuthorisedAsync(ApiOperationContext operationContext, ApiOperationDescriptor descriptor, object resource)
        {
            if (operationContext.UserAuthorisationContext == null)
            {
                return(_userUnauthenticated);
            }

            if (!operationContext.UserAuthorisationContext.IsActive)
            {
                return(_userInactive);
            }

            if (!(operationContext.UserAuthorisationContext is IClaimsHolder userClaims))
            {
                throw new InvalidOperationException($"Cannot apply {nameof(ClaimsRequiredApiAuthoriser)} to a user context that does not implement {nameof(IClaimsHolder)}");
            }

            var claimRequiredAttribute = descriptor.TypeAttributes.OfType <ClaimRequiredAttribute>().Single();

            var expansionState = ClaimExpansionState.RequiresExpansion;
            var requiredClaim  = claimRequiredAttribute.GetClaim(resource);

            // Pre-expand key if it is a match
            var apiResource = resource as IHaveResourceKey;
            var expandedKey = apiResource?.ResourceKey;

            if (expandedKey != null && expandedKey.EndsWith(requiredClaim.Value))
            {
                requiredClaim  = new Claim(requiredClaim.Type, expandedKey, requiredClaim.ValueType);
                expansionState = ClaimExpansionState.AlreadyExpanded;
            }

            var result = this._claimInspector.IsDemandedClaimFulfilled(userClaims, requiredClaim, expansionState);

            if (!result)
            {
                return(Task.FromResult(ExecutionAllowed.No(
                                           $"User does not have required claim {requiredClaim.Type} {requiredClaim.ValueType} for {requiredClaim.Value}",
                                           "You do not have enough permissions to perform this action",
                                           ExecutionAllowedFailureType.Authorisation)));
            }

            return(ExecutionAllowed.YesTask);
        }
Пример #29
0
        public async ValueTask <ExecutionAllowed> CanShowLinkAsync(ApiOperationContext operationContext, ApiOperationDescriptor descriptor, [CanBeNull] object resource)
        {
            var traceLogEnabled = this._logger.IsEnabled(LogLevel.Trace);

            foreach (var checker in this._apiAuthorisers)
            {
                if (!checker.AppliesTo(descriptor))
                {
                    continue;
                }

                var result = await checker.CanShowLinkAsync(operationContext, descriptor, resource);

                if (result.IsAllowed == false)
                {
                    // Base links could have many, potentially hundreds, of failures, which are completely
                    // normal, we will not log unless enabled
                    if (traceLogEnabled)
                    {
                        this._logger.LogTrace(
                            "Link check failed. type={0} resource_type={1} reason={2} authoriser={3}",
                            descriptor.OperationType.Name,
                            resource?.GetType().Name,
                            result.Reason,
                            checker.GetType());
                    }

                    return(result);
                }
            }

            if (traceLogEnabled)
            {
                this._logger.LogTrace(
                    "Link check succeeded. type={0} resource_type={1}",
                    descriptor.OperationType.Name,
                    resource?.GetType().Name);
            }

            return(ExecutionAllowed.Yes);
        }
Пример #30
0
        /// <summary>
        /// Configures the <see cref="ApiOperationContext" /> with a HTTP context configured for the given URL.
        /// </summary>
        /// <remarks>
        /// In addition to adding the <see cref="RouteContext"/> on the descriptor this method will set the newly
        /// created <see cref="HttpContext" /> on the <see cref="HttpContextAccessor" /> that has been registered
        /// with the context's <see cref="IServiceProvider"/>.
        /// </remarks>
        /// <param name="context">The context to configure.</param>
        /// <param name="url">The URL to set for this context's request.</param>
        public static HttpContext ConfigureHttp(this ApiOperationContext context, string url)
        {
            var httpContext = new DefaultHttpContext();

            httpContext.Connection.RemoteIpAddress = IPAddress.Loopback;
            httpContext.SetRequestUri(url);
            httpContext.Request.Method = context.Descriptor.GetFeatureData <HttpOperationFeatureData>().HttpMethod;
            httpContext.Request.Headers["Content-Type"] = "application/test-data";

            context.SetHttpFeatureContext(new HttpFeatureContext
            {
                HttpContext = httpContext,
                RouteData   = new RouteData(),
            });

            httpContext.SetBaseUri("https://api.blueprint-testing.com/api/");

            context.ServiceProvider.GetRequiredService <IHttpContextAccessor>().HttpContext = httpContext;

            return(httpContext);
        }