Is there a way of adding the Last-Modified header to an API response in asp .net? Perhaps a "best practice"?
I'm reading a date with "last modified" from the database, and would like to set that in the header so that resources can cache accordingly. I guess I don't know enough about the life cycle of the response. Is there an event I can attach to where I have access to both the response (headers) and before the result object is serialized to JSON ?
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
// context.Response.Body seams to already have been set to a
// stream object, and I would in stead like to find the date in
// the object I'm sending back, which implements an interface
// containing the date
context.Response.Headers.Append("Last-Modified","????");
}
})
The code in the API controller
[HttpGet]
[Route("system/resources/{lcid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IResult> Get(int lcid, CancellationToken token)
{
var resources = (await _resourceManager.GetResourcesAsync(lcid, token).ConfigureAwait(false));
if (resources?.Items?.Any() ?? false)
{
// resources has a .LastModified property
return Results.Ok(resources );
}
else
{
return Results.NotFound();
}
}
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
The easiest way is to just set the header in the action itself :
if (resources?.Items?.Any() ?? false)
{
Response.Headers.LastModified=resources.LastModified;
// resources has a .LastModified property
return Results.Ok(resources );
}
else
{
return Results.NotFound();
}
A reusable option could be to create an action filter. Let's assume the result implements this ICacheInfo
interface so we don't have to use reflection. It uses a default interface member so it can be applied to objects and modified without breaking the object :
interface ICacheInfo
{
DateTimeOffset LastModified=>DateTimeOffset.Now;
}
The action filter could look like this:
public class LastModifiedHeaderAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is OkObjectResult {Value:ICacheInfo c})
{
context.HttpContext.Response.Headers.LastModified=c.LastModified;
}
base.OnResultExecuting(context);
}
}
And applied as an attribute:
[HttpGet]
[Route("system/resources/{lcid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[LastModifiedHeader]
public async Task<IResult> Get(int lcid, CancellationToken token)
{
...
}