使用 ASP.NET MVC 6 和 AngularJS 在 SPA 中路由
Routing in SPA with ASP.NET MVC 6 and AngularJS
我有一个示例 MVC6 单页应用程序,我想在其中使用 ngRoute 加载 2 Angular 个局部视图。您可以在 GitHub
查看它
应用中有 3 个 URL:
- 本地主机 - Index.cshtml
- localhost/games - Index.cshtml 和 Angular 的 gamelist.html 部分
- 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");
}
我认为这是一个更好的解决方案。
他的解决方案是重写不适合任何路由且对 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
});
}
}
我有一个示例 MVC6 单页应用程序,我想在其中使用 ngRoute 加载 2 Angular 个局部视图。您可以在 GitHub
查看它应用中有 3 个 URL:
- 本地主机 - Index.cshtml
- localhost/games - Index.cshtml 和 Angular 的 gamelist.html 部分
- 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");
}
我认为这是一个更好的解决方案。
他的解决方案是重写不适合任何路由且对 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 }); } }