使用 ASP.NET MVC 6 和 AngularJS 在 SPA 中路由

Routing in SPA with ASP.NET MVC 6 and AngularJS

我有一个示例 MVC6 单页应用程序,我想在其中使用 ngRoute 加载 2 Angular 个局部视图。您可以在 GitHub

查看它

应用中有 3 个 URL:

  1. 本地主机 - Index.cshtml
  2. localhost/games - Index.cshtml 和 Angular 的 gamelist.html 部分
  3. localhost/games/2 - Index.cshtml 和 Angular 的 game.html 部分

路由配置如下:

MVC:

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}");

            routes.MapRoute("gamelist", "games", new { controller = "Home", action = "Index"});
            routes.MapRoute("gameWithId", "games/2", new { controller = "Home", action = "Index" });
        });

Angular:

myApp.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
    $routeProvider
        .when('/games', {
            templateUrl: 'partials/gameslist.html',
            controller: 'GameController',
            controllerAs: 'ctrl'
        })
        .when('/games/:gameId', {
            templateUrl: 'partials/game.html',
            controller: 'GameController',
            controllerAs: 'ctrl'
        });

    $locationProvider.html5Mode(true);
}]);

只要我从主页“/”启动应用程序,然后使用页面上的链接导航到部分内容,它就可以正常工作。问题是 URL #3 (localhost/games/2) 如果我从它启动应用程序,通过在地址栏中键入它,它不起作用。 URL #2 (/games/) 确实有效。

#3 不起作用的原因是 MVC 从 URL 中删除了“/games”部分,而 Angular 得到的只是“/2”。如果您 运行 示例应用程序,您将看到“$location.path = /2”。当然 Angular 无法使用该路径进行映射,并且不会呈现任何部分。所以我的问题是 - 如何使 MVC return 成为客户端的完整路径,以便 Angular 可以映射它?

为了让 link #3 在浏览器的地址栏中工作,我在 Angular 中关闭了 "html5Mode" 并使 links #-based。

您可以让它在 HTML5 模式下工作,您只需要确保每个请求都映射回您的 Index.cshtml 视图。那时 AngularJS 框架加载,client-side 路由启动并评估请求 URI 并加载适当的控制器和视图。

尽管我们使用带通配符的属性路由,但我们已经在 MVC 中使用多个 Angular 应用程序和不同的 .cshtml 页面完成了此操作,例如

[Route("{*anything}")]
public ActionResult Index()
{
    return View("Index");
}

通配符 (*) 告诉路由引擎 URI 的其余部分应该与 anything 参数匹配。

我还没有机会掌握 MVC6,但我认为您可以使用属性路由的 "new" 版本做这样的事情?

[HttpGet("{*anything:regex(^(.*)?$)}"]
public ActionResult Index()
{
    return View("Index");
}

感谢this blog

我认为这是一个更好的解决方案。

他的解决方案是重写不适合任何路由且对 angular 的着陆页没有任何扩展的请求。

这是代码。

public class Startup
{
    public void Configure(IApplicationBuilder app, IApplicationEnvironment environment)
    {
        // Route all unknown requests to app root
        app.Use(async (context, next) =>
        {
            await next();

            // If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
            // Rewrite request to use app root
            if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
            {
                context.Request.Path = "/app/index.html"; // Put your Angular root page here 
                await next();
            }
        });

        // Serve wwwroot as root
        app.UseFileServer();

        // Serve /node_modules as a separate root (for packages that use other npm modules client side)
        app.UseFileServer(new FileServerOptions()
        {
            // Set root of file server
            FileProvider = new PhysicalFileProvider(Path.Combine(environment.ApplicationBasePath, "node_modules")),
            // Only react to requests that match this path
            RequestPath = "/node_modules", 
            // Don't expose file system
            EnableDirectoryBrowsing = false
        });
    }
}