Routing and Controller Actions - Optional Data - c#

I created a route in ASP.net MVC application that looks like this:
routes.MapRoute(
"ArticleRoute",
"{year}/{month}/{day}/{articleId}/{displayName}",
new { controller = "Article", action = "Details" }
);
I want a route that will be like a blog post article.
Example: http://www.foobar.com/2010/01/01/123456789/the-name-of-the-article
In my Details controller action I want to do a permanent redirect if the year, month, date and display name are not correct. I want to know the best way to write the Details() controller method.
The only field that is truly required is the articleId. If I have the article Id then the database will have what the article date and name are.
I really want to know how the controller method should look like. Do I pass in all of the values to the method or use RouteData to get them?
public ActionResult Details(int articleId, string displayName)
{
var article = */SNIP*/;
int articleYear = RouteData.Values["year"];
// etc.
DateTime articleDate = new DateTime(articleYear, articleMonth, articleDay);
string realDisplayName = article.Name.ToSeoString();
if( displayName != realDisplayName || article.Date != articleDate)
// permanent redirect to the actual article
return View();
}
OR
public ActionResult(int year, int month, int day, int articleId, string displayName)
{
var article = /*SNIP*/;
DateTime articleDate = new DateTime(year, month, day);
string realDisplayName = article.Name.ToSeoString();
if( displayName != realDisplayName || article.Date != articleDate )
// permanent redirect to the actual article
return View();
}
I think both technically work, but which is the better way?
Also: if there are any other flaws in what I am doing feel free to point them out.

I prefer RESTFul url s to query strings because they are clear, can be easily read by users and seo friendly.
In your case if you really want the url as .../2010/01/01/123456789/the-name-of-the-article the second method is the better one.
But I think there's no need to use the data/month if you are using the article id in the url.
Applications like wordpress use the /year/month/title format because if you just give the title of the article only it takes more time to query the database. But with the year and month you can speed up the db querying since you can narrow down the results and then do the title search on that result set.
I think you should use a route like .../123456789/the-name-of-the-article
See the url of this question it is Routing and Controller Actions - Optional Data in here the question title is used only for the seo purposes.
If you want the design to be pure restful then the url should be .../articles/the-name-of-the-article
I create urls like this as there are many ways to optimize the db querying (like hashing) and I LOVE pure REST.

In the case of the first example, what's the point of passing anything in if you are going to use RouteData.Values to get some values?
Also, is the var article = ... a placeholder for now until you hook up the database lookup?
From the client side, it wouldn't matter, because the URL wouldn't be any different.
On the server side, the second example looks cleaner in the code, because you don't have the lookup of RouteData.Values.

Query strings are much better than REST URLs. Routes are from early days of HTTP but was passed over for query strings for many web sites because parameters depend on route construction. Also, routes are not good for encrypting parameters when you need to.

Passing everything in is better because your parameters are strongly typed and the RouteData.Values collection is not. You are also able to cast the RouteData.Values items but that is extra code you don't have to do. This is another example of ASP.NET MVC doing code for you so you have less control if you want to do something different. Web Forms are more transparent for what the framework is actually doing, while MVC is smoke and mirrors.

Related

How do I show a loading screen/icon while loading a view that requires querying a database? Asp.Net Core C#

Currently I have a view that has just a single form with three values. When a user submits the form with those three values, my controller checks if all three values actually have a value other than being empty, and if they do then it calls my service that fetches data.
public IActionResult Index(string clientName = "", string tableName = "", string date = "")
{
if (!string.IsNullOrEmpty(clientName) && !string.IsNullOrEmpty(tableName) && !string.IsNullOrEmpty(date))
{
// Unimportant stuff for setting temp variables for FetchData parameters removed
TheData = _fieldDataService.FetchData(tempAcr, tempID, tableName, date, numRows);
}
return View(TheData);
}
My goal is to make the view display a loading icon or something while the data is being fetched. I've looked into JQuery and Ajax but I have no experience with either and cannot figure out how to apply them to my specific task. Since I am using Asp.Net Core, I also looked into using Blazor, but I couldn't wrap my head around it either.
Could somebody point me in the right direction as to what I should/could use to solve this problem? I have a vague understanding that there needs to be some sort of asynchronous function that retrieves the data, but nothing more than that.
You need to fetch the data with JavaScript and manipulate the UI to show a loader. But anyway, a request like this should be so fast, that you don't even need a loader.
I'm also a bit worried that you are passing a tableName as input parameter. You aren't just string concatenating the query right? You might be susceptible to SQL injections, see https://en.wikipedia.org/wiki/SQL_injection.
To do a request with JavaScript, look into the XMLHttpRequest, see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest, or the new way of doing it with fetch(...), see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch.
This answer shows a more practical example: Display loading image while ajax fetches file content.

How does FromQueryAttribute actually work and why do I need it?

I'm working with some pre-existing code that uses an HttpGet endpoint to get all comments. It takes some default parameters and before each parameter it is decorated with a [FromQuery] attribute. I'm a little confused about why we need this here and what it really does for me.
There isn't much online all I found was this documentation:
(https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.fromqueryattribute?view=aspnetcore-2.2)
This doesn't answer my question though...
What is a query string?
How is it used in endpoints?
When is it that I don't need to specify this attribute? I don't see it being used very much.
[HttpGet]
[ProducesResponseType(typeof(FooResponse), 200),
public async Task<ActionResult<FooResponse>> GetAll([FromQuery]string sortColumn = "CommentId",
[FromQuery]SortDirections sortDirection = SortDirections.Asc,
[FromQuery]string filter = "",
[FromQuery]int page = 1,
[FromQuery]int pageSize = 10)
{
var data = await _mediator.Send(new GetAllComments(sortColumn, sortDirection, filter, page, pageSize));
.
.
.
return Ok(data);
}
I suspect that it has something to do with what is passed into the URL but I'm not quite sure...
The usage here is superfluous. The action would function the same without [FromQuery] being applied to all the params, as it only responds to GET requests.
What is a query string?
The query string is the portion of the URI after the ? character is applied. It constitutes a data portion of a URI versus a pure routing portion.
How is it used in endpoints?
The question here is a little tenuous, so it would be more appropriate to ask "How is it used in a request?" The answer to that it is a way to pass data along with a GET request to a particular URI. Unlike other HTTP methods, GET does not really allow a request "body". (Actually, the spec technically does allow this, but it's almost universally not implemented that way.)
When is it that I don't need to specify this attribute? I don't see it being used very much.
Again, a better question is "When do I actually need to specify this attribute?" The answer to that is basically when it's ambiguous where the data is coming from. This will almost invariably be with other HTTP methods such as POST. By default, data is expected to come from the request body there, and depending on the content type of that request body, the binding is expected to be either FromForm or FromBody, which will usually be the default for params there. (Which is the default depends on whether you're dealing with a traditional MVC-style controller or an API-style controller.) If you need to actually get a particular param from the query string instead of the body in such a scenario, then you would apply the [FromQuery] attribute to that particular param.
Since the action in your post is for GET requests only ([HttpGet]) the [FromQuery] isn't necessary - the parameters will automatically use the associated query string values.
For POST actions ([HttpPost]) this attribute specifies to grab the value from the query string as opposed to the data which was posted.
Query string is a part of the URL that comes after an ? and it provides a way to pass values as a list of key-value pairs, each pair separated by & characters. Say you have the following URL:
http://mywebsite.com/somePage?a=123&b=Hello&c=World
So the query string part int that URL corresponds to a=123&b=Hello&c=World, and the list of key-value pairs that were passed was:
a = 123
b = Hello
c = World
ASP.NET Core can bind values to variables in a lot of different ways, and one of them is by reading values from a query string and converting/assigning these values to the values of your API's methods. To specify that a parameter should come from the query string, you use the [FromQuery] attribute.
So - for instance - when you have [FromQuery]string sortColumn = "CommentId" you are basically telling it to read the sortColumn key from the query string and assign it to the sortColumn parameter of the GetAll() method, and if that "sortColumn" key is not present in the query string, it should be assigned the default "CommentId" value. It is similar to the other parameters.
So you could access that URL with something like that (you should use the correct URL to get to your GetAll method, which I can't tell with the code you have provided):
http://your-system-url-here/GetAll?page=2&pageSize=20
Which would assing the page parameter a value of 2 and the pageSize parameter a size of 20.

Using a slug in URL & ID to query DB in MVC

I am trying to use slugs in an MVC web application but can seem to work out the best way to implement them.
I have found the the recommendation on how to create the URL friendly slug stackoverflow slug post
I still want to be able to query the Db with the ID but don't want this to be in the URL similarly to most stackoverflow URLs, for example
http://website/home/list/outdoor-products
How can a slug be displayed in the URL while still passing and using an ID to be used to query with?
It's doesn't really depends on a technology/framework which you are using, the main thing is you have to have destinctive urls to unambiguously select page content.
If you do have unique titles/slugs for pages, then you may use them as identity for content selection. Otherwise, you need to put some sort of id (it could be int or guid, whatever) into your urls. There isn't anything which will hide your int id behind the slug.
Talking about stakoverflow's urls, you'll find id just before the friendly title. Another option could be put actual id at the end of friendly title (friendly-title-1559063).

Model in QueryString?

So, I'm facing a problem here. I'm building an ASP.NET MVC application, and the final stage is to implement a search page feature that uses multiple filters to perform the search.
I've done a model class called SearchModel that has some properties, among em' is a collection of search filters. The problem is at the search results page. I need to preserve the whole SearchModel as search criteria and additional paging parameters. I'd like to add links to next and previous pages and stuff like that, only available by a GET request. If I enable GET I get a HUGE query string containing the whole model as follows:
Documents/Search?CriteriaFilters[0].Field=OwnerUserName&CriteriaFilters[0].Type=Text&CriteriaFilters[0].Text=albert.sheppard&CriteriaFilters[0].TextMatchMode=Exactly
And that's only using one filter with a small criteria text. Looks bad and nasty when I'm using 10+ filters.
How you pro's handle search and results pages with multiple filters without parsing the whole model to the query string? Query string encryption? If so, how to achieve this?
Thanks.
I don't know about ASP.NET MVC, but it should be done with POST. I understand many of your fields are unused in your example, but they might as well have values, and the size of GET is very limited. Or maybe you can come up with a very compressed way of representing your criteria. But still, POST is a lot better.
I have implemented search criteria URL using MVC as follows:
acer notebook >> Notebook >> 15.6in >> Core 2 Duo
http://myserver/SearchApp/Search/4/acer%20notebook/1/50-97-77868%2c50-111-76631?sort=0&catGrp=50
private
ActionResult SearchAction
(
int siteId,
string query,
int? page,
string options,
SortType sortType,
int catGroupId
)
{
// Search code implementation
}
The above URL and controller translation is as follows:
Search - Action
4 - siteId
acer%20notebook - query
50-97-77868%2c50-111-76631 - options (it is a comma separated list of options), which represents 15.6in >> Core 2 Duo
0 - sortType
50 - catGroup
Hope this will help.

ASP.Net MVC - route object id == title - how to deal with duplicates?

Web pages have moved to use URLs like:
//weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
i.e. they include the title of the page in the url rather than having some coded id.
I understand that this is useful for SEO, and also for users in finding the correct page where they wish to type in the url.
I would like to follow this approach, but wonder how best to acheive it, and particularly how to deal with duplicates.
Is a database trigger which creates the url based on the title and adds a numeric incremental suffix to any duplicates the best way to go, and if so what would such a trigger look like?
Instead of having an id based on a title they could use id based on both a date and a title (2007/12/03/asp-net-mvc-framework-part-2-url-routing). So if you don't have articles with the same titles in one day (which isn't too severe restriction) duplicates are eliminated.
In Wordpress at least, the "slug" (as they call it) is generated once from the item's title and stored separately in the database. If two "slugs" collide, it appends -1, -2, etc. to the end. I personally prefer if you add an (optional) field to the submission form to allow people to insert their own—it allows people to specify a shorter URL than my-long-article-is-hard-to-type.
You've got to model this concept in your application. URL generation based on title can be automatic, but it can't be invisible. WordPress (and probably other CMS's, too) do a pretty good job of this -- they'll default a URL based on the information you enter, but the "key" part of the URL is visible and editable to the user, and uniqueness is enforced at the appropriate level (globally, per month, per day -- whatever).
Making URL generation completely invisible will lead to confusing errors for the user, I believe.
You could do the same thing that SO does. That is, the slug is only there as GoogleJuice. These two URLs resolve to the same thing:
ASP.Net MVC - route object id == title - how to deal with duplicates?
ASP.Net MVC - route object id == title - how to deal with duplicates?
So, in the example you gave, if the CMS gave each post a unique numeric identifier (which I suppose is quite likely) then you can include it in the URL:
http://weblogs.asp.net/scottgu/archive/2007/12/03/1234/asp-net-mvc-framework-part-2-url-routing
In this example, the symbol 1234 is the post's identifier.

Categories

Resources