Is there a named routes and default params equivalent for Razor Pages page routes? - asp.net-core

I have a JobPosts/Index page with multiple GET parameter bindings to allow filtering: let's take CityId and IsRemote for example. I don't want these to be passed as query string parameters, instead I want to use friendly routes for them. So I have defined these:
options.Conventions.AddPageRoute("/JobPosts/Index", "cities/{cityId}/jobs");
options.Conventions.AddPageRoute("/JobPosts/Index", "remote-jobs");
options.Conventions.AddPageRoute("/JobPosts/Index", "jobs");
The routes work just fine when I type them in the browser and the CityId one is bound properly, but two things are missing.
First, there is no way to specify a default value for my IsRemote param, which I want to set to true ONLY when using the remote-jobs URL.
And second, when trying to generate a URL like this:
<a asp-area="" asp-page="/JobPosts/Index" asp-route-cityId="#Model.CityId"></a>
I get the following URL:
https://localhost:44391/jobs?cityId=2265885
When what I actually expect is:
https://localhost:44391/cities/2265885/jobs
So it looks like the tag helper or the part responsible for constructing the URL doesn't look at all at the different routes to try and get a best match based on the list of parameters. Actually, it will always use the last page route defined for that page.
Nor do I have the option anywhere to specify a route name for the page route and then use asp-route to explicitly say which route I want.
Any ideas how to achieve that? Or if it's something that's on the roadmap for Razor Pages?
EDIT: Hardcoding the href is not an option. I want this to go through the proper routing services as there are other things to be done as well, like generating culture-specific URL for non-english users (eg. {cultureId}/cities/{cityId}/jobs - this is done through route conventions. Hardcoding the href would obviously bypass that.

There is a easy way to set IsRemote default value.
public bool IsRemote { get; set; } = true;
This tag asp-page will link to Page /JobPosts/Index.csthml directly,
https://localhost:44391/JobPosts?cityId=2265885
= https://localhost:44391/JobPosts/Index?cityId=2265885
If you are looking forward the URL https://localhost:44391/jobs?cityId=2265885
you could try this a tag to request.
Go to JobPosts
———————————————————————————————
Using a middleware to handle /remote-jobs
app.Run(next => async context =>
{
if (context.Request.Path == "/remote-jobs")
{
return View with default IsRemote
}
});

Related

Dot.NET Core Routing and Controller Processing discrepancy

I am trying to use the asp-route- to build a query string that I am using to query the database.
I am confused on how the asp-route- works because I do not know how to specifically use the route that I created in my cshtml page. For example:
If I use the old school href parameter approach, I can then, inside my controller, use the specified Query to get the parameter and query my database, like this:
If I use this href:
#ulsin.custName
then, in the controller, I can use this:
HttpContext.Request.Query["ID"]
The above approach works and I am able to work with the parameter. However, if I use the htmlHelper approach, such as:
<a asp-controller="Report" asp-action="Client" asp-route-id="#ulsin.ID">#ulsin.custName</a>
How do I get that ID from the Controller? The href approach does not seem to work in this case.
According to the Anchor Tag Helper documentation, if the requested route parameter (id in your case) is not found in the route, it is added as a query parameter. Therefore, the following final link will be generated: /Report/Client?id=something. Notice that the query parameter is lowercase.
When you now try to access it in the controller as HttpContext.Request.Query["ID"], since HttpContext.Request.Query is a collection, indexing it would be case-sensitive, and so "ID" will not get you the query parameter "id". Instead of trying to resolve this manually, you can use a feature of the framework known as model binding, which will allow you to automatically and case-insensitively get the value of a query parameter.
Here is an example controller action that uses model binding to get the value of the query parameter id:
// When you add the id parameter, the framework's model binding feature will automatically populate it with the value of the query parameter 'id'.
// You can then use this parameter inside the method.
public IActionResult Client(int id)

Friendly URL for RazorPage with query string

I would like to create friendly routes that map to different query string values of a single Razor Page and it's base GET handler. I'd be fine with switching the query string to being a part of the route as well. The idea is we have a single page that loads a file list and can filter. I'd prefer it if the URL was more straight forward. If it's not possible I could just go with /Files/In or /Files/Out
Example
/Files?direction=In -> /In
/Files?direction=Out -> /Out
Is there any way to do something like this?
You can use route data parameters which are defined in a Route Template as part of the #page directive in the .cshtml file :
#page "{direction?}"
Then you can access the route parameter value in OnGet method of your file page :
public void OnGet(string direction)
{
if (!string.IsNullOrEmpty(direction))
{
//filter files based on direction
}
}
You can pass direction value as /Files/In or /Files/Out , without filter directly use /Files .

ASP.NET Web API 2 - define controller-to-method mappings?

Microsoft has a good tutorial for getting your first ASP.NET Web API project going:
http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api
...but what's unclear to me -- how is the mapping between the API URIs and my C# controller methods defined?
for ex:
/api/products/{id}
resolves to
public IHttpActionResult GetProduct(int id)
{
[...]
}
...but im not sure how. automagic?
I ask because I want to create a new mapping for this URI:
/api/setReportNotificationsAsRead?uid={username}&items={itemIDs}
to this new method in my controller:
public IHttpActionResult SetReportNotificationsAsRead(string username, string itemIDs)
{
[...]
}
...but im not sure how. automagic?
The Automagic lies with HTTP Verbs in Web API. The route /api/products/{id} can map to anything that starts with GET (or just method name Get) if you make a GET request for the WebAPI.
If you make a POST request then it will map to any POST method (or else is you use route prefix).
This link can help you understand the magic. That can also help you understanding how you can configure SetReportNotificationsAsRead function to be called.
You're very close to having a working solution. You have your method parameter names mixed up:
public IHttpActionResult SetReportNotificationsAsRead(string uid, string items)
{
//Do your stuff!
}
The name of the HTTP GET parameter should match the name of the method parameter. This is part of the ASP.NET MVC naming convention.
One final note, if you are actually expecting a group of items (i.e. multiple item ids) you can actually save yourself some work and use a string[] parameter so long as your HTTP parameters are sent correctly. In this case you would want the URI to look like: /api/setReportNotificationsAsRead?items=123&items=456&items=789. Notice how the parameter name is repeated multiple times, but with different values.
For more information about the routing system I would suggest heading over to the ASP.NET MVC routing overview.

How can I get jade/express to output the full path in anchor links?

I would like my links to be full absolute paths to the resource being linked without me having to hard code the scheme and URI to the resource. I wish to do this in relation to using itemprop='url' on links to have the full url appear in the href attribute.
There are several approaches. The first one I would suggest is a basic helper function passed to jade during the rendering. Perhaps bound to the current request:
In your express code:
function absolute(req, url) {
//you'll need more logic here, but this is the concept
return req.originalUrl + url;
}
res.render("somePage", {absolute: absolute.bind(null, req)});
In your jade markup:
a(itemprop=url, href=absolute(url))
//- This is assuming "url" is a variable with a string like "/cars/42"

Passing variables into JavaScript in ExpressJS/PassportJS/Jade app

This is essentially a continuation of the question here: Nodejs Passport display username.
app.get('/hello', function(req, res) {
res.render('index.jade', { name: req.user.username });
});
So users log in via PassportJS, and goes to index.jade, which contains #{name} in the body, which will be replaced by the value of req.user.username.
Question: Is it possible to use the value of req.user.username in index.jade's JavaScript? I tried assigning its value to a variable but it doesn't work.
I have been using the trick of having a hidden input with #{name} as value:
input(type='hidden', id='variableName', value='#{name}')
Then JavaScript can access this value using:
$("#variableName").val()
This works. But does it have any potential downside like security issues? What is the right way to do this?
You have a few options. One of them is what you did and put the value inside you html. You can also solve it by doing:
script
window.name = #{name};
This will create an inline script that sets the variable. The other option you have is using ajax. That means you probably need to make an extra route to reply to that request.