-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Why this happens
ContentTypeProvider registers both built-in and custom patterns in the matcher, but when a match is found, the lookup prefers the built-in dictionary first: if (_builtInMappings.TryGetValue(match.Pattern, out mapping) || _customMappings.TryGetValue(match.Pattern, out mapping))
Because built-ins include entries for *.html and *.js (with Cache = null), those win and the user-provided Cache is ignored, resulting in the default behavior in DefineStaticWebAssetEndpoints:
fingerprinted: Cache-Control = "max-age=31536000, immutable"
non-fingerprinted: Cache-Control = "no-cache"
That matches the symptom you described after updating to 9.0.300+.
Proposed fix (small, targeted)
Change the order so that custom mappings take precedence over built-ins:
File: src/StaticWebAssetsSdk/Tasks/Data/ContentTypeProvider.cs
In TryGetMapping, swap the dictionary checks:
Before: if (_builtInMappings.TryGetValue(match.Pattern, out mapping) || _customMappings.TryGetValue(match.Pattern, out mapping))
After: if (_customMappings.TryGetValue(match.Pattern, out mapping) || _builtInMappings.TryGetValue(match.Pattern, out mapping))
This is minimal, matches the file’s style, and restores the expected behavior that user configuration overrides defaults. It does not change the special handling for fingerprinted assets (those still get immutable).
Suggested tests
Add a regression test to cover precedence and cache usage. Extending the existing ContentTypeProviderTests is sufficient and lightweight.
Example additions:
Verify that a custom ".html" mapping with a non-null Cache is returned and used (ensures precedence over built-in):
Create ContentTypeProvider with new[] { new ContentTypeMapping("text/html", "no-store, must-revalidate, no-cache", ".html", 2) }.
Resolve for "index.html" and assert mapping.Cache equals the custom value.
Optionally, cover compressed paths to ensure the same precedence applies with .gz:
Resolve for "index.html.gz" and assert mapping.Cache equals the custom value.
Behavior change note
This is a user-visible behavioral change for projects relying on built-in defaults winning over custom mappings, but it restores the previous (9.0.101–9.0.205) and expected behavior where user configuration overrides the built-ins.
Notes about code search
I inspected the current implementation in the repo to reach this conclusion. The code search results shown here may be incomplete; for a full view, browse the repository search directly: https://github.com/search?q=repo%3Adotnet%2Fsdk%20ContentTypeProvider&type=code
Open question for review
baronfel: any preference on whether we also flip the registration order in the matcher (AddIncludePatternsList) or is swapping the lookup order sufficient for clarity? The lookup swap alone works and is the smallest change.