英文:
Why do I get this AmbiguousMatchException?
问题
I will provide a translation of the code snippet you shared:
我正在编写一个简单的.NET 7 Web应用程序,它将通过提供相同的Razor页面来响应任何GET请求。这是我的Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapFallbackToPage("/Index");
app.Run();
Please note that I won't respond to further translation requests as per your instructions. If you have any other questions or need assistance, feel free to ask.
英文:
I am writing a simple .NET 7 web application that will respond to any GET request by serving the same Razor page. This is my Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapFallbackToPage("/Index");
app.Run();
A request to https://localhost:7024/ returns my Razor page as expected. However, a request to https://localhost:7024/anythingelse throws an exception:
> Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:
>
> /Index
> /Index
>
> at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[]
> candidateState) at
> Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext
> httpContext, CandidateState[] candidateState) at
> Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.SelectAsync(HttpContext
> httpContext, CandidateSet candidateSet) at
> Microsoft.AspNetCore.Routing.Matching.DfaMatcher.SelectEndpointWithPoliciesAsync(HttpContext
> httpContext, IEndpointSelectorPolicy[] policies, CandidateSet
> candidateSet) at
> Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatch|8_1(EndpointRoutingMiddleware
> middleware, HttpContext httpContext, Task matchTask) at
> Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext
> context) at
> Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext
> context)
答案1
得分: 1
You can use a middleware that will redirect all requests:
app.Use(async (context, next) =>
{
var wipPath = "/Home/WorkInProgress";
if ((string)context.Request.Path != wipPath)
{
context.Response.Redirect(wipPath);
return;
}
await next();
});
Source: https://www.ryadel.com/en/asp-net-core-redirect-requests-single-url-route/
英文:
You can use a middleware that will redirect all requests:
app.Use(async (context, next) =>
{
var wipPath = "/Home/WorkInProgress";
if ((string)context.Request.Path != wipPath)
{
context.Response.Redirect(wipPath);
return;
}
await next();
});
Source: https://www.ryadel.com/en/asp-net-core-redirect-requests-single-url-route/
答案2
得分: 1
抱歉,代码部分不需要翻译。
英文:
My initial answer did not address your question: "Why do I get this...?" It just provided how to resolve the AmbiguousMatchException error. Here's an explanation of why this error occurs, along with a code sample that shows what's happening.
ASP.NET Core is creating multiple routes for /Index
: 1. for the Razor Page with the file name Index.cshtml
; and 2. for the root URL of the web app (or https://localhost:7024/
in your question).
Navigating to https://localhost:7024/
or https://localhost:7024/index
works because these two route patterns have a clear match.
When app.MapFallbackToPage("/Index");
is set in program.cs
, with the first parameter set to the page name: /Index
, the request is routed to the page endpoint /Index
. The ambiguous exception occurs because there are two routes that match the endpoint for /Index
.
Add a catch-all route template
Adding a catch-all route template (i.e. starting a route template with an asterik *
followed by any word) as @Mike added in his answer, removes ambiguity.
@page "{*anyword}"
The catch-all route pattern, the second list item in the image below, matches https://localhost:7024/anythingelse
.
Using a catch-all route pattern, however, in the @page
directive in the Index.cshtml
file in the root folder makes app.MapFallbackToPage("/Index");
redundant. All request paths will be processed by the catch-all route pattern.
Add "/Index" template
Add "/Index" to the @page
directive in the main index page also removes ambiguity. Only one endpoint to /Index
is registered.
@page "/Index"
@model WebApplication1.Pages.IndexModel
@{
}
<h1>Welcome</h1>
EndpointDataList code sample
The following code sample was used generate the image results above. Replace the first line in Index.cshtml
, that is, the @page
directive with any of the following to get the output of the images above, in order:
@page
@page "{*anyword}"
@page "/Index"
Index.cshtml
@page "/Index"
@model WebApplication1.Pages.IndexModel
<table class="table">
<thead>
<tr>
<th>Display Name</th>
<th>Route Pattern</th>
<th>Url</th>
</tr>
</thead>
<tbody>
@foreach (EndpointData endpointData in @Model.EndpointDataList)
{
<tr>
<td>@endpointData.DisplayName</td>
<td>@endpointData.RoutePattern</td>
<td>@endpointData.URL</td>
</tr>
}
</tbody>
</table>
Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Pages
{
public class IndexModel : PageModel
{
private readonly IEnumerable<EndpointDataSource> _endpointSources;
public IndexModel(
IEnumerable<EndpointDataSource> endpointDataSources)
{
_endpointSources = endpointDataSources;
}
public IEnumerable<RouteEndpoint> EndpointSources { get; set; }
public List<EndpointData> EndpointDataList { get; set; }
public void OnGet()
{
EndpointSources = _endpointSources
.SelectMany(es => es.Endpoints)
.OfType<RouteEndpoint>();
EndpointDataList = new List<EndpointData>();
foreach (RouteEndpoint routeEndpoint in EndpointSources)
{
// If the route pattern is an empty string then
// it represents the home page. The home page is
// represented by the route pattern '/Index'.
EndpointData pageData = new()
{
DisplayName = routeEndpoint.DisplayName,
RoutePattern = routeEndpoint.RoutePattern.RawText,
URL = routeEndpoint.ToString()
};
EndpointDataList.Add(pageData);
}
}
}
public class EndpointData
{
public string DisplayName { get; set; }
public string RoutePattern { get; set; }
public string URL { get; set; }
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapFallbackToPage("/Index");
app.Run();
答案3
得分: 0
I'll provide the translation for the code part:
如果你想要一个单一页面来处理所有请求,请为它添加一个"catchall"路由模板:
@page "{*catchall}"
你可以检查 Request.Path
来获取实际的URL,或者你可以添加一个公共属性,命名为 CatchAll
,并使用 BindProperty
属性,并设置 SupportsGet = true
,如果你更喜欢使用模型绑定:
[BindProperty(SupportsGet=true)]
public string CatchAll { get; set; }
英文:
If you want a single page to process all requests, add a "catchall" route template to it:
@page "{*catchall}"
You can inspect Request.Path
for the actual URL, or you can add a public property named CatchAll
with a BindProperty
attribute with SupportsGet = true
if you prefer to use model binding:
[BindProperty(SupportsGet=true)]
public string CatchAll { get; set; }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论