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

[Blazor] Consume fingerprinted assets in MVC and Blazor #56045

Merged

Conversation

javiercn
Copy link
Member

@javiercn javiercn commented Jun 3, 2024

Fixes #56076

Overview

Fingerprinting endpoints are defined as part of the build process. The fingerprinted endpoints contain a property "fingerprint" with the fingerprint for the file content.
These endpoints end up mapped as StaticAsset endpoints and include cache headers to ensure that the content is cached for a long time.

The StaticAssets routerware exposes an IReadOnlyList to consumers. Each static asset descriptor represents an addressable resource and includes the route, the response headers, a set of properties that other layers can use to identify concrete assets, and a list of selectors, used to discriminate between resources mapped to the same path.

A StaticAssetDescriptor represents the "physical" representation of the file at the HTTP level.

The information from the descriptors is exposed to Components via the ResourceAssetCollection. This concept lives in Components and provides additional information about the resources in the app that can be used by components to obtain content specific URLs and to read additional properties from assets, like their integrity value, which can then be used to apply things like CSP or subresource integrity.

For scenarios see the companion issue #56076 and this commit that focuses on the template updates.

Key design points

Exposing the asset information to components

This could have been done in three ways:

  • Cascading Value.
  • Scoped service.
  • Through the RenderHandle

I chose to expose this through the render handle directly for a couple of motives:

  • It's something deeply coupled to the application being rendered.
  • We want this to be as performant as possible (ideally a dictionary lookup) and with minimal overhead.
  • We want to work for all components.

Why I avoided a cascading value:

  • Adding it as a cascading value requires the user to add a cascading value to their component every time, they want to look up an asset.
  • This is not really a hierarchical concept, is a per-renderer concept, we don't want situations in which this can be replaced/overriden.
  • Requires additional initialization on a per-request basis.

Why I avoided a scoped service:

  • Requires initializing the service on every request.
  • Does not work with OwningComponentBase components as the service will not be initialized.
  • Requires injecting the service, which is additional overhead.

How webassembly consumes fingerprinted assets:

  • When we detect webassembly or automodes are enabled for an app, we map an additional endpoint '_framework/resource-collection.<>.js' which exposes the resource collection as a JS module.
  • We emit the URL to the body of the request only when a webassembly component is rendered into the page as persisted component state.
  • During WebAssembly boot, we retrieve the url, import the module and call a function to retrieve the asset collection and reconstruct it in memory.
  • The URL we provide is content specific and is cached forever, so we only pay this cost once per user until the app gets updated.
  • The resource collection is also exposed at a human-readable URL '_framework/resource-collection.js' so the JS side has access to it should it be needed (for enhanced nav or other frameworks, 3rd party components).

Hot reload

  • During development/hot reload we disable certain features to ensure a good developer experience even in the event that some files change.
    • We remove the integrity information from the assets to avoid issues when a file is changed while the app is running.
    • We disable immutable caching from content-specific URLs and replace it with no-cache (in fact, we no-cache every asset/endpoint) to ensure that the browser always tries to retrieve up to date content.
      • As a note, we already support updating ETags and other properties on the flight.

Integrations

  • For MVC and Razor pages, this is almost transparent, since it's baked in on their built-in tag helpers.
  • For Blazor there is a new ImportMap component that takes care of rendering the importmap script.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Jun 3, 2024
@javiercn javiercn force-pushed the javiercn/static-web-asset-endpoints-fingerprinting-runtime branch 2 times, most recently from 2f69855 to 4a66520 Compare June 4, 2024 20:22
@javiercn javiercn marked this pull request as ready for review June 4, 2024 22:11
@javiercn javiercn marked this pull request as draft June 4, 2024 22:12
@javiercn javiercn marked this pull request as ready for review June 10, 2024 14:10
@javiercn javiercn force-pushed the javiercn/static-web-asset-endpoints-fingerprinting-runtime branch 2 times, most recently from afef78d to ec82e49 Compare June 13, 2024 21:29
@javiercn javiercn force-pushed the javiercn/static-web-asset-endpoints-fingerprinting-runtime branch from 849e896 to 456ee28 Compare June 14, 2024 13:57
@Webreaper
Copy link

Out of interest (and since this ticket is somewhat related to hot reload) is any work being done in .Net 9 to make hot reload work reliably for anything other than the most simple projects? If there's an open issue, I'd really like to track it. We have significant problems with the reliability of hot reload in both VS and Rider, on any platform, once a hosted blazor server project gets past anything larger than a couple of trivial pages/components.

@javiercn
Copy link
Member Author

@Webreaper this work is orthogonal to hot reload.

If you have concrete issues, it's best if you file issues for them and let us triage them. If there are already issues, then we'll dupe against those. With that said, the best way for us to resolve hot reload issues is if you include repro projects and detailed steps on how to trigger the concrete problems that you find. Without that, it's really hard for us to act on particular issues.

@Webreaper
Copy link

Thanks. The problem is that with hot reload, because it requires a substantially large and complex project to break it, a small repro is challenging. I can't post my enterprise blazor platform....

But I'll see what I can do and raise a separate issue. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Blazor] Runtime APIs to support fingerprinting
3 participants