Как перенаправить всё, кроме Web API, на /index.html
Я работаю над проектом на AngularJS внутри ASP.NET MVC с использованием Web API. Все работает отлично, за исключением случаев, когда вы пытаетесь перейти напрямую к URL, маршрутизируемому Angular, или обновить страницу. Вместо того чтобы копаться в конфигурации сервера, я подумал, что смогу сделать это с помощью маршрутизатора MVC.
Текущая конфигурация WebAPI:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Маршруты Web API
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = @"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiWithActionAndName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = @"^[a-z]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiWithAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Get" }
);
}
}
Текущая конфигурация RouteConfig:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute(""); // Позволяет загружать index.html
routes.IgnoreRoute("partials/*");
routes.IgnoreRoute("assets/*");
}
}
Текущий Global.asax.cs:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
}
}
ЦЕЛЬ:
/api/* продолжает вести к WebAPI, /partials/, и /assets/ обращаются к файловой системе, абсолютно все остальное должно быть направлено на /index.html, который является моим Angular одностраничным приложением.
--ИЗМЕНЕНИЕ--
Мне кажется, что я смог это заставить работать. Я добавил следующее в КОНЕЦ RouteConfig.cs:
routes.MapPageRoute("Default", "{*anything}", "~/index.html");
И это изменение в корневом web.config:
<system.web>
...
<compilation debug="true" targetFramework="4.5.1">
<buildProviders>
...
<add extension=".html" type="System.Web.Compilation.PageBuildProvider" /> <!-- Позволяет маршрутизировать всё на ~/index.html -->
...
</buildProviders>
</compilation>
...
</system.web>
Тем не менее, это кажется хаком. Есть ли лучший способ сделать это?
5 ответ(ов)
Чтобы использовать сегмент с подстановочным знаком (wildcard segment) в ваших маршрутах ASP.NET MVC, вы можете настроить маршрут следующим образом:
routes.MapRoute(
name: "Default",
url: "{*anything}",
defaults: new { controller = "Home", action = "Index" }
);
В этом примере маршрут перехватывает любой входящий URL и перенаправляет его на метод Index
контроллера Home
. Это будет полезно, если вы хотите обрабатывать все маршруты или динамически взаимодействовать с различными URL.
Важно помнить, что этот маршрут должен быть последним в вашем наборе маршрутов, так как маршруты обрабатываются в порядке их определения. Если вы определите другие маршруты после этого, они никогда не будут достигнуты.
Если у вас есть дополнительные вопросы или вам нужно больше примеров, не стесняйтесь спрашивать!
Ваша конфигурация в web.config
позволяет настраивать пользовательские ошибки для статуса 404. Чтобы сделать это более "нативным" образом, можно использовать механизм обработки ошибок в ASP.NET MVC или ASP.NET Core. Например, для ASP.NET Core вы можете использовать middleware для обработки ошибок.
Вот пример настройки в ASP.NET Core:
- Убедитесь, что у вас есть контроллер для обработки ошибок:
public class ErrorController : Controller
{
[Route("Error/{statusCode}")]
public IActionResult HandleError(int statusCode)
{
if (statusCode == 404)
{
return View("NotFound");
}
return View("Error");
}
}
- В
Startup.cs
, добавьте обработку ошибок в методеConfigure
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/Error/{0}");
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Это позволит вам обрабатывать статус 404 напрямую в вашем приложении, и вы сможете легко изменить представление для каждой ошибки, использовав контроллеры и представления ASP.NET MVC.
Если же вы используете старую версию ASP.NET (например, Web Forms), рассмотрите возможность создания пользовательского класса для обработки исключений или использования Global.asax
для централизованной обработки ошибок.
В моем случае ни один из предложенных подходов не сработал. Я застрял между двумя адскими сообщениями об ошибках: либо "такой тип страницы не обслуживается", либо что-то вроде 404.
Тем не менее, перенаправление URL сработало:
<system.webServer>
<rewrite>
<rules>
<rule name="AngularJS" stopProcessing="true">
<match url="[a-zA-Z]*" />
<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>
...
Обратите внимание, что я использовал [a-zA-Z]
, потому что не хочу перенаправлять запросы к файлам .js
, .map
и т.д.
Это решение работало в Visual Studio сразу после настройки, но в IIS вам может понадобиться установить модуль для перенаправления URL. Ссылку на него можно найти здесь: IIS URL Rewrite Module.
У меня был похожий подход, как у нескольких ответов выше, но у него есть недостаток: если кто-то делает неправильный API-запрос, он получает индексную страницу вместо более полезного ответа.
Поэтому я обновил свой код так, чтобы он возвращал мою индексную страницу для любого запроса, который не начинается с /api
:
//Web Api
GlobalConfiguration.Configure(config =>
{
config.MapHttpAttributeRoutes();
});
//MVC
RouteTable.Routes.Ignore("api/{*anything}");
RouteTable.Routes.MapPageRoute("AnythingNonApi", "{*url}", "~/wwwroot/index.html");
Такой подход позволяет изолировать API-методы и обрабатывать остальные запросы в более удобной форме, предотвращая путаницу для пользователей, которые могут обращаться к вашему серверу некорректно.
Если вы работаете с OWIN Self-Host и React Router и столкнулись с похожей проблемой, вот возможное решение.
Мой обходящий метод довольно прост: проверьте, существует ли запрашиваемый файл в системе. В противном случае верните index.html, так как не всегда нужно возвращать этот файл при запросе других статических ресурсов.
В вашем файле конфигурации Web API:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{*anything}",
defaults: new { controller = "Home", action = "Index" }
);
Затем создайте HomeController следующим образом:
public class HomeController : ApiController
{
[HttpGet]
[ActionName("Index")]
public HttpResponseMessage Index()
{
var requestPath = Request.RequestUri.AbsolutePath;
var filepath = "/path/to/your/directory" + requestPath;
// Если запрашиваемый файл существует в системе
if (File.Exists(filepath))
{
var mime = MimeMapping.GetMimeMapping(filepath);
var response = new HttpResponseMessage();
response.Content = new ByteArrayContent(File.ReadAllBytes(filepath));
response.Content.Headers.ContentType = new MediaTypeHeaderValue(mime);
return response;
}
else
{
var path = "/path/to/your/directory/index.html";
var response = new HttpResponseMessage();
response.Content = new StringContent(File.ReadAllText(path));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
}
}
Таким образом, ваше приложение сможет правильно обрабатывать запросы к статическим файлам и динамически возвращать index.html, когда это необходимо. Надеюсь, это поможет!
Как создать выпадающий список из enum в ASP.NET MVC?
Обмен сессиями между приложениями с использованием службы состояния сессии ASP.NET
Когда и зачем использовать TryUpdateModel в ASP.NET MVC 2?
Если папка не существует, создайте её
Как запустить модульные тесты на платформе x64?