Skip UseFileServer when wwwroot is missing.
Local Aspire has no published client folder; the middleware warned on every boot. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
# Баг 4. UseFileServer без каталога wwwroot
|
||||||
|
|
||||||
|
## Симптом
|
||||||
|
|
||||||
|
При локальном старте сервера (Aspire, без publish):
|
||||||
|
|
||||||
|
```
|
||||||
|
warn: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[16]
|
||||||
|
The WebRootPath was not found: …\src\HSchool.Server\wwwroot. Static files may be unavailable.
|
||||||
|
```
|
||||||
|
|
||||||
|
Клиент в деве идёт с Vite на 5173, не с сервера. Ожидали отсутствие этого предупреждения.
|
||||||
|
|
||||||
|
## Причина
|
||||||
|
|
||||||
|
`Program` всегда зовёт `UseFileServer()`. ASP.NET подставляет `ContentRoot/wwwroot` даже если папки нет и пишет warning 16. Каталог появляется только в опубликованном контейнере (`PublishWithContainerFiles(client, "wwwroot")`).
|
||||||
|
|
||||||
|
## Путь
|
||||||
|
|
||||||
|
`UseFileServer` только если `wwwroot` есть на диске. Отвергнуто: пустой каталог с `.gitkeep`; флаг `IsDevelopment` (publish с Development тогда не раздаст клиент).
|
||||||
|
|
||||||
|
## Задачи
|
||||||
|
|
||||||
|
- [x] Не вызывать `UseFileServer`, пока `WebRootPath` не существует
|
||||||
|
- [x] Тест предиката на существующий и отсутствующий каталог
|
||||||
|
|
||||||
|
## Тест, без которого не закрыт
|
||||||
|
|
||||||
|
`HSchool.Server.Tests`: `ClientStaticFiles.CanServe` ложь на несуществующем пути, истина на живом каталоге.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test tests/HSchool.Server.Tests --filter FullyQualifiedName~ClientStaticFiles
|
||||||
|
```
|
||||||
|
|
||||||
|
## Стоп
|
||||||
|
|
||||||
|
Протокол, сейв, Vite, `PublishWithContainerFiles` не трогать.
|
||||||
@@ -12,3 +12,4 @@
|
|||||||
| [1. Окно входа](01-login-layout.md) | ✅ | Форма входа прижата к углу, поле пароля на всю ширину |
|
| [1. Окно входа](01-login-layout.md) | ✅ | Форма входа прижата к углу, поле пароля на всю ширину |
|
||||||
| [2. Запуск AppHost](02-client-endpoint-proxy.md) | ✅ | `run-aspire` падает: proxy + Port=TargetPort=5173 у client |
|
| [2. Запуск AppHost](02-client-endpoint-proxy.md) | ✅ | `run-aspire` падает: proxy + Port=TargetPort=5173 у client |
|
||||||
| [3. users.json как сейв](03-users-json-as-school.md) | ✅ | При старте: `Save …/users.json has id 0; ids start at 1` |
|
| [3. users.json как сейв](03-users-json-as-school.md) | ✅ | При старте: `Save …/users.json has id 0; ids start at 1` |
|
||||||
|
| [4. Нет wwwroot](04-wwwroot-not-found.md) | 🔄 | При старте: `The WebRootPath was not found: …/wwwroot` |
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace HSchool.Server;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Published images copy the Vite build into wwwroot. Local Aspire has no such folder.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ClientStaticFiles
|
||||||
|
{
|
||||||
|
public static bool CanServe(string? webRootPath) =>
|
||||||
|
!string.IsNullOrEmpty(webRootPath) && Directory.Exists(webRootPath);
|
||||||
|
}
|
||||||
@@ -159,7 +159,11 @@ app.Map("/ws/game", async (HttpContext context, GameSocketHandler handler, Sessi
|
|||||||
app.MapDefaultEndpoints();
|
app.MapDefaultEndpoints();
|
||||||
|
|
||||||
// In a published container the built client lands in wwwroot next to the server.
|
// In a published container the built client lands in wwwroot next to the server.
|
||||||
app.UseFileServer();
|
// Local Aspire has no such folder — Vite serves the UI, and UseFileServer would warn.
|
||||||
|
if (ClientStaticFiles.CanServe(app.Environment.WebRootPath))
|
||||||
|
{
|
||||||
|
app.UseFileServer();
|
||||||
|
}
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace HSchool.Server.Tests;
|
||||||
|
|
||||||
|
public class ClientStaticFilesTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CanServe_IsFalseWhenTheDirectoryIsMissing()
|
||||||
|
{
|
||||||
|
var missing = Path.Combine(Path.GetTempPath(), "h-school-no-wwwroot-" + Guid.NewGuid().ToString("N"));
|
||||||
|
|
||||||
|
Assert.False(ClientStaticFiles.CanServe(missing));
|
||||||
|
Assert.False(ClientStaticFiles.CanServe(null));
|
||||||
|
Assert.False(ClientStaticFiles.CanServe(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanServe_IsTrueWhenTheDirectoryExists()
|
||||||
|
{
|
||||||
|
var directory = Directory.CreateTempSubdirectory("h-school-wwwroot-");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.True(ClientStaticFiles.CanServe(directory.FullName));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
directory.Delete(recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user