Search code examples
asp.net-mvcangularjsasp.net-mvc-routingangularjs-routing

ASP.NET MVC and AngularJS routing


I'm using AngularJS inside my ASP.NET MVC application, but I'm having a problem with nested routes. I'm using ASP.NET MVC routing but inside the SPA I'm using UI-Router.

My Home/Index is my AngularJS app and the <div ui-view> is in the Index.cshtml

I want my URL to be completely controlled by my AngularJS SPA (with html5Mode=true).

I have set up my RouteConfig in ASP.NET MVC like this:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

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

I'm using UI-Router, but that is irrelevant because this is a ASP.NET MVC routing problem. The AngularJS routing code however is the following:

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {

    $urlRouterProvider.otherwise("/");

    $stateProvider
    .state('home', {
      url: "/",
      templateUrl: "Scripts/app/partials/home.html",
      controller: "HomeController"
    })
    .state('login', {
      url: "/login",
      templateUrl: "Scripts/app/partials/login.html",
      controller: "AccountController"
    })
    .state('login.list', {
      url: "/list",
      templateUrl: "Scripts/app/partials/login.list.html",
      controller: "AccountController"
    });

    $locationProvider.html5Mode(true);

});

This works perfect when I go to / or /login, but when I go to /login/list I get a 404 from my ASP.NET MVC application saying:

The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

What is the problem? I have specified a {.*} rule so it should be directed to the AngularJS app.


Solution

  • "It's a SPA where the routes are controlled by AngularJS UI-Router" - Not true until the app is actually loaded.

    You need to make the application available from every conceivable route.

    What I normally do is to have a structure like this on the server:

    • assets/* -> Ignore Routes (pass thought to files)
    • api/* -> Api Routes
    • * -> index.html / index.cshtml / ???

    Here is an example of a set of routes in order which is common use for me.

    router
    .Route("assets/{*ignore}").Through()
    .Route("api/search").To<SearchController>()
    .Route("api/content/{contentType}/{id}").To<ContentController>(x => x.Set.Defaults(new {id = RouteParameter.Optional}))
    .Route("api/file/{contentType}/{id}/{head}").To<FileController>(x => x.Set.Defaults(new {id = RouteParameter.Optional, head = RouteParameter.Optional}))
    .Default().To<TController>();
    

    (Note that they are defined in a framework that wraps around the WebAPI framework, so hence the fluent syntax and strongly typed controller targeting)

    If you use Razor for your main view I normally do:

    <base href="@string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~/"))" /> 
    

    To set the base path.