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

Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with response compression #28293

Closed
szalapski opened this issue Dec 1, 2020 · 15 comments
Labels
affected-few This issue impacts only small number of customers area-commandlinetools Includes: Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPI bug This issue describes a behavior which is not expected - a bug. feature-dotnetwatch This issue is related to the dotnet-watch command-line tool (now external) ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. investigate severity-nice-to-have This label is used by an internal tool Status: Resolved
Milestone

Comments

@szalapski
Copy link

szalapski commented Dec 1, 2020

Describe the bug

Hmmm, auto-refreshing the browser in .NET 5 using dotnet watch run doesn't seem to be working for me. I used to use Westwind.AspNetCore.LiveReload, but I have removed it to try this out. When I make a change, dotnet watch rebuilds and restarts the web server for me, but the browser doesn't refresh.

I am using dotnet watch --project MyProject\Server\MyProject.Server.csproj run.

I never see dotnet-watch reload socket connected in the console. Of course, I also never see File changes detected. Waiting for application to rebuild..

This is a project I upgraded from .NET Core 3.1. Is there anything that needs to be done in program.cs or startup.cs to enable this? What else could be interfering? How can I troubleshoot?

To Reproduce

On my project, I can reproduce by running it using dotnet watch --project MyProject\Server\MyProject.Server.csproj run and noticing that I never see dotnet-watch reload socket connected in the console.

I cannot reproduce it on a new project; auto-reload seems to work when I do dotnet watch run.

  • ASP.NET Core version 5.0.0
  • The output of dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.100
 Commit:    5044b93829

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18363
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.100\

Host (useful for support):
  Version: 5.0.0
  Commit:  cf258a14b7

.NET SDKs installed:
  5.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  • The IDE (VS / VS Code/ VS4Mac) you're running on, and it's version
    VS Pro 16.8.2

-Here's my launchSettings.json:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:49342",
      "sslPort": 44324
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "MyProject": {
      "commandName": "Project",
      "launchBrowser": true,
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

@szalapski
Copy link
Author

Created this issue based on #5456 (comment)

@szalapski
Copy link
Author

@danroth27 asked, "Does auto refresh work if you start from a new project? Have you set "launchBrowser": true in launchSettings.json?"

Yes, and I do have "launchBrowser": true.

@szalapski szalapski changed the title Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with an upgraded project Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with my upgraded project Dec 1, 2020
@szalapski szalapski changed the title Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with my upgraded project Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with my upgraded Blazor WebAssembly project Dec 1, 2020
@danroth27
Copy link
Member

@pranavkm

@javiercn javiercn added area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Dec 2, 2020
@pranavkm pranavkm added area-commandlinetools Includes: Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPI and removed area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Dec 2, 2020
@pranavkm
Copy link
Contributor

pranavkm commented Dec 2, 2020

@szalapski could you run dotnet watch --verbose run and include the output? It might provide some hints.

@szalapski
Copy link
Author

szalapski commented Dec 2, 2020

With --verbose, my command line shows me:
watch : Reloading browser.

But of course the browser doesn't reload. I still I never see dotnet-watch reload socket connected in the browser console, nor anything else that seems to be relevant.

Isn't there some implied middleware or something that is needed for this to work? It seems a bit too black-box for me to troubleshoot.

@pranavkm
Copy link
Contributor

pranavkm commented Dec 2, 2020

The middleware gets wired up using startup hooks and hosting startup (👻 magic) so you don't really need to do anything to set it up.

The middleware specifically looks for the closing body tag to determine where to inject the WebSocket script block that listens to dotnet watch. The code is very much inspired from Westwind.AspNetCore.LiveReload, but it's possible that there's a bug in our implementation. The middleware runs as part of your app and includes Debug logs. You could try changing the log level for the Microsoft.AspNetCore.Watch.BrowserRefresh filter to Debug to have it print it out.

Alternatively, if you're able to share the app I'd be happy to debug.

@szalapski
Copy link
Author

szalapski commented Dec 2, 2020

I have Debug on and I don't see any relevant console messages. I'll turn it on more selectively. UPDATE: it seems there are no logevents out of Microsoft.AspNetCore.Watch.BrowserRefresh, at Debug log level. I really think the relevant code isn't even running inside my site.

I can't publish the site publicly but I could share over MS Teams.

@szalapski
Copy link
Author

szalapski commented Dec 2, 2020

Here's my host config. Does anything here raise a flag?

        public static IWebHost BuildWebHost(string[] args)
        {
            string? env = System.Environment.GetEnvironmentVariable("MyCustomEnv");
            if (env == null) throw new PlatformNotSupportedException("MyCustomEnv environment variable not set");

            Console.WriteLine($"Starting TradePlanner.Server web host with appsettings.{env}.json...");


            return WebHost
                .CreateDefaultBuilder(args)
                .UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build())
                .UseEnvironment(env)
                .UseStaticWebAssets()
                .UseSerilog()
                .UseStartup<Startup>()
                .Build();
        }

@ghost
Copy link

ghost commented Dec 3, 2020

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@harvey-k
Copy link

I'm making the assumption that @szalapski is running into the same issue I did based on his comments here:
RickStrahl/Westwind.AspnetCore.LiveReload#38 (comment)

If not, please pardon the interruption and I'll create a new issue.

Using Response Compression causes the following to appear in the dotnet watch debugging output

dbug: Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware[3]
      Unable to configure browser refresh script injection on the response.

I'm guessing this is due to ResponseCompressionBody obfuscating the body close tag WebSocketScriptInjection is looking for.

As a workaround, reloading is working fine with the following in the Startup.cs Configure method

if(!env.IsDevelopment()) // response compression currently conflicts with dotnet watch browser reload
{
     app.UseResponseCompression();
}

Also, thank you for the ongoing efforts to improve the "frontend" developer inner-loop in ASP.Net Core, it's greatly appreciated!

@szalapski
Copy link
Author

szalapski commented Dec 26, 2020

Interesting that it could be a similar issue as we ran into in Westwind's live reload--good thought.

When I enable debug-level logging, I do indeed get Unable to configure browser refresh script injection on the response. out of Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware (BTW, @pranavkm , this should probably be a Warning, not Debug).

I tried commenting out response compression; the message went away, and instead I got Response markup was updated to include browser refresh script injection., and it started working. Great!

I suppose the solution, @pranavkm, is that you must ensure that BrowserRefreshMiddleware can be compatible with UseResponseCompression--I think it must happen after UseResponseCompression in the pipeline, even though we don't have a line for "UseBrowserRefreshMiddleware" in Startup.cs. Do you think you can fix it now?

@szalapski szalapski changed the title Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with my upgraded Blazor WebAssembly project Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with UseReponseCompression Jan 4, 2021
@szalapski szalapski changed the title Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with UseReponseCompression Auto-refreshing the browser in .NET 5 using dotnet watch run doesn't work with response compression Jan 4, 2021
@szalapski
Copy link
Author

Any thoughts, @pranavkm ?

@javiercn javiercn added affected-few This issue impacts only small number of customers bug This issue describes a behavior which is not expected - a bug. severity-nice-to-have This label is used by an internal tool labels Feb 19, 2021 — with ASP.NET Core Issue Ranking
@mkArtakMSFT mkArtakMSFT added the feature-dotnetwatch This issue is related to the dotnet-watch command-line tool (now external) label Feb 19, 2021
@baratgabor
Copy link

I have the same experience. Rebuilding happens, and watch : Reloading browser appears, but the page doesn't reload. --verbose doesn't show anything for me either, so unfortunately I can't add more information.

@pranavkm
Copy link
Contributor

pranavkm commented Mar 23, 2021

Sorry, I missed responding to this earlier. The way dotnet-watch works refreshes the browser is by

a) Adding a middleware using a hosting startup to register a middleware
b) For HTML responses, the middleware rewrites the page to include a script block

Hosting startups only allow registering middlewares at the beginning or end of the middleware pipeline, so we couldn't choose to register a middleware to be invoked before response compression happens. Response compression changes the app's content-type to no longer be plain text, so the dotnet-watch middleware is no longer able to operate.

A couple of possible workarounds:

  1. You could consider disabling the response compression middleware in Development (as noted by @harvey-k):
if (!env.IsDevelopment()) // response compression currently conflicts with dotnet watch browser reload
{
     app.UseResponseCompression();
}
  1. You could consider manually registering the dotnet-watch script if you do want response compression. We haven't documented it as yet, but we could start considering it part of our public contract.
// _Layout.cshtml
<environment names="Development">
    <script src="/_framework/aspnetcore-browser-refresh.js"></script>
</environment>

For option (2), please note that if the script was successfully injected by dotnet-watch, things might be broken. So only use if if you're able to verify that watch is unable to inject it.

@szalapski
Copy link
Author

OK, that seems a fine workaround, though it is a little surprising to an outsider that these two features wouldn't just work well together. I do indeed recommend documenting the approach you prefer (I think #1). But thanks regardless.

@mkArtakMSFT mkArtakMSFT added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Aug 12, 2021
@ghost ghost added the Status: Resolved label Aug 12, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Sep 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affected-few This issue impacts only small number of customers area-commandlinetools Includes: Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPI bug This issue describes a behavior which is not expected - a bug. feature-dotnetwatch This issue is related to the dotnet-watch command-line tool (now external) ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. investigate severity-nice-to-have This label is used by an internal tool Status: Resolved
Projects
None yet
Development

No branches or pull requests

7 participants