Morgan Skinner
Morgan Skinner

Reputation: 520

Azure Functions - how to return a location header

I'm new to Azure Functions and have a simple question about how to generate a Location header for a newly created resource. I have created a simple function that is used to create a Person (original eh?).

In my example I'm using DocumentDB for storage. I want to return a Location header to the client that they can then de-reference should they wish to, but to do that I need to know about routing.

My code is as follows...

public static class PersonProcessing
{
    [FunctionName("person")]
    public static async Task<HttpResponseMessage> Create(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequestMessage req,
        [DocumentDB("Test", "People", CreateIfNotExists = true)]ICollector<Person> outTable,
        TraceWriter log)
    {
        var tx = await req.Content.ReadAsAsync<Person>();

        tx.Id = Guid.NewGuid();

        outTable.Add(tx);

        var response = req.CreateResponse(HttpStatusCode.Created, tx);
        response.Headers.Location = new Uri($"{req.RequestUri}/{tx.Id}");
        return response;
    }

    public class Person
    {
        [JsonProperty("id")]
        public Guid Id { get; set; }

        public string Name { get; set; }
    }
}

I have created the Location header based on the incoming RequestUri, but is there a better (or more standard) way to do this with Azure Functions?

Is what I have done here accepted wisdom - I can't find any useful resources on the web hence my question?

Thanks in advance for your responses.

Upvotes: 1

Views: 2098

Answers (3)

Adrian
Adrian

Reputation: 3438

Another way to accomplish this is by having your function return an IActionResult and then returning a CreatedResult

For example:

[FunctionName("TwoOhWhan")]
        public async Task<IActionResult> ReturnATwoOhWhanEvenIfCORSIsInThePicture(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/test/twoohwhan")]
            HttpRequest req,
            ILogger logger)
        {
            // ... logic here

            var @return = new CreatedResult(location, new
            {
                id = resourceIdentifier
            });

            //This trumped me for a while. In ASPNET Core you get this done by using 
            //the extension method: .WithExposedHeaders("Location")
            req.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Location");
            return @return;
        }

I like this approach because its easier to gather what is going on here by returning a result that signifies the 201 itself.

I snuck in a little icing on top of this answer. Check out the line right about the return @return statement. If you are making cross origin requests to an HTTP triggered function you are in for a bad time. The location header will be visible in fiddler or your network chrome tab but you won't have access to it in your JS code.

Typically this is handled in your startup.cs file in an aspnetcore setting but azure functions don't have a way to do this. The azure portal dashboard also does not give you an option to do this in the CORS settings page found under the Platform Settings section.

enter image description here

Its not very intuitive and information on the internet regarding this little caveat is hard to come by. Hopefully this helps someone else in a similar situation.

Upvotes: 1

No Refunds No Returns
No Refunds No Returns

Reputation: 8336

I'm using azure functions v2 and the above doesn't seem to work. However I tried

        var headers = req.HttpContext.Response.Headers;
        var when = DateTime.UtcNow;
        var v = new StringValues(when.ToString("yyyy-MM-dd HH:mm:ss.ffffff"));
        headers.Add("now", v);

and it seems to work just fine.

Upvotes: 2

David Ebbo
David Ebbo

Reputation: 43183

I don't know of a different way, and there is nothing wrong with what you have. It's using the standard HttpResponseMessage pattern rather than inventing a different way to do this. Generally, http triggered Functions just use the standard paradigms when it comes to dealing with the request.response.

Upvotes: 2

Related Questions