Azure / Azure-Functions

1.11k stars 198 forks source link

Unable to use the built-in model binding while retaining access to the context with functions v2 #1147

Open NibblyPig opened 5 years ago

NibblyPig commented 5 years ago

I need to create a POST function, so I can send data to it. I want to leverage the ability to send JSON or XML, using the appropriate headers, and the automatic model binding.

I can do this:

[FunctionName("CreateAccount")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "CreateAccount")] CreateAccountModel createAccountModel,
    ILogger log)

However I don't get access to the http context, so I can't check the headers for my authorisation token etc.

I would have to change it to HttpRequestMessage request to be able to access this, but then I lose the model binding, and have to do a lot of work to get this working myself (write code to see if the data 'looks like' json or xml, deserialize to an object).

I think there should be some way to leverage both of these, such as HttpRequestMessage<CreateAccountModel> request but I've not been able to find a way.

ColbyTresness commented 5 years ago

@brettsam is there any way to do this?

NibblyPig commented 5 years ago

I've since learned you can do this by adding a HttpRequest request parameter, not a HttpRequestMessage though as by default. This lives in the ASP.NET core namespace/library though so it doesn't seem particularly ideal, but it does work and gives you access to headers etc. although not the request URI. It feels like a workaround and that I am perhaps missing a much more obvious way to do it.

ColbyTresness commented 5 years ago

Makes sense. I'll close then. @brettsam can reopen if necessary.

MisinformedDNA commented 5 years ago

@NibblyPig How does HttpRequest allow you to keep model binding of the object?

NibblyPig commented 5 years ago

@MisinformedDNA If you do this, CreateAccountModel binds automatically with the built-in model binding:

[FunctionName("CreateAccount")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "CreateAccount")] CreateAccountModel createAccountModel,
    ILogger log)

But you don't get access to the http context. If you want it, you can do:

[FunctionName("CreateAccount")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "CreateAccount")] HttpRequestMessage request,
    ILogger log)

This lets you access all the headers etc of the request but you lose the automatic binding.

If you could do HttpRequestMessage<CreateAccountModel> for example you would be able to access the headers and leverage the in built binding, but you cannot it seems.

In the end I had to use the latter to get the headers, and do some manual deserialisation which was long and ugly - because you can call it with XML or JSON in your headers depending on what you want to send. MVC took care of this automatically, which is the biggest advantage. By hand you need to work out which it is and write a bunch of code to manually deserialise.