Page route prevents OnGet() receiving query argument - c#

I'm porting a WebForms application to Razor Pages, and I'm using the following route to prevent breaking one of my URLs.
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Resources/CharClass", "/Resources/CharClass.aspx");
});
But using the code below, when I use the URL /Resources/CharClass.aspx?page=500, page is always null.
cshtml
#page "{page:int?}"
cshtml.cs
public void OnGet(int? page)
{
//
}
The argument seems to work okay for pages that aren't routed this way. How can I have both?

I suspect that the problem you have is with the name of the parameter - page. This seems to conflict with the Page property of the PageModel class, and nothing is bound during model binding. You can however access the query string value using traditional methods:
var page = Request.Query["page"];
EDIT: Just seen that this is partly covered in a reply to your other question on the issue: Razor page link ignores route argument

Try:
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Resources/CharClass", "/Resources/CharClass.aspx/{page?}");
});

Related

Blazor server page with parameters throwing "404 not found " on manual refreshes

I have a Blazor Server app with a page that takes in parameters in the uri. When I click on an anchor tag that has the route setup to hit that page with params (like below) the link works fine, and the page loads.
<!--link content in here-->
However, if one tries to access the page from that url directly, or manually refreshes in the browser, then the page doesn't reinitialize or hit any breakpoints on the parameters. Instead, it throws a 404 not found.
So two things here:
Firstly, I'm confused about why it works fine from within the anchor tag, but dies any other way. Especially when pages without params in the #page directive work fine with refreshes/direct-urls.
Second, is this an intended behavior for Blazor Server or am I missing something here that's breaking the page refreshes/hitting-url-directly? Doesn't seem like a feature, but maybe I'm misunderstanding Blazor's routing.
Razor and Razor.cs for page in question:
#page "/MyPage/{Param1}/{Param2}"
<h1>MyPage</h1>
<Metrics Param1="#Param1" />
<Details Param1="#Param1" Param2="#Param2" />
<InProgress Param1="#Param1" Param2="#Param2" />
<InQueue Param1="#Param1" />
<br />
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using MyApp.Data.Models.ApiResponse;
namespace MyApp.Pages
{
public partial class MyPage
{
[Parameter]
public string Param1 { get; set; }
[Parameter]
public string Param2{ get; set; }
public TaskList Tasks { get; set; }
protected override Task OnInitializedAsync()
{
// work in progress, intend to do more here later on
var test = "";
return base.OnInitializedAsync();
}
}
}
Edit(s) -- per comment suggestions
UseEndpoints section of Configure method in Startup.cs:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
After further digging I noticed that the #param2 will occaionally have a . char in it. Blazor does have a need for configuring routes that have params with dots in them. The below fall back doesn't work:
endpoints.MapFallbackToPage("/MyPage/{Param1}/{Param2}", "/MyPage");
It throws an:
InvalidOperationException: Cannot find the fallback endpoint specified by route values: { page: /MyPage, area: }.
I'm guessing that the area: being empty is a problem but I'm not finding how or where to properly set that. The example from the link shows just the Page's name in the fallback. Could someone please point out what's wrong with this fall back and how one can properly correct it?
My problem was with a dot character being in the parameter's value. When the routing issue is from a "dot" being in the parameter, then doing exactly what the docs recommends does the trick (go figure). You will want to specify a fall back for that particular route like so:
endpoints.MapFallbackToPage("/MyPage/{Param1}/{Param2}", "/_Host");
For WASM projects: you want to specify the html file vs _Host which should be Blazor Server specific.
endpoints.MapFallbackToFile("/MyPage/{Param1}/{Param2}", "index.html");
After this setup is applied, refreshing a page or reaching the URL directly should result in your application working as intended, no more 404 error different from the standard <NotFound> tag setup in the App.razor.
Key points:
Check if your url parameters have any known parsing exceptions like dots (Blazor assumes those are files being requested)
Use the default page of your application for the fall back, not the page you want to hit. In a Server app that's _Host for wasm it's index.html

Can you link to a Razor component without hard-coding its URL?

I'm trying to create a github pages website with the new Blazor WASM. I use C# almost every day for work, but I've never used Blazor/Razor/ASP.NET before, so it's very possible that I'm trying to use server-side techniques that won't work here.
I have a group of pages, and I want to show a little preview of each page with a link to that page on the homepage. All of these pages implement the same component base class. For example, Sample.razor might look like
#page "/sample"
#inherits GroupBase
<!-- html here -->
#code {
public override string Name { get; } = "Sample";
}
Index.razor might look like
#page "/"
#foreach (GroupBase p in mPagesToPreview)
{
#p.Name
}
#code {
List<GroupBase> mPagesToPreview = new List<GroupBase> { new Sample() };
}
Is there any way to route to the Sample page without explicitly putting in "/sample"? Or even using a normal link (I've checked both a and NavLink) with a function similar to nameOf() or getType()?
My best option right now is to add a URL property to the base, but it seems like I shouldn't have to type out "/sample" twice. (It's not hard; it's the principle of the thing!)
I've also seen some examples of people injecting a NavigationManager and then using that to deduce the URL, but that would also have to be repeated for each page. I've seen some examples that get the page directive from RouteData.Values, but those were Razor pages. I couldn't figure out how to create a RouteData object or use it as a static class.
You can use class RouteAttribute to find the route.
It looks like this:
[Microsoft.AspNetCore.Components.RouteAttribute("/counter")]
public partial class Counter : Microsoft.AspNetCore.Components.ComponentBase
See How do I read an attribute on a class at runtime?

ASP.NET MVC - How do I Call a Controller Method from within a View to Redirect to Multiple Other Views?

I am trying to add a single link in my navbar that redirects the user to a different webpage depending on their account type. I am having issues doing this and could use some help.
The Controller code that I am calling looks like this:
public IActionResult Attendance(char accountType)
{
if (accountType.Equals("m") || accountType.Equals("a"))
{
return RedirectToAction("FacultyAttendance");
}
else
{
return RedirectToAction("StudentAttendance");
}
}
public IActionResult StudentAttendance()
{
// More functionality will be added later
return View();
}
public IActionResult FacultyAttendance()
{
// More functionality will be added later
return View();
}
Following this answer for calling the Controller method, I have this code snippet in the View file:
Attendance
This gives me the following error:
Bad Request - Invalid URL
HTTP Error 400. The request URL is invalid.
I also tried following this answer by removing the <%: and %>.
Attendance
If I do this, I just get blank webpage.
My first problem lies in which style I should use for this method call within the View file. Are either of these correct, or should I use something else entirely? Might the issue be with the way I have the Controller code set up?
Any help would be appreciated, as I am new to the MVC framework for ASP.NET.
Edit: The solution I found is a bit different than what I originally posted. I used this tag in my View and got it to work:
<a asp-controller="Home" asp-action="Attendance" asp-route-accountType='s'>Attendance</a>
I also followed ThisGuy's suggestions for improving the code since I had mismanaged some variables and that may have been part of the problem.
accountType is a char, but you are passing a string:
new {accountType = "m"}
Change the Controller to take a string instead of char for accountType.
public IActionResult Attendance(string accountType)
Also, I'd write it like this:
public IActionResult Attendance(string accountType) =>
RedirectToAction(
accountType.Equals("m") ||
accountType.Equals("a")
? nameof(FacultyAttendance)
: nameof(StudentAttendance));

Razor Pages Custom Page Routing

This question has been asked earlier and as I am new to Razor Pages, unable to figure out how I can make the required (Silly one). So here it's: I've a razor page where I show a list of data where I've to make the page routing or url as follows:
#foreach (var item in Model.Data)
{
#item.Name #item.City #item.State
}
So pretty simple, so in the Startup.cs, I tried to do the following to make it work. Unfortunately failed:
//This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc();
services.AddEntityFrameworkSqlite().AddDbContext<MyDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc().AddRazorPagesOptions(options =>
{
//Here is the configured routing that I tried
options.Conventions.AddPageRoute("/search_details", "/search_details/id");
});
}
I would like to return the url as ./search_details/id/1002 and in the back-end, did this to get the query string value:
string id = this.RouteData.Values["id"].ToString();
This is pretty basic, but as I said failed (Failed means in the back-end, the query string doesn't hit while debugging). I've one more scenario and this thing is bothering me right now the most. My requirement is to hide the page name as well. Something like this:
localhost:8080/search_details/id/1002
To
localhost:8080/1002
Is there appropriate way where I can accomplish that? I understand that's a bad practice for other purposes but for the time being, I would go with it.
N.B: I am not willing to do it in the client-end unless there is no alternate. If possible, better with something server-side using C#.
You can use Route Templates in Razor pages :
https://www.learnrazorpages.com/razor-pages/routing
Comment out this line options.Conventions.AddPageRoute , and in your search_details page add nullable parameter :
#page "{id?}"
In cs file , you can get route data like :
public void OnGet(string id)
{
}
For the second requirement, you can add template like :
options.Conventions.AddPageRoute("/search_details", "{id}");
So that localhost:8080/1002 will redirect to search_details page and you will get id as route data also .

ASP.NET MVC: Generate URLs without specifying the actionName

This is how I generate a URL in ASP.NET MVC currently:
Url.Action("Index", new { page = 2 })
In previous frameworks I have used, there were special url functions which created a url based on the current url, only modifying the parts you wanted to change. This is in Pylons:
{{ url.current(page=2) }}
This would come in handy with partial views, where the partial view may be showing a list of items but not necessarily know which controller they belong to.
Two questions - why is such an obviously useful feature missing from ASP.NET MVC, and is there some common alternative way of doing what I mentioned with partial views? Maybe I'm approaching partial views completely wrong?
why is such an obviously useful feature missing from ASP.NET MVC
What makes you think that such feature is missing:
string url = Url.Action(null, new { page = 2 });
or:
string url = Url.RouteUrl(new { page = 2 });

Categories