ASP.NET Core Razor Pages Routing With parameters
Solution 1
If you need to be able to cater for either a referenceid
or a clientid
, or both, you can either make the first parameter required and pass in 0, or you can use a query string. Otherwise there is no way for routing to know if the 42
on the end of /details/2019-8-15/2019-8-16/42
is a clientid
or a referenceid
value.
As you have discovered, as far as routing is concerned
"{datestart}/{dateend}/{client?}"
is the same as
"{datestart}/{dateend}/{referenceid?}"
and will generate an AmbiguousActionException
.
If you want to make the first parameter required, the way to do that is as follows:
@page "{datestart}/{dateend}/{referenceid:int=0}/{client?}"
Any link internally generated by the tag helper will automatically populate the referenceid
segment with 0
unless another value is supplied.
Solution 2
Might be an odd way but you can add an addtional root by using the AddPageRoute method.
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/details", "{datestart}/{dateend}/{client?}");
});
Jackal
Updated on July 21, 2022Comments
-
Jackal almost 2 years
I have a simple page where users can search for records based on start and end date, and the Reference and Client fields are optional
on the details page I have this
@page "{datestart}/{dateend}/{referenceId?}/{client?}"
on the search page I have this post handler
public IActionResult OnPost() { if (!ModelState.IsValid) { var options = new MemoryCacheEntryOptions() { AbsoluteExpiration = DateTime.Now.AddMinutes(10) }; ViewData["ReferenceId"] = new SelectList(_context.Referencias.AsNoTracking().FromCache(), "Id", "Name"); return Page(); } return RedirectToPage("Details", new { datestart= SearchViewModel.DateStart.ToString("dd-MM-yyyy"), dateend = SearchViewModel.DateEnd.ToString("dd-MM-yyyy"), referenceId = SearchViewModel.ReferenceId, client = SearchViewModel.Client }); }
However everything works well except when the Reference field is null
on my details page
public void OnGet(string datestart, string dateend, int? referenceId, int? client)
The intended result was that i would be able to go to details page if i don't supply a referenceId event if i did supply a client.
The dates are always required though.
So is there a way that i can still route even if referenceId is not supplied but client is?
All i get is this exception
InvalidOperationException: No page named 'Details' matches the supplied values. Microsoft.AspNetCore.Mvc.Infrastructure.RedirectToPageResultExecutor.ExecuteAsync(ActionContext context, RedirectToPageResult result) Microsoft.AspNetCore.Mvc.RedirectToPageResult.ExecuteResultAsync(ActionContext context) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync() Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters() Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync() Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext) Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) NToastNotify.NtoastNotifyAjaxToastsMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+<>c__DisplayClass5_1+<b__1>d.MoveNext() Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Also I tried switch the order of {referenceId?} and {client?} and it worked but then it fails if i do the same for referenceId.
UPDATE
As per @Nick suggestion I tried once again with multiple routes and it except for the 2nd route, it won't route if it's the only one.
options.Conventions.AddAreaPageRoute("Production","/BackOffice/Account/Records/Details", "{datestart}/{dateend}/{client?}"); options.Conventions.AddAreaPageRoute("Production", "/BackOffice/Account/Records/Details", "{datestart}/{dateend}/{referenceId?}"); options.Conventions.AddAreaPageRoute("Production", "/BackOffice/Account/Records/Details", "{datestart}/{dateend}/{client?}/{referenceId?}");
-
Ehsan Sajjad over 4 yearsi don't think so, you might need to introduce overloads
-
Jackal over 4 yearsHow could i achieve this?
-
Ehsan Sajjad over 4 yearsyou can create a pagemodel with those 4 parameters as properties see below : learnrazorpages.com/razor-pages/routing
-
Mike Brind over 4 yearsProbably the best solution is to use query string values instead of route parameters. Otherwise you won't be able to disambiguate between the
referenceid
andclientid
when one is missing. -
Mike Brind over 4 yearsOr you can make
referenceid
required and pass in0
when there isn't one... -
Jackal over 4 yearsthanks that's exactly what i did @MikeBrind
-
Mike Brind over 4 yearsIf your app permits
clientid
or areferenceid
, the accepted answer will not work for the reasons I provided in the comments I posted to it. Use one of my previous suggestions, or provide a bit more detail on the parameters that the page expects.
-
-
Mike Brind over 4 yearsThis will match a route where the optional
referenceid
is provided but the optionalclientid
is not. -
Nick over 4 yearsIf you keep the current root in the razor page this should work fine.
-
Jackal over 4 yearshow do you specify an area in this route?
-
Nick over 4 yearsYou could try to use the AddAreaPageRoute method. docs.microsoft.com/en-us/dotnet/api/…
-
Jackal over 4 yearsthis does seem to work, the only difference is that the referenceId appears as a query string format after client "client?referenceId=10", I tried even set multiple routes with each parameter but gave me an error of too many endpoints
-
Nick over 4 yearsDid you kept the routing value in your razor page and added one using the AddPageRoute method addtionally? Because that worked fine for me.
-
Mike Brind over 4 yearsIf a
referenceid
is provided, but noclientid
is provided, your additional route will be matched, but thereferencid
will be bound to theclientid
parameter. -
Jackal over 4 yearsi udpated my post with what i implemented, it works except for 2nd route just for a referenceId