Remove Magic Link Generation
This change resolves #3512 and #3636 by removing 'magic' link generation and adding an extension method to add routes to areas correctly using the new pattern. This is pretty much exactly the same as how MapWebApiRoute works. For site authors, we recommend adding area-specific routes in a way that includes a default AND constraint for the area. Put your most specific (for link generation) routes FIRST. Ex: routes.MapRoute( "Admin/{controller}/{action}/{id?}", defaults: new { area = "Admin" }, constraints: new { area = "Admin" }); The bulk of the changes here are to tests that unwittingly relied on the old behavior.
This commit is contained in:
parent
91e837d465
commit
6875ee55d3
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Microsoft.AspNet.Routing;
|
||||
|
||||
namespace Microsoft.AspNet.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IRouteBuilder"/>.
|
||||
/// </summary>
|
||||
public static class MvcAreaRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the given MVC area with the specified
|
||||
/// <paramref name="name"/>, <paramref name="areaName"/> and <paramref name="template"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="areaName">The MVC area name.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapAreaRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string areaName,
|
||||
string template)
|
||||
{
|
||||
MapAreaRoute(routeBuilder, name, areaName, template, defaults: null, constraints: null, dataTokens: null);
|
||||
return routeBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the given MVC area with the specified
|
||||
/// <paramref name="name"/>, <paramref name="areaName"/>, <paramref name="template"/>, and
|
||||
/// <paramref name="defaults"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="areaName">The MVC area name.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the
|
||||
/// names and values of the default values.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapAreaRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string areaName,
|
||||
string template,
|
||||
object defaults)
|
||||
{
|
||||
MapAreaRoute(routeBuilder, name, areaName, template, defaults, constraints: null, dataTokens: null);
|
||||
return routeBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the given MVC area with the specified
|
||||
/// <paramref name="name"/>, <paramref name="areaName"/>, <paramref name="template"/>,
|
||||
/// <paramref name="defaults"/>, and <paramref name="constraints"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="areaName">The MVC area name.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the
|
||||
/// names and values of the default values.
|
||||
/// </param>
|
||||
/// <param name="constraints">
|
||||
/// An object that contains constraints for the route. The object's properties represent the names and
|
||||
/// values of the constraints.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapAreaRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string areaName,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints)
|
||||
{
|
||||
MapAreaRoute(routeBuilder, name, areaName, template, defaults, constraints, dataTokens: null);
|
||||
return routeBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the given MVC area with the specified
|
||||
/// <paramref name="name"/>, <paramref name="areaName"/>, <paramref name="template"/>,
|
||||
/// <paramref name="defaults"/>, <paramref name="constraints"/>, and <paramref name="dataTokens"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="areaName">The MVC area name.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the
|
||||
/// names and values of the default values.
|
||||
/// </param>
|
||||
/// <param name="constraints">
|
||||
/// An object that contains constraints for the route. The object's properties represent the names and
|
||||
/// values of the constraints.
|
||||
/// </param>
|
||||
/// <param name="dataTokens">
|
||||
/// An object that contains data tokens for the route. The object's properties represent the names and
|
||||
/// values of the data tokens.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapAreaRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string areaName,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints,
|
||||
object dataTokens)
|
||||
{
|
||||
if (routeBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(routeBuilder));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
var defaultsDictionary = new RouteValueDictionary(defaults);
|
||||
defaultsDictionary["area"] = areaName;
|
||||
|
||||
var constraintsDictionary = new RouteValueDictionary(constraints);
|
||||
constraintsDictionary["area"] = areaName;
|
||||
|
||||
routeBuilder.MapRoute(name, template, defaultsDictionary, constraintsDictionary, dataTokens);
|
||||
return routeBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,11 +32,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
|
|||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
EnsureServices(context.Context);
|
||||
|
||||
// The contract of this method is to check that the values coming in from the route are valid;
|
||||
// that they match an existing action, setting IsBound = true if the values are OK.
|
||||
context.IsBound = _actionSelector.HasValidAction(context);
|
||||
context.IsBound = true;
|
||||
|
||||
// We return null here because we're not responsible for generating the url, the route is.
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Builder
|
||||
{
|
||||
public class MvcAreaRouteBuilderExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void MapAreaRoute_Simple()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = Mock.Of<IRouter>(),
|
||||
ServiceProvider = CreateServices(),
|
||||
};
|
||||
|
||||
// Act
|
||||
builder.MapAreaRoute(name: null, areaName: "admin", template: "site/Admin/");
|
||||
|
||||
// Assert
|
||||
var route = Assert.IsType<TemplateRoute>((Assert.Single(builder.Routes)));
|
||||
|
||||
Assert.Null(route.Name);
|
||||
Assert.Equal("site/Admin/", route.RouteTemplate);
|
||||
Assert.Collection(
|
||||
route.Constraints.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.IsType<RegexRouteConstraint>(kvp.Value);
|
||||
});
|
||||
Assert.Empty(route.DataTokens);
|
||||
Assert.Collection(
|
||||
route.Defaults.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.Equal(kvp.Value, "admin");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapAreaRoute_Defaults()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = Mock.Of<IRouter>(),
|
||||
ServiceProvider = CreateServices(),
|
||||
};
|
||||
|
||||
// Act
|
||||
builder.MapAreaRoute(
|
||||
name: "admin_area",
|
||||
areaName: "admin",
|
||||
template: "site/Admin/",
|
||||
defaults: new { action = "Home" });
|
||||
|
||||
// Assert
|
||||
var route = Assert.IsType<TemplateRoute>((Assert.Single(builder.Routes)));
|
||||
|
||||
Assert.Equal("admin_area", route.Name);
|
||||
Assert.Equal("site/Admin/", route.RouteTemplate);
|
||||
Assert.Collection(
|
||||
route.Constraints.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.IsType<RegexRouteConstraint>(kvp.Value);
|
||||
});
|
||||
Assert.Empty(route.DataTokens);
|
||||
Assert.Collection(
|
||||
route.Defaults.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "action");
|
||||
Assert.Equal(kvp.Value, "Home");
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.Equal(kvp.Value, "admin");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapAreaRoute_DefaultsAndConstraints()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = Mock.Of<IRouter>(),
|
||||
ServiceProvider = CreateServices(),
|
||||
};
|
||||
|
||||
// Act
|
||||
builder.MapAreaRoute(
|
||||
name: "admin_area",
|
||||
areaName: "admin",
|
||||
template: "site/Admin/",
|
||||
defaults: new { action = "Home" },
|
||||
constraints: new { id = new IntRouteConstraint() });
|
||||
|
||||
// Assert
|
||||
var route = Assert.IsType<TemplateRoute>((Assert.Single(builder.Routes)));
|
||||
|
||||
Assert.Equal("admin_area", route.Name);
|
||||
Assert.Equal("site/Admin/", route.RouteTemplate);
|
||||
Assert.Collection(
|
||||
route.Constraints.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.IsType<RegexRouteConstraint>(kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "id");
|
||||
Assert.IsType<IntRouteConstraint>(kvp.Value);
|
||||
});
|
||||
Assert.Empty(route.DataTokens);
|
||||
Assert.Collection(
|
||||
route.Defaults.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "action");
|
||||
Assert.Equal(kvp.Value, "Home");
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.Equal(kvp.Value, "admin");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapAreaRoute_DefaultsConstraintsAndDataTokens()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = Mock.Of<IRouter>(),
|
||||
ServiceProvider = CreateServices(),
|
||||
};
|
||||
|
||||
// Act
|
||||
builder.MapAreaRoute(
|
||||
name: "admin_area",
|
||||
areaName: "admin",
|
||||
template: "site/Admin/",
|
||||
defaults: new { action = "Home" },
|
||||
constraints: new { id = new IntRouteConstraint() },
|
||||
dataTokens: new { some_token = "hello" });
|
||||
|
||||
// Assert
|
||||
var route = Assert.IsType<TemplateRoute>((Assert.Single(builder.Routes)));
|
||||
|
||||
Assert.Equal("admin_area", route.Name);
|
||||
Assert.Equal("site/Admin/", route.RouteTemplate);
|
||||
Assert.Collection(
|
||||
route.Constraints.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.IsType<RegexRouteConstraint>(kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "id");
|
||||
Assert.IsType<IntRouteConstraint>(kvp.Value);
|
||||
});
|
||||
Assert.Collection(
|
||||
route.DataTokens.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "some_token");
|
||||
Assert.Equal(kvp.Value, "hello");
|
||||
});
|
||||
Assert.Collection(
|
||||
route.Defaults.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "action");
|
||||
Assert.Equal(kvp.Value, "Home");
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.Equal(kvp.Value, "admin");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapAreaRoute_ReplacesValuesForArea()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = Mock.Of<IRouter>(),
|
||||
ServiceProvider = CreateServices(),
|
||||
};
|
||||
|
||||
// Act
|
||||
builder.MapAreaRoute(
|
||||
name: "admin_area",
|
||||
areaName: "admin",
|
||||
template: "site/Admin/",
|
||||
defaults: new { area = "Home" },
|
||||
constraints: new { area = new IntRouteConstraint() },
|
||||
dataTokens: new { some_token = "hello" });
|
||||
|
||||
// Assert
|
||||
var route = Assert.IsType<TemplateRoute>((Assert.Single(builder.Routes)));
|
||||
|
||||
Assert.Equal("admin_area", route.Name);
|
||||
Assert.Equal("site/Admin/", route.RouteTemplate);
|
||||
Assert.Collection(
|
||||
route.Constraints.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.IsType<RegexRouteConstraint>(kvp.Value);
|
||||
});
|
||||
Assert.Collection(
|
||||
route.DataTokens.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "some_token");
|
||||
Assert.Equal(kvp.Value, "hello");
|
||||
});
|
||||
Assert.Collection(
|
||||
route.Defaults.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal(kvp.Key, "area");
|
||||
Assert.Equal(kvp.Value, "admin");
|
||||
});
|
||||
}
|
||||
|
||||
private IServiceProvider CreateServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddRouting();
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -408,7 +408,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
"InlineConstraints_Products",
|
||||
"GetProductById",
|
||||
"id",
|
||||
"sdsd", ""
|
||||
"sdsd",
|
||||
"/area-exists/InlineConstraints_Products/GetProductById?id=sdsd"
|
||||
};
|
||||
|
||||
// Attribute Route, name:alpha constraint
|
||||
|
|
@ -458,7 +459,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
"GetProductByCategoryId",
|
||||
"catId",
|
||||
"500",
|
||||
""
|
||||
"/area-exists/InlineConstraints_Products/GetProductByCategoryId?catId=500"
|
||||
};
|
||||
|
||||
// Attribute Route, name:length(1,20)? constraint
|
||||
|
|
@ -488,7 +489,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
"GetProductByManufacturerId",
|
||||
"manId",
|
||||
"qwer",
|
||||
""
|
||||
"/area-exists/InlineConstraints_Products/GetProductByManufacturerId?manId=qwer"
|
||||
};
|
||||
|
||||
// Attribute Route, manId:int:min(10)? constraint
|
||||
|
|
@ -498,7 +499,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
"GetProductByManufacturerId",
|
||||
"manId",
|
||||
"1",
|
||||
""
|
||||
"/area-exists/InlineConstraints_Products/GetProductByManufacturerId?manId=1"
|
||||
};
|
||||
|
||||
// Attribute Route, dateTime:datetime constraint
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
@"<root data-root=""true""><input class=""form-control"" type=""number"" data-val=""true""" +
|
||||
@" data-val-range=""The field Age must be between 10 and 100."" data-val-range-max=""100"" "+
|
||||
@"data-val-range-min=""10"" data-val-required=""The Age field is required."" " +
|
||||
@"id=""Age"" name=""Age"" value="""" /><a href="""">Back to List</a></root>";
|
||||
@"id=""Age"" name=""Age"" value="""" /><a href=""/TagHelpers"">Back to List</a></root>";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("http://localhost/TagHelpers/Add");
|
||||
|
|
|
|||
|
|
@ -903,7 +903,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConventionalRoutedAction_InArea_ImplicitLeaveArea()
|
||||
public async Task ConventionalRoutedAction_InArea_StaysInArea()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/Travel/Flight").To(new { action = "Contact", controller = "Home", });
|
||||
|
|
@ -919,7 +919,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal("Flight", result.Controller);
|
||||
Assert.Equal("Index", result.Action);
|
||||
|
||||
Assert.Equal("/Home/Contact", result.Link);
|
||||
Assert.Equal("/Travel/Home/Contact", result.Link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -985,7 +985,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AttributeRoutedAction_InArea_ImplicitLeaveArea()
|
||||
public async Task AttributeRoutedAction_InArea_StaysInArea_ActionDoesntExist()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/ContosoCorp/Trains")
|
||||
|
|
@ -1002,7 +1002,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal("Rail", result.Controller);
|
||||
Assert.Equal("Index", result.Action);
|
||||
|
||||
Assert.Equal("/Home/Contact", result.Link);
|
||||
Assert.Equal("/Travel/Home/Contact", result.Link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1158,7 +1158,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ControllerWithCatchAll_GenerateLink_FailsWithoutCountry()
|
||||
public async Task ControllerWithCatchAll_GenerateLink_FallsThroughWithoutCountry()
|
||||
{
|
||||
// Arrange
|
||||
var url = LinkFrom("http://localhost/")
|
||||
|
|
@ -1170,7 +1170,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
|
||||
Assert.Null(result.Link);
|
||||
Assert.Equal("/Products/GetProducts", result.Link);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<a title="<the title>" href="">Product Index</a>
|
||||
<a title="<the title>" href="HtmlEncode[[/HtmlGeneration_Product]]">Product Index</a>
|
||||
</div>
|
||||
<div>
|
||||
<a title=""the" title" href="">Product List</a>
|
||||
<a title=""the" title" href="HtmlEncode[[/HtmlGeneration_Product/List]]">Product List</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_Index">HtmlGenerationWebSite Index</a>
|
||||
|
|
@ -13,10 +13,10 @@
|
|||
<a href="HtmlEncode[[/]]">Default Controller</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Product Index Fragment</a>
|
||||
<a href="HtmlEncode[[/Product/Index#fragment]]">Product Index Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Produt Submit Fragment</a>
|
||||
<a href="HtmlEncode[[/HtmlGeneration_Product/Submit#fragment]]">Product Submit Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_IndexFragment" href="HtmlEncode[[/#fragment]]">
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="HtmlEncode[[unkonwn://localhost/HtmlGeneration_Product/List]]">
|
||||
Unknown Protocol Product List
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -39,32 +39,32 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Customer Area Customer Index</a>
|
||||
<a href="HtmlEncode[[/Customer/Customer#fragment]]">Customer Area Customer Index</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/Order/List">Href Order List</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Controller</a>
|
||||
<a href="HtmlEncode[[/NonExistentController]]">Non-existent Controller</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="HtmlEncode[[/NonExistentController#fragment]]">
|
||||
Non-existent Controller Fragment
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Action</a>
|
||||
<a href="HtmlEncode[[/Order/NonExistentAction]]">Non-existent Action</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="Id" href="HtmlEncode[[http://somewhere/]]">Some Where</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="HtmlEncode[[unknown://localhost/NoControll#fragment]]">
|
||||
Unknown Protocol Non-existent Controller Fragment
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Product Route Non-existent Area Parameter</a>
|
||||
<a href="HtmlEncode[[/Product/Submit?area=NonExistentArea&id=1#fragment]]">Product Route Non-existent Area Parameter</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Area</a>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<a title="<the title>" href="">Product Index</a>
|
||||
<a title="<the title>" href="/HtmlGeneration_Product">Product Index</a>
|
||||
</div>
|
||||
<div>
|
||||
<a title=""the" title" href="">Product List</a>
|
||||
<a title=""the" title" href="/HtmlGeneration_Product/List">Product List</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_Index">HtmlGenerationWebSite Index</a>
|
||||
|
|
@ -13,10 +13,10 @@
|
|||
<a href="/">Default Controller</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Product Index Fragment</a>
|
||||
<a href="/Product/Index#fragment">Product Index Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Produt Submit Fragment</a>
|
||||
<a href="/HtmlGeneration_Product/Submit#fragment">Product Submit Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_IndexFragment" href="/#fragment">
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="unkonwn://localhost/HtmlGeneration_Product/List">
|
||||
Unknown Protocol Product List
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -39,32 +39,32 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Customer Area Customer Index</a>
|
||||
<a href="/Customer/Customer#fragment">Customer Area Customer Index</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/Order/List">Href Order List</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Controller</a>
|
||||
<a href="/NonExistentController">Non-existent Controller</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="/NonExistentController#fragment">
|
||||
Non-existent Controller Fragment
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Action</a>
|
||||
<a href="/Order/NonExistentAction">Non-existent Action</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="Id" href="http://somewhere/">Some Where</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">
|
||||
<a href="unknown://localhost/NoControll#fragment">
|
||||
Unknown Protocol Non-existent Controller Fragment
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Product Route Non-existent Area Parameter</a>
|
||||
<a href="/Product/Submit?area=NonExistentArea&id=1#fragment">Product Route Non-existent Area Parameter</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">Non-existent Area</a>
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ namespace HtmlGenerationWebSite
|
|||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "HtmlGeneration_Home", action = "Index" });
|
||||
routes.MapRoute(
|
||||
name: "areaRoute",
|
||||
template: "{area:exists}/{controller}/{action}/{id?}",
|
||||
|
|
@ -37,6 +33,10 @@ namespace HtmlGenerationWebSite
|
|||
name: "productRoute",
|
||||
template: "Product/{action}",
|
||||
defaults: new { controller = "Product" });
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "HtmlGeneration_Home", action = "Index" });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<a asp-controller="Product" title="<the title>">Product Index</a>
|
||||
<a asp-controller="HtmlGeneration_Product" title="<the title>">Product Index</a>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-controller="Product" asp-action="List" title='"the" title'>Product List</a>
|
||||
<a asp-controller="HtmlGeneration_Product" asp-action="List" title='"the" title'>Product List</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_Index">HtmlGenerationWebSite Index</a>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<a asp-fragment="fragment" asp-controller="Product">Product Index Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-controller="Product" asp-action="Submit" asp-fragment="fragment">Produt Submit Fragment</a>
|
||||
<a asp-controller="HtmlGeneration_Product" asp-action="Submit" asp-fragment="fragment">Product Submit Fragment</a>
|
||||
</div>
|
||||
<div>
|
||||
<a id="HtmlGenerationWebSite_IndexFragment" asp-fragment="fragment">
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-controller="Product" asp-protocol="unkonwn" asp-action="List">
|
||||
<a asp-controller="HtmlGeneration_Product" asp-protocol="unkonwn" asp-action="List">
|
||||
Unknown Protocol Product List
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,18 +24,21 @@ namespace RoutingWebSite
|
|||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute("areaRoute",
|
||||
"{area:exists}/{controller}/{action}",
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
routes.MapRoute(
|
||||
"areaRoute",
|
||||
"{area:exists}/{controller}/{action}",
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
routes.MapRoute(
|
||||
"products",
|
||||
"api/Products/{country}/{action}",
|
||||
defaults: new { controller = "Products" });
|
||||
|
||||
routes.MapRoute(
|
||||
"ActionAsMethod",
|
||||
"{controller}/{action}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
// Added this route to validate that we throw an exception when a conventional
|
||||
// route matches a link generated by a named attribute route.
|
||||
// The conventional route will match first, but when the attribute route generates
|
||||
|
|
|
|||
|
|
@ -23,15 +23,15 @@ namespace WebApiCompatShimWebSite
|
|||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
// This route can't access any of our webapi controllers
|
||||
routes.MapRoute("default", "{controller}/{action}/{id?}");
|
||||
|
||||
// Tests include different styles of WebAPI conventional routing and action selection - the prefix keeps
|
||||
// them from matching too eagerly.
|
||||
routes.MapWebApiRoute("named-action", "api/Blog/{controller}/{action}/{id?}");
|
||||
routes.MapWebApiRoute("unnamed-action", "api/Admin/{controller}/{id?}");
|
||||
routes.MapWebApiRoute("name-as-parameter", "api/Store/{controller}/{name?}");
|
||||
routes.MapWebApiRoute("extra-parameter", "api/Support/{extra}/{controller}/{id?}");
|
||||
|
||||
// This route can't access any of our webapi controllers
|
||||
routes.MapRoute("default", "{controller}/{action}/{id?}");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue