Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing HttpRequestData in middleware #414

Closed
hajekj opened this issue Apr 23, 2021 · 14 comments
Closed

Accessing HttpRequestData in middleware #414

hajekj opened this issue Apr 23, 2021 · 14 comments
Assignees
Labels
enhancement New feature or request

Comments

@hajekj
Copy link

hajekj commented Apr 23, 2021

As per the sample I am able to build a custom middleware which accesses the FunctionContext and works with it. However, how do I access the HttpRequestData object?

Some broader context: I am trying to build an authentication middleware: https://hajekj.net/2022/04/22/azure-functions-out-of-process-and-authentication-with-azure-ad/

I wanted to do it via context.Features.GetRequired<IFunctionBindingsFeature>(); but IFunctionBindingsFeature is internal. Another approach would be to go via the raw data: context.BindingContext.BindingData; but that would require me to manually parse the headers JSON.

So far, I managed to parse out the headers and seems to work fine - but it looks really dirty to me - https://github.com/hajekj/azure-functions-dotnet-worker-miw/blob/master/WorkerAuthentication/AuthenticationMiddleware.cs.

Also, I would like to be able to modify the response - for example to return HTTP 401 when token is not present, but I didn't manage to figure out how to do it in middleware. Is there any way to do it - or modify the Function output in general?

@ghost ghost assigned fabiocav Apr 23, 2021
@godefroi
Copy link

Also, I would like to be able to modify the response - for example to return HTTP 401 when token is not present, but I didn't manage to figure out how to do it in middleware. Is there any way to do it - or modify the Function output in general?

This isn't currently possible. See #340

@david-peden-q2
Copy link

While waiting for a proper solution, this works:

public static class FunctionContextExtensions
{
    public static HttpRequestData GetHttpRequestData(this FunctionContext functionContext)
    {
        try
        {
            KeyValuePair<Type, object> keyValuePair = functionContext.Features.SingleOrDefault(f => f.Key.Name == "IFunctionBindingsFeature");
            object functionBindingsFeature = keyValuePair.Value;
            Type type = functionBindingsFeature.GetType();
            var inputData = type.GetProperties().Single(p => p.Name == "InputData").GetValue(functionBindingsFeature) as IReadOnlyDictionary<string, object>;
            return inputData?.Values.SingleOrDefault(o => o is HttpRequestData) as HttpRequestData;
        }
        catch
        {
            return null;
        }
    }
}

And you can use it like this:

public class CustomMiddleware : IFunctionsWorkerMiddleware
{
    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        HttpRequestData httpRequestData = context.GetHttpRequestData();

        // do something with httpRequestData

        await next(context);
    }
}

@fabiocav
Copy link
Member

fabiocav commented May 6, 2021

Thank you for opening the issue and the workaround shared.

Currently, as you've found, the context carries the raw information and the services that would enable binding processing are not publicly exposed. This is work that is planned and, although I don't have a concrete ETA, we hope to expose soon. Keeping this open so we can update with progress.

@fabiocav fabiocav added enhancement New feature or request and removed Needs: Triage (Functions) labels May 6, 2021
@fabiocav fabiocav removed their assignment May 6, 2021
@nlpMbordogna
Copy link

Any change of a work around for the missing OutputData, something we can reflect to retrieve the HttpResponseData class? I inspected the data at runtime and cannot find anything similar

@rhollamby
Copy link

Yes I am also struggling with this. I need to set some http headers for all requests. Even if I create a service that I inject into each function it's still really messy.

@JanosNollFD
Copy link

Any change of a work around for the missing OutputData, something we can reflect to retrieve the HttpResponseData class? I inspected the data at runtime and cannot find anything similar

The workaround is so hacky it almost shouldn't be used, but here goes:

var feature = context.Features.FirstOrDefault(f => f.Key.Name == "IFunctionBindingsFeature").Value;
if (feature == null) throw new Exception("Cannot get function bindings feature");
PropertyInfo pinfo = feature.GetType().GetProperty("InvocationResult");
HttpResponseData resp = ...
// create a new instance of a child class because HttpResponseData is abstract. You can set 
// StatusCode, Body, HeadersCollection, but for some reason Body doesn't work (content not sent to the calling client)
pinfo.SetValue(feature, resp);

I hope the proper interfaces get exposed publicly soon.

@chandsalam1108
Copy link

Hi, How can we get access to HttpContext object to set User Claims?

In my case I'm using custom Authorization lib to validate JWT token. Upon successful validation I need to set user claims to HttpContext so that I can use that in other parts of function app method execution.

Thank you!
Chand

@Stef16Robbe
Copy link

Any updates on this about official support?

@ThomasArdal
Copy link

Wondering about when we will see official support for this as well 👍

For anyone interested, I have this small utility class that helps with both the request and response: https://github.com/elmahio/Elmah.Io.Functions.Isolated/blob/main/src/Elmah.Io.Functions.Isolated/FunctionContextExtensions.cs. All credits goes to @david-peden-q2 and @JanosNollFD who provided the code for this class through comments in this thread. I only aligned the two messages and added a null check if I remember correctly.

@arashcomsense
Copy link

@ThomasArdal I tried GetHttpResponseData() but the issue is that it overwrites the already-generated response object in the middleware executed after the function. For e.g. in my case, I need to add a header to every single function response. Is there a way to be able to manipulate the "existing" HttpResponseData object?

@ThomasArdal
Copy link

@arashcomsense Don't know. I think you should use StackOverflow or create another issue since that is out of the scope of this issue.

@SeanFeldman
Copy link
Contributor

SeanFeldman commented Apr 13, 2022

As someone that has gone through trying to figure out how to do authentication using middleware for this SDK, this is not the experience I'd like developers to go through. Isolated Worker SDK middleware is a solid tool to tap into the pipeline. It falls short by supporting anything other than the trivial termination or enrichment. Combined with the forced reflection to access specific types needed at runtime, it casts a massive shadow on the maturity of this SDK despite the GA label.

More samples would help but would likely add the burden on the team to maintain those. Not to mention the hesitation to show how to do things in a "wrong" way. Support for SDK types, imperative bindings, Application Insights, access to Functions SDK types, all these issues point to the same root problem - missing clarity on the path going forward for this SDK. Not to mention the convergence of the two SDKs into one after .NET 7.

@Arash-Sabet
Copy link

@SeanFeldman Very well said.

@kshyju
Copy link
Member

kshyju commented May 5, 2022

You can use the GetHttpRequestDataAsync extension method to get the HttpRequestData instance. This API is available from Version 1.8.0-preview1 onwards.

HttpRequestData requestData = await functionContext.GetHttpRequestDataAsync();

Please refer the StampHttpHeaderMiddleware middleware in the sample app for usage of this API in a middleware.

Thanks for your patience. Please let us know if you have questions.

@kshyju kshyju closed this as completed May 5, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jun 4, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests