StaticFileOptions.OnPrepareResponse does not get called for index.html
Asked Answered
S

2

9

I'm currently trying to disable the caching for index.html for my Angular SPA with a .NET Core 2.2 backend.

I'm doing this according to this answer by setting an OnPrepareResponse action for my StaticFileOptions.

But the Cache-Control header never gets sent. When I set a breakpoint in the OnPrepareResponse action I break for everyfile except index.html

What am I missing here? How can I actually control the cache for the index.html file?

enter image description here

// I've changed nothing else in the default ASP.NET Core/Angular template
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...

    var staticFileOptions = new StaticFileOptions
    {
        OnPrepareResponse = context =>
        {
            // Breakpoint for next line hits for following files
            // 1: styles.61d14a95058dbe9da495.css
            // 2: runtime.a66f828dca56eeb90e02.js
            // 3: polyfills.7a0e6866a34e280f49e7.js
            // 4: main.d9791b5a6df420d81994.js
            // 5: favicon.ico
            if (context.File.Name == "index.html")
            {
                context.Context.Response.Headers
                    .Add("Cache-Control", "no-cache, no-store, must-revalidate");
                context.Context.Response.Headers.Add("Pragma", "no-cache");
                context.Context.Response.Headers.Add("Expires", "0");
            }
        }
    };

    app.UseStaticFiles(staticFileOptions);
    app.UseSpaStaticFiles(staticFileOptions);

    // ...
}
Surefire answered 17/9, 2019 at 8:38 Comment(0)
A
8

You can use this code to see if it work for you

      app.UseSpaStaticFiles(new StaticFileOptions()
            {
                OnPrepareResponse = ctx =>
                {
                    var headers = ctx.Context.Response.GetTypedHeaders();
                    headers.CacheControl = new CacheControlHeaderValue
                    {
                        Public = true,
                        MaxAge = TimeSpan.FromDays(0)
                    };

                }
            });


     app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";
                spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions()
                {
                    OnPrepareResponse = ctx => {
                        var headers = ctx.Context.Response.GetTypedHeaders();
                        headers.CacheControl = new CacheControlHeaderValue
                        {
                            Public = true,
                            MaxAge = TimeSpan.FromDays(0)
                        };
                    }
                };

                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });
Acetamide answered 17/9, 2019 at 10:26 Comment(5)
Thank you for your response :) This would disable the caching for all files, but I only want to disable it for index.html (as the js and css files are versioned anyways). I will play around with your soultion a bit if I can adapt it for my needs. But I've also just found a solution which works (see my answer)Surefire
@Surefire : I believe this answer by @TonyNgo is correct. The reason why the the OP's code doesn't work is that the index.html is processed by app.UseSpa() instead of app.UseSpaStatic(). Only other css/js/... static files are served by app.UseSpaStatic().Hako
I think the section under UseSpa is correct, but in the section inside UseSpaStaticFiles you should remove the OnPrepareResponse handler. That way you would only be disabling the cache for the default file path, and you would be enabling the cache for the other static files.Ladysmith
I'm using WebView2 with Kestrel server inside the windows WPF app. Last two comments above are correct. UseStaticFiles does nothing, UseSpaStaticFiles serves all css/js. UseSpa serves index.htmlTomlin
For anyone else running into the OnPrepareResponse simply not running, I found that the spa.UseAngularCliServer in dev mode prevents it from being hit. If you "npm run build" the spa app and then comment out the spa.UseAngularCliServer lines, it will start hitting the OnPrepareResponse.Polyphemus
S
5

Another solution I came up with looks like this. I'm not sure I love this solution but at least it works.

app.Use(async (context, next) =>
{
    context.Response.OnStarting(() =>
    {
        var requestPath = context.Request.Path.Value;

        if (requestPath.EndsWith("index.html"))
        {
            context.Response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
            context.Response.Headers.Add("Pragma", "no-cache");
            context.Response.Headers.Add("Expires", "0");
        }

        return Task.FromResult(0);
    });

    await next();
});
Surefire answered 17/9, 2019 at 10:36 Comment(1)
This approach does work. But it does not explain why your original code doesn't work. IMO, @Acetamide Ngo 's reply answers the reason.Hako

© 2022 - 2024 — McMap. All rights reserved.