0

Как перенаправить всё, кроме Web API, на /index.html

12

Я работаю над проектом на 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 ответ(ов)

0

Чтобы использовать сегмент с подстановочным знаком (wildcard segment) в ваших маршрутах ASP.NET MVC, вы можете настроить маршрут следующим образом:

routes.MapRoute(
    name: "Default",
    url: "{*anything}",
    defaults: new { controller = "Home", action = "Index" }
);

В этом примере маршрут перехватывает любой входящий URL и перенаправляет его на метод Index контроллера Home. Это будет полезно, если вы хотите обрабатывать все маршруты или динамически взаимодействовать с различными URL.

Важно помнить, что этот маршрут должен быть последним в вашем наборе маршрутов, так как маршруты обрабатываются в порядке их определения. Если вы определите другие маршруты после этого, они никогда не будут достигнуты.

Если у вас есть дополнительные вопросы или вам нужно больше примеров, не стесняйтесь спрашивать!

0

Ваша конфигурация в web.config позволяет настраивать пользовательские ошибки для статуса 404. Чтобы сделать это более "нативным" образом, можно использовать механизм обработки ошибок в ASP.NET MVC или ASP.NET Core. Например, для ASP.NET Core вы можете использовать middleware для обработки ошибок.

Вот пример настройки в ASP.NET Core:

  1. Убедитесь, что у вас есть контроллер для обработки ошибок:
public class ErrorController : Controller
{
    [Route("Error/{statusCode}")]
    public IActionResult HandleError(int statusCode)
    {
        if (statusCode == 404)
        {
            return View("NotFound");
        }
        return View("Error");
    }
}
  1. В 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 для централизованной обработки ошибок.

0

В моем случае ни один из предложенных подходов не сработал. Я застрял между двумя адскими сообщениями об ошибках: либо "такой тип страницы не обслуживается", либо что-то вроде 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.

0

У меня был похожий подход, как у нескольких ответов выше, но у него есть недостаток: если кто-то делает неправильный API-запрос, он получает индексную страницу вместо более полезного ответа.

Поэтому я обновил свой код так, чтобы он возвращал мою индексную страницу для любого запроса, который не начинается с /api:

//Web Api
GlobalConfiguration.Configure(config =>
{
    config.MapHttpAttributeRoutes();
});

//MVC
RouteTable.Routes.Ignore("api/{*anything}");
RouteTable.Routes.MapPageRoute("AnythingNonApi", "{*url}", "~/wwwroot/index.html");

Такой подход позволяет изолировать API-методы и обрабатывать остальные запросы в более удобной форме, предотвращая путаницу для пользователей, которые могут обращаться к вашему серверу некорректно.

0

Если вы работаете с 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, когда это необходимо. Надеюсь, это поможет!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь