Merge remote-tracking branch 'origin/release/3.1'
This commit is contained in:
commit
ea4b52ccba
|
|
@ -85,7 +85,14 @@ export class WebAssemblyResourceLoader {
|
||||||
const cacheKey = toAbsoluteUri(`${url}.${contentHash}`);
|
const cacheKey = toAbsoluteUri(`${url}.${contentHash}`);
|
||||||
this.usedCacheKeys[cacheKey] = true;
|
this.usedCacheKeys[cacheKey] = true;
|
||||||
|
|
||||||
const cachedResponse = await cache.match(cacheKey);
|
let cachedResponse: Response | undefined;
|
||||||
|
try {
|
||||||
|
cachedResponse = await cache.match(cacheKey);
|
||||||
|
} catch {
|
||||||
|
// Be tolerant to errors reading from the cache. This is a guard for https://bugs.chromium.org/p/chromium/issues/detail?id=968444 where
|
||||||
|
// chromium browsers may sometimes throw when working with the cache.
|
||||||
|
}
|
||||||
|
|
||||||
if (cachedResponse) {
|
if (cachedResponse) {
|
||||||
// It's in the cache.
|
// It's in the cache.
|
||||||
const responseBytes = parseInt(cachedResponse.headers.get('content-length') || '0');
|
const responseBytes = parseInt(cachedResponse.headers.get('content-length') || '0');
|
||||||
|
|
@ -136,12 +143,19 @@ export class WebAssemblyResourceLoader {
|
||||||
|
|
||||||
// Add to cache as a custom response object so we can track extra data such as responseBytes
|
// Add to cache as a custom response object so we can track extra data such as responseBytes
|
||||||
// We can't rely on the server sending content-length (ASP.NET Core doesn't by default)
|
// We can't rely on the server sending content-length (ASP.NET Core doesn't by default)
|
||||||
await cache.put(cacheKey, new Response(responseData, {
|
const responseToCache = new Response(responseData, {
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': response.headers.get('content-type') || '',
|
'content-type': response.headers.get('content-type') || '',
|
||||||
'content-length': (responseBytes || response.headers.get('content-length') || '').toString()
|
'content-length': (responseBytes || response.headers.get('content-length') || '').toString()
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await cache.put(cacheKey, responseToCache);
|
||||||
|
} catch {
|
||||||
|
// Be tolerant to errors writing to the cache. This is a guard for https://bugs.chromium.org/p/chromium/issues/detail?id=968444 where
|
||||||
|
// chromium browsers may sometimes throw when performing cache operations.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@
|
||||||
|
|
||||||
<WriteLinesToFile
|
<WriteLinesToFile
|
||||||
File="$(_CombinedHashIntermediatePath)"
|
File="$(_CombinedHashIntermediatePath)"
|
||||||
Lines="@(_ServiceWorkerAssetsManifestItemWithHash->'%(FileHash)')"
|
Lines="@(_ServiceWorkerAssetsManifestItemWithHash->'%(Integrity)')"
|
||||||
WriteOnlyWhenDifferent="true"
|
WriteOnlyWhenDifferent="true"
|
||||||
Overwrite="true" />
|
Overwrite="true" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -41,5 +42,77 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
|
var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
|
||||||
Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
|
Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
|
||||||
|
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
|
||||||
|
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var publishOutputDirectory = project.PublishOutputDirectory;
|
||||||
|
|
||||||
|
var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
|
||||||
|
var version = File.ReadAllLines(serviceWorkerFile).Last();
|
||||||
|
var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
|
||||||
|
Assert.True(match.Success);
|
||||||
|
Assert.Equal(2, match.Groups.Count);
|
||||||
|
Assert.NotNull(match.Groups[1].Value);
|
||||||
|
var capture = match.Groups[1].Value;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css");
|
||||||
|
File.WriteAllText(cssFile, ".updated { }");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
|
||||||
|
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
|
||||||
|
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
|
||||||
|
Assert.True(updatedMatch.Success);
|
||||||
|
Assert.Equal(2, updatedMatch.Groups.Count);
|
||||||
|
Assert.NotNull(updatedMatch.Groups[1].Value);
|
||||||
|
var updatedCapture = updatedMatch.Groups[1].Value;
|
||||||
|
|
||||||
|
Assert.NotEqual(capture, updatedCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Publish_DeterministicAcrossBuilds_WhenNoSourcesChange()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
|
||||||
|
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
|
||||||
|
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var publishOutputDirectory = project.PublishOutputDirectory;
|
||||||
|
|
||||||
|
var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
|
||||||
|
var version = File.ReadAllLines(serviceWorkerFile).Last();
|
||||||
|
var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
|
||||||
|
Assert.True(match.Success);
|
||||||
|
Assert.Equal(2, match.Groups.Count);
|
||||||
|
Assert.NotNull(match.Groups[1].Value);
|
||||||
|
var capture = match.Groups[1].Value;
|
||||||
|
|
||||||
|
// Act && Assert
|
||||||
|
var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
|
||||||
|
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
|
||||||
|
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
|
||||||
|
Assert.True(updatedMatch.Success);
|
||||||
|
Assert.Equal(2, updatedMatch.Groups.Count);
|
||||||
|
Assert.NotNull(updatedMatch.Groups[1].Value);
|
||||||
|
var updatedCapture = updatedMatch.Groups[1].Value;
|
||||||
|
|
||||||
|
Assert.Equal(capture, updatedCapture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
||||||
|
// Ensure the file buffer stream is always disposed at the end of a request.
|
||||||
|
request.HttpContext.Response.RegisterForDispose(readStream);
|
||||||
|
|
||||||
await readStream.DrainAsync(CancellationToken.None);
|
await readStream.DrainAsync(CancellationToken.None);
|
||||||
readStream.Seek(0L, SeekOrigin.Begin);
|
readStream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
||||||
|
// Ensure the file buffer stream is always disposed at the end of a request.
|
||||||
|
request.HttpContext.Response.RegisterForDispose(readStream);
|
||||||
|
|
||||||
await readStream.DrainAsync(CancellationToken.None);
|
await readStream.DrainAsync(CancellationToken.None);
|
||||||
readStream.Seek(0L, SeekOrigin.Begin);
|
readStream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
|
||||||
|
// Ensure the file buffer stream is always disposed at the end of a request.
|
||||||
|
request.HttpContext.Response.RegisterForDispose(readStream);
|
||||||
|
|
||||||
await readStream.DrainAsync(CancellationToken.None);
|
await readStream.DrainAsync(CancellationToken.None);
|
||||||
readStream.Seek(0L, SeekOrigin.Begin);
|
readStream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
@ -278,7 +280,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
|
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
|
||||||
/// that is passed gives an ability to create serializer specific to the context.
|
/// that is passed gives an ability to create serializer specific to the context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
|
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
|
@ -297,7 +299,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
|
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
|
||||||
/// that is passed gives an ability to create serializer specific to the context.
|
/// that is passed gives an ability to create serializer specific to the context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">A context object used by an input formatter for deserializing the request body into an object.</param>
|
/// <param name="context">A context object used by an input formatter for deserializing the request body into an object.</param>
|
||||||
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
|
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
@page "/error"
|
@page
|
||||||
|
|
||||||
|
|
||||||
<h1 class="text-danger">Error.</h1>
|
<h1 class="text-danger">Error.</h1>
|
||||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
|
|
||||||
<h3>Development Mode</h3>
|
<h3>Development Mode</h3>
|
||||||
<p>
|
<p>
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||||
It can result in displaying sensitive information from exceptions to end users.
|
It can result in displaying sensitive information from exceptions to end users.
|
||||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||||
and restarting the app.
|
and restarting the app.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -25,13 +25,13 @@ namespace ComponentsWebAssembly_CSharp
|
||||||
builder.RootComponents.Add<App>("app");
|
builder.RootComponents.Add<App>("app");
|
||||||
|
|
||||||
#if (!Hosted || NoAuth)
|
#if (!Hosted || NoAuth)
|
||||||
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||||
#else
|
#else
|
||||||
builder.Services.AddHttpClient("ComponentsWebAssembly_CSharp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
|
builder.Services.AddHttpClient("ComponentsWebAssembly_CSharp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
|
||||||
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
|
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
|
||||||
|
|
||||||
// Supply HttpClient instances that include access tokens when making requests to the server project
|
// Supply HttpClient instances that include access tokens when making requests to the server project
|
||||||
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
|
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
|
||||||
#endif
|
#endif
|
||||||
#if(!NoAuth)
|
#if(!NoAuth)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -914,7 +914,7 @@
|
||||||
"Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs",
|
"Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs",
|
||||||
"Data/Migrations/ApplicationDbContextModelSnapshot.cs",
|
"Data/Migrations/ApplicationDbContextModelSnapshot.cs",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
"Pages/_Host.cshtml",
|
"Pages/_Host.cshtml",
|
||||||
|
|
@ -951,7 +951,7 @@
|
||||||
"Data/WeatherForecast.cs",
|
"Data/WeatherForecast.cs",
|
||||||
"Data/WeatherForecastService.cs",
|
"Data/WeatherForecastService.cs",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
"Pages/_Host.cshtml",
|
"Pages/_Host.cshtml",
|
||||||
|
|
@ -988,7 +988,7 @@
|
||||||
"Data/WeatherForecast.cs",
|
"Data/WeatherForecast.cs",
|
||||||
"Data/WeatherForecastService.cs",
|
"Data/WeatherForecastService.cs",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
"Pages/_Host.cshtml",
|
"Pages/_Host.cshtml",
|
||||||
|
|
@ -1024,7 +1024,7 @@
|
||||||
"_Imports.razor",
|
"_Imports.razor",
|
||||||
"Data/WeatherForecast.cs",
|
"Data/WeatherForecast.cs",
|
||||||
"Data/WeatherForecastService.cs",
|
"Data/WeatherForecastService.cs",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
|
|
@ -1061,7 +1061,7 @@
|
||||||
"Data/WeatherForecast.cs",
|
"Data/WeatherForecast.cs",
|
||||||
"Data/WeatherForecastService.cs",
|
"Data/WeatherForecastService.cs",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
"Pages/_Host.cshtml",
|
"Pages/_Host.cshtml",
|
||||||
|
|
@ -1098,7 +1098,7 @@
|
||||||
"Data/WeatherForecast.cs",
|
"Data/WeatherForecast.cs",
|
||||||
"Data/WeatherForecastService.cs",
|
"Data/WeatherForecastService.cs",
|
||||||
"Pages/Counter.razor",
|
"Pages/Counter.razor",
|
||||||
"Pages/Error.razor",
|
"Pages/Error.cshtml",
|
||||||
"Pages/FetchData.razor",
|
"Pages/FetchData.razor",
|
||||||
"Pages/Index.razor",
|
"Pages/Index.razor",
|
||||||
"Pages/_Host.cshtml",
|
"Pages/_Host.cshtml",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue