Url with email address in ASP.NET MVC4 - asp.net-mvc-4

I have a situation where I'm using URLs that could possibly contain email addresses. For example:
http://MyUrl.com/Edit/Mike
or
http://MyUrl.com/Edit/bob#whatever.com
According to this StackOverflow question it appears the easy way to get around this is to use a querystring instead. Is there a way I could set this up in the routing rule, where it would determine if value is invalid and have it fall back to a querystring?

How about this:
Route:
routes.MapRoute(
name: "UserEdit",
url: "Edit/{user}",
defaults: new { controller = "User", action = "Edit" },
constraints: new { user = #"[a-z0-9]+" /*Add valid characters here*/ }
);
routes.MapRoute(
name: "UserEditWithIllegalChars",
url: "Edit/",
defaults: new { controller = "User", action = "Edit" }
);
The route will only match URLs like /Edit/Mike and Edit/?user=bob#whatever.com.
Generating the URL also works. For example
#Html.ActionLink("1", "Edit", "User", new { user = "Mike" }, new { })
#Html.ActionLink("2", "Edit", "User", new { user = "bob#whatever.com" }, new { })
Produces the same URLs as above
Controller:
public ActionResult Edit(string user)
Note:
1) you need to check if user is null
2) add valid characters into the constraint because I don't know all of them.

Related

how to render date part segments with anchor taghelper in ASP.NET Core

I'm trying to make a blog with a setting that allows a choice between 2 url formats for posts, one with the date as segments and one with only the slug. I want users to be able to choose either of these settings for their blog and it should not require changes in startup code to switch back and forth. In fact I'm trying to support multi-tenant blogs so each blog can have its own url format preference.
I have the following routes defined in Startup.cs
routes.MapRoute(
name: "blogcategory",
template: "blog/category/{category=''}/{pagenumber=1}"
, defaults: new { controller = "Blog", action = "Category" }
);
routes.MapRoute(
"blogarchive",
"blog/{year}/{month}/{day}",
new { controller = "Blog", action = "Archive", month = "00", day = "00" },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
routes.MapRoute(
"postwithdate",
"blog/{year}/{month}/{day}/{slug}",
new { controller = "Blog", action = "PostWithDate" },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
routes.MapRoute(
name: "blogpost",
template: "blog/{slug}"
, defaults: new { controller = "Blog", action = "Post" }
);
routes.MapRoute(
name: "blogindex",
template: "blog/"
, defaults: new { controller = "Blog", action = "Index" }
);
routes.MapRoute(
name: "pageindex",
template: "{slug=none}"
, defaults: new { controller = "Page", action = "Index" }
);
routes.MapRoute(
name: "def",
template: "{controller}/{action}"
);
routes.MapRoute(
"postwithdate",
"blog/{year}/{month}/{day}/{slug}",
new { controller = "Blog", action = "PostWithDate" },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
My Blog controller has these methods related to the post routes
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Post(string slug, string mode = "")
{
return await Post(0, 0, 0, slug, mode);
}
[HttpGet]
[AllowAnonymous]
[ActionName("PostWithDate")]
public async Task<IActionResult> Post(int year , int month, int day, string slug, string mode = "")
{
...
}
If I manually navigate to
http://localhost:60000/blog/2016/03/07/squirrel
the page works as expected
in my view I'm rendering the link to the post like this with the anchor tag helper
#if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
<a asp-controller="Blog" asp-action="Post"
asp-route-year="#Model.TmpPost.PubDate.Year"
asp-route-month="#Model.TmpPost.PubDate.Month"
asp-route-day="#Model.TmpPost.PubDate.Day"
asp-route-slug="#Model.TmpPost.Slug"
itemprop="url">#Model.TmpPost.Title</a>
}
else
{
<a asp-controller="Blog" asp-action="Post" asp-route-slug="#Model.TmpPost.Slug" itemprop="url">#Model.TmpPost.Title</a>
}
but when I configure it to use the pubDate in the url it renders the link like this:
http://localhost:60000/blog/squirrel?year=2016&month=3&day=7
That url also works but how can I make it render like this:?
http://localhost:60000/blog/2016/03/07/squirrel
I tried also using a named route with the taghelper instead of controller and action, like this:
<a asp-route="postwithdate"
asp-route-year="#Model.TmpPost.PubDate.Year"
asp-route-month="#Model.TmpPost.PubDate.Month"
asp-route-day="#Model.TmpPost.PubDate.Day"
asp-route-slug="#Model.TmpPost.Slug"
itemprop="url">#Model.TmpPost.Title</a>
but that one renders completely wrong without even the slug like this:
http://localhost:60000/blog
I want to make the tagHelper render the url like this:
http://localhost:60000/blog/2016/03/07/squirrel
Can anyone see what I'm doing wrong or not doing right? Or would I need to implement a custom anchor taghelper for this?
ok, I found a solution using the named route, just need to make sure the month and day get formatted as 2 digits, after doing that it now renders as I wanted with the date segments
<a asp-route="postwithdate"
asp-route-year="#Model.TmpPost.PubDate.Year"
asp-route-month="#Model.TmpPost.PubDate.Month.ToString("00")"
asp-route-day="#Model.TmpPost.PubDate.Day.ToString("00")"
asp-route-slug="#Model.TmpPost.Slug"
itemprop="url">#Model.TmpPost.Title</a>

Differentiating Between MVC Routes

I have two similar seo friendly routes going to different controllers/actions:
routes.MapRoute(
"Locations",
"{controller}/{locationId}/{location}",
new { controller = "Locations", action = "Location", location = UrlParameter.Optional },
new { locationId = #"\d+" }
);
routes.MapRoute(
"News",
"{controller}/{newsId}/{newsTitle}",
new { controller = "News", action = "NewsItem", newsTitle = UrlParameter.Optional },
new { newsId = #"\d+" }
);
The News route returns a 404 error and visa versa if I swap them. I tried adding a default controller to each - this did nothing. I then tried replacing {controller} with the actual name of the controller. This generated a
The matched route does not include a 'controller' route value, which
is required
error message.
Here's a link for each:
#Html.ActionLink(x.newsTitle, "NewsItem", "News", new { newsId = x.newsID, newsTitle = x.newsTitle.ToSeoUrl() },null)
<i class="fa fa-map-marker fa-li red"></i>My Place Name
I 'fixed it' by switching locationId and location querystring elements. OK so it works, but I feel this isn't the right solution. What if I have a third controller action with a similar querystring pair?
A final attempt was to make the route more generic:
routes.MapRoute(
"SEO",
"{controller}/{Id}/{Title}",
new { Title = UrlParameter.Optional },
new { Id = #"\d+" }
);
Bu then I can't pass the action and I'd be back to a less SEO friendly URL.
In the end I would like to end up with:
Locations/locationId/my-place-name
News/newsId/my-news-item
I'm relatively new to MVC and I'm sure I'm missing something fundamental (or just being dumb).
The problem is that a URL for both actions will match the first route you have added. For example, this URL:
http://yoursite.com/News/123/some-news-article
Will attempt to call the action Locations in your News controller, which obviously doesn't exist and gives you a 404 error.
You can get around this by specifying the path in the URL of the route instead of using {controller}. For example:
routes.MapRoute(
"Locations",
"Locations/{locationId}/{location}",
new { controller = "Locations", action = "Location", location = UrlParameter.Optional },
new { locationId = #"\d+" }
);
routes.MapRoute(
"News",
"News/{newsId}/{newsTitle}",
new { controller = "News", action = "NewsItem", newsTitle = UrlParameter.Optional },
new { newsId = #"\d+" }
);

How switch Route when using method Url.Action in UrlHelper MVC4

I have following define route ,
routes.MapRoute(
name: "sp",
url: "sp/{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "pm",
url: "pm/{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
when running, url of my project like to "http://example.com:7549/nhj/pm" and "http://example.com:7549/nhj/sp"
But, when using method Url.Action("Edit", "Product"), the Url has been generated by method Url.Action is always "http://example.com:7549/nhj/sp/Product/Edit".
How can I use the method Url.Action("Edit", "Product") to generate two different urls that are "http://example.com:7549/nhj/pm/Product/Edit" and "http://example.com:7549/nhj/sp/Product/Edit" match with 2 routes above.
Anyone can you help me??? Thanks
Use Url.RouteLink() that is using the route name when generating the Url:
// for SP route
Url.RouteUrl("sp", new { action = "Edit", controller = "Product" });
// for PM route
Url.RouteUrl("pm", new { action = "Edit", controller = "Product" });
When you're using the returned Url to redirect from action, use:
// for SP route
return RedirectToRoute("sp", new { action = "Edit", controller = "Product" });
// for PM route
return RedirectToRoute("pm", new { action = "Edit", controller = "Product" });
See MSDN.

MVC 4: Custom Routes

I see a lot of problems with MVC routes and I'm having a similar problem getting a route to match a URL.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//default route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute("Beer", "Beer/{beerid}", new { controller = "Beer", action = "Id", beerid = 0});
routes.MapRoute("Beer", "Beer/{beername}", new { controller = "Beer", action = "Name" });
BeerController Methods
public ActionResult Id(int beerid)
public ActionResult Name(string beername)
If I change the methods to the following,
public ActionResult Id(int? id)
public ActionResult Name(string id)
the default routing works with the following URLs:
http://localhost/Beer/Id/100
http://localhost/Beer/Name/Coors
But what I'm going for is just
http://localhost/Beer/100
http://localhost/Beer/Coors
Any ideas?
So a couple things here.
More specific routes should be placed before more general routes, because the first route that is matched will be used and routes are inspected in the order they are added.
If you plan on not providing the name of the action in your URL then you will need to do something to ensure the correct route is targeted so the correct default value will be used. In your case you could use a route constraint to distinguish between the two. Try changing your beer id route to this:
routes.MapRoute(
name: "Beer",
url: "Beer/{beerid}",
defaults: new { controller = "Beer", action = "Id", beerid = 0},
constraints: new { beerid = #"\d+" }
);
The constraint will ensure that the route only matches two-segment URLs where the second segment is composed of one or more digits. This route as well as your route for beer name should be placed before the default route.
UPDATE
My configuration seems to be yielding the results you want. The entirety of my RegisterRoutes method is as follows:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Id",
url: "Beer/{beerid}",
defaults: new { controller = "Beer", action = "Id", beerid = 0 },
constraints: new { beerid = #"\d+" }
);
routes.MapRoute(
name: "Name",
url: "Beer/{beername}",
defaults: new { controller = "Beer", action = "Name" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

MVC4 Route with Id and Index Action

I'm trying to accomplish the following and receiving the "resource not found" error for #2. Assuming because the routing is not configured correctly.
Desired URLs:
1) domain.com/Customer
2) domain.com/Customer/1 *Does Not Work
3) domain.com/Customer/All
public ActionResult Index(int? id)
{
var viewModel = new CustomerViewModel();
if (!id.HasValue)
id = 1; // ToDo: Current Logged In Customer Id
viewModel.Load(id.Value);
return View(viewModel);
}
public ActionResult All()
{
return View(CustomerModel.All());
}
My Route Config has the default route setup and I've tried adding an additional route to no avail.
routes.MapRoute(
name: "Customer",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Customer", action = "Index", id = UrlParameter.Optional }
);
I've excluded my attempts at setting up a new route since it doesn't work.
Your route would normalize out to domain.com/Customer/Index/1. When you have subsequent parts of the route, you can't eliminate an earlier component just because its value will be the default. In this case, it's looking for an action named "1" which it can't find.
Edit:
If your desired route is domain.com/Customer/ID, then you can add such a route to your route table:
routes.MapRoute(
name: "CustomerAll",
url: "Customer/All",
defaults: new { controller = "Customer", action = "All" }
);
routes.MapRoute(
name: "CustomerByID",
url: "Customer/{id}",
defaults: new { controller = "Customer", action = "Index" }
);
These more-specific routes should come before your default route.
try to add this route before the default one
routes.MapRoute(
name: "Customer",
url: "Customers/{id}",
defaults: new { controller = "Customer", action = "Index", id = UrlParameter.Optional }
);
and note that the url starts with Customers not Customer.
and your desired URL will be : domain.com/Customers/1 instead of domain.com/Customer/1