Проблема с настройкой IIS UrlRewrite для ASP.NET Core 3.1
Мигрирую старый проект c asp net core 2.2 на asp net core 3.1. В качестве frontend используется Angular. Вот рабочий код .net core 2.2, который подключает правила UrlRewrite для IIS. Код из класса Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
ConfigureUrlRewriting(app, env);
ConfigureRouting(app);
}
private static void ConfigureUrlRewriting(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment() && File.Exists("IISUrlRewrite.xml"))
{
using (var reader = File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions().AddIISUrlRewrite(reader);
app.UseRewriter(options);
}
}
}
private static void ConfigureRouting(IApplicationBuilder app)
{
app.UseDefaultFiles(new DefaultFilesOptions
{
DefaultFileNames = new List<string> { "index.html" }
});
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "api/{controller}/{id}");
});
}
После миграции на .net core 3.1 по гайду от Microsoft код не сильно изменился:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
ConfigureUrlRewriting(app, env);
ConfigureRouting(app);
}
private static void ConfigureUrlRewriting(IApplicationBuilder app, IHostEnvironment env)
{
if (env.IsDevelopment() && File.Exists("IISUrlRewrite.xml"))
{
using (var reader = File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions().AddIISUrlRewrite(reader);
app.UseRewriter(options);
}
}
}
private static void ConfigureRouting(IApplicationBuilder app)
{
app.UseDefaultFiles(new DefaultFilesOptions
{
DefaultFileNames = new List<string> { "index.html" }
});
app.UseStaticFiles();
// Изменилось только то, что ниже
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "api/{controller}/{id}");
});
}
Файл index.html браузер возвращает, но все статические файлы почему-то не находятся. Вместо них возвращается index.html. Вот доказательство:
Из документации майкрософта для UrlRewrite понятно, что добавление middleware AddRewriter(rewriter) должно происходить до AddStaticFiles().
Не понимаю, что могло поменяться в .net core, что такая последовательность добавления middleware могла перестать работать как ожидается. В чем может быть дело? Уже сижу двое суток, борюсь с этой проблемой. Может быть, для .net core 3.1 используется какой-то другой IIS модуль и синтаксис xml поменялся?
На всякий случай, добавлю содержимое файла IISUrlRewrite.xml:
<rewrite>
<rules>
<rule name="AngularJS Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)/" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
Ответы (2 шт):
Читая раздел When to use URL Rewriting Middleware из документации Microsoft по UrlRewrite Middleware был неприятно удивлен. Оказывается, что UrlRewrite Middleware не поддерживает IsFile и IsDirectory ограничения. Цитата:
The main reasons to use the server-based URL rewriting technologies in IIS, Apache, and Nginx are:
The middleware doesn't support the full features of these modules.
Some of the features of the server modules don't work with ASP.NET Core projects, such as the IsFile and IsDirectory constraints of the IIS Rewrite module. In these scenarios, use the middleware instead.
Поэтому в моем случае эти условия не срабатывают
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
Из-за чего вместо статических файлов мне возвращается index.html. Что самое интересное -- с уже устаревшей версией ASP.NET Core 2.2 эти правила на статические файлы и директории работают. Получается, при переходе на Generic Host что-то очень сильно поменялось, из-за чего Microsoft решили отказаться от поддержки IsFile и IsDirectory
В итоге решил отказаться от этого Middleware и буду настраивать UrlRewrite на самом сервере
Зачем вообще нужен UrlRewrite? Попытка написания логики в XML-файле при наличии под рукой нормального языка программирования выглядит странно. Вот как подобное делается без UrlRewrite:
app.UseStaticFiles();
// Если мы дошли до сюда - значит, запрос уже не соответствует ни одному реальному файлу
app.UseRouting();
app.MapWhen(
ctx => ctx.GetEndpoint() == null, // Если для запроса не найден маршрут…
app2 => app2.Use((ctx, next) =>
{
ctx.Request.Path = "/index.html"; // …то переписываем URL запроса…
return next();
})
.UseStaticFiles() // …и даём ещё один шанс UseStaticFiles
);
app.UseEndpoints(…);
Также вместо MapWhen можно просто перенести всю обработку (это выглядит проще, но при добавлении авторизации поломается):
app.UseStaticFiles();
// Если мы дошли до сюда - значит, запрос уже не соответствует ни одному реальному файлу
app.UseRouting();
app.UseEndpoints(…);
// Если мы дошли до сюда - значит, для запроса не найден маршрут
app.Use((ctx, next) =>
{
ctx.Request.Path = "/index.html";
return next();
})
app.UseStaticFiles();
