Endless looping solution for action trouble - c#

The goal
Stop an infinite loop generated by actions.
The problem
I have a store application. The products are on categories and they can be marked as offer. There is a left-side menu to list the categories of my application (like Clothing, Shoes, Caps, etc.). One item of this menu is Offers, but Offers isn't a category, it is just a flag. Any item can be in the offerings "category", but a shoe can not be in caps.
If I access myapp.com/Category/Shoes/, only shoes will be displayed to me. If I access myapp.com/Category/Daily-Offers/, today's deal will be displayed to me — and offers can be shoes, caps, clothes.
As you can see, in the both cases (shoes or daily offers) the user will come across with Category route, but the controllers are different. Look at my App_Start > RouteConfig.cs:
routes.MapRoute(
name: "Categories",
url: "Category/{categoryName}",
defaults: new { controller = "Products",
action = "Category",
categoryName = UrlParameter.Optional
}
);
Now, my ProductsController:
[HttpGet]
public ActionResult Category(string categoryName = null)
{
int? categoryId =
(categoryName != "Daily-Offers") ?
Convert.ToInt32(Regex.Match(categoryName, #"\d+").Value) :
(int?)null;
if (categoryName == "Daily-Offers")
{
return RedirectToAction("DailyOffers", "Category");
}
else if (Regex.Match(categoryName, #"\d+").Success && String.Format("{0}-{1}",
categoryId,
CommodityHelpers.UppercaseFirst
(CommodityHelpers.GenerateSlug
(Categories.GetDetails((sbyte)categoryId).Category_Name)))
== categoryName)
{
[...]
return View(Products.BuildListForHome(categoryId, null));
}
else
{
return View("404");
}
}
As you can see, there is an "if" that points "Daily-Offers" for the correct controller.
The DailyOffers method, from ProductsController:
[HttpGet]
public ActionResult DailyOffers()
{
var productsList = Products.BuildOffersList();
ViewBag.Title = String.Format("Today's deal ({0})", DateTime.Now);
ViewBag.CategoryProductsQuantity = productsList.Count;
ViewBag.CurrentCategory = "Daily-Offers";
return View(productsList);
}
Finally, when I access myapp.com/Category/Daily-Offers, the response is an endless looping. Yes, the reason is logical, but what is the way to prevent this? Does someone has any idea of how to solve it?
What I have already tried
I already decorate the DailyOffers' method with:
[ActionName("Daily-Offers")]
But no success — endless looping again.
To emphasize
Daily offers isn't a category — it is a flag. The application treats Offers like a category, but on database there is a column to the products called onSale that tells you whether an item is an offer or not.

Your route table only knows about the /Category/{categoryName} so /Category/Daily-Offers is going to hit your Category action.
You can add a new mapping for the daily offers action above the existing category mapping:
routes.MapRoute(
name: "DailyOffers",
url: "Category/Daily-Offers",
defaults: new { controller = "Products",
action = "DailyOffers"
}
);
routes.MapRoute(
name: "Categories",
url: "Category/{categoryName}",
defaults: new { controller = "Products",
action = "Category",
categoryName = UrlParameter.Optional
}
);

Related

Generate custom URL - showing description in URL

I have a list of products with some title field, e.g.:
ID Title
1 TShirts CK
2 Brand new books
3 Selling whatever I have useless
Ok, I'm calling a Detail method this way:
<a href='#Url.Action("Detail", "Products", new { productId = 3 })'>See detail</a>
[Route("detail/{productId:int}")]
public ViewResult Detail(int productId) {
//...
return View();
}
The generated URL is:
http:example.com/products/detail/3
Well, the idea I want is show an URL like this:
http://example.com/products/detail/3/selling-wathever-i-have-useless
Is there a nice and clean way to do this, based on the given scenario?
I believe this has been termed a URL slug, which will make it 100x easier to search for. I suggest you start: https://stackoverflow.com/a/2921135/507025 for an algorithm to help you slugify your url.
If you have a database with the information, you might want to save the slugified description to it so you can check for duplicates upon the creation of a new item.
Routing will be very similar although you'll be needing to change your routing attribute to:
[Route("detail/{productName:string")]
public ViewResult Detail(string productName)
{
return View();
}
You can then search your DB for the slugified description to return the item searched for OR use the terms given in a search to return multiple results.
There are probably different ways of doing this but now that you know it's called 'slug' you will have an easier time finding info about them.
You can have a route as below and when you define Url.Action you can pass product title to some kind of method which convert the given text to URL friendly text.
Example
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{productId}/{title}",
defaults: new { controller = "Home", action = "Index", productId = UrlParameter.Optional, title = UrlParameter.Optional }
);
<a href='#Url.Action("Index", "Home", new { productId = product.ID, title = ToFriendlyUrl(product.Title) })'>See detail</a>
public ViewResult Detail(int productId, string title)
{
// ...
return View();
}
Thanks!

MV4 Routing Dynamic Routing from MySQL Database

I have the following sample data from a MySQL table.
My question is in my MVC 4 project how can I get routing to work so that if a user goes to the URL www.mydomain.com/products/apples/ I want the call to actually be the view of www.mydomain.com/products/index/1, how do I achieve this ?
You could create this one route:
routes.MapRoute("Product", "products/{productName}",
new {controller = "products", action="index"});
This route is saying:
When a request comes in with a URL matching the pattern: "products/{productName}" (e.g. http://www.example.com/products/apple), pass that over to the "index" action in the "ProductsController" to handle and pass the segment of the url indicated by the {productName} placeholder, in to that action as the parameter called "productName".
Then your action would be:
[HttpGet]
public ActionResult Index(string productName) {
// Lookup product from DB
// do stuff
var viewModel = ...;
return View(viewModel);
}
So, when a request comes in for products/apple, "apple" gets passed into the Index action as the productName parameter.
Put below code in you RouteConfig.cs before the default route expression.
foreach (var item in _context.products)
{
routes.MapRoute(
item.Url,
"products/" + item.Url,
new { controller = "products", action = "index", #id = item.ProductId }
);
}
In above _context.products is used to retrieve the products from the DB. You can modify this according your models.

ASP.NET MVC C# routes.MapRoute not working

I am altering an ASP.NET, MVC, C# application but a routes.MapRoute entry isn't working as expected. In my Global.asax.cs file I have the following two routes -
routes.MapRoute(
"MyRoute1", // Route name
"{controller}/{action}/{something}/{name}/{id}/{myParameterA}",
new { controller = "MyController", action = "MyActionA", category = "something", name = "name", id = "id", myParameterA = "myParameterA" });
routes.MapRoute(
"MyRoute2", // Route name
"{controller}/{action}/{something}/{name}/{id}/{myParameterB}",
new { controller = "MyController", action = "MyActionB", category = "something", name = "name", id = "id", myParameterB = UrlParameter.Optional } );
The code in my controller looks like this -
public ActionResult MyActionA(string something, string name, string id, string myParameterA)
{
//do cool stuff!
}
public ActionResult MyActionB(string something, string name, string id, string myParameterB)
{
//do awesome stuff!
}
When I call MyActionB, the final parameter myParameterB is coming into the Controller as null even when the parameter is in the URL - (example: /MyController/MyActionB/aThing/aName/123/456).
I do want the final parameter ('456' in my above example) to be optional.
MyActionA is working fine.
Any suggestions would be appreciated!
Also, is there a good reference out there on how routes.MapRoute works?
Thank you!
This is because there is nothing to distinguish between those 2 routes once you replace the parameters with strings in the route itself. If you add a static part to the routes you should be able to differentiate between them.
routes.MapRoute(
"MyRoute1", // Route name
"{controller}/{action}/{something}/{name}/{id}/firstroute/{myParameterA}",
new { controller = "MyController", action = "MyActionA", category = "something", name = "name", id = "id", myParameterA = "myParameterA" });
routes.MapRoute(
"MyRoute2", // Route name
"{controller}/{action}/{something}/{name}/{id}/secondroute/{myParameterB}",
new { controller = "MyController", action = "MyActionB", category = "something", name = "name", id = "id", myParameterB = UrlParameter.Optional } );
See if that works.
Not sure but I think swap the two around, when you set "myParameterA = "myParameterA"" on the first, you're assigning a default value, when you pass /MyController/MyActionB/aThing/aName/123/456 the url is mapped to the first, but the number 456 is not compatible with the default string value - and so is passed as null.
EDIT: oh and for a good reference, the Apress Pro MVC 3 has an excellent chapter on this - Safari Informit.

Where do the parameters come from for action that gets invoked by Html.RenderAction?

I tried to use the debugger but I can't seem to get anywhere. I can't step into Html.RenderAction(), it's in my master page.
I read that it gets the value "automatically" through routing. How does that work?
// "Nav" is the name of the controller housing the "Menu" action
// This is called in Site.Master
<% Html.RenderAction("Menu", "Nav"); %>
// where does "category" come from?
public ViewResult Menu(string category)
{
}
I made this according to a book, but I can't find an explanation in there. The category supposedly comes from the URL automatically into the parameter.
On a related note: Do you advise to download the source code for MVC to work properly, or would that complicate my efforts more than it would help?
The Category parameter is being picked up from the following routing entry
routes.MapRoute(null, "{category}", // Matches ~/Football or ~/AnythingWithNoSlash
new { controller = "Products", action = "List", page = 1 }
);
so if /Football is entered then it is supplied as a parameter to the ViewResult Menu in the
which in turn calls
public ViewResult List(string category, int page = 1)
{
var productsToShow = (category == null)
? productsRepository.Products
: productsRepository.Products.Where(x => x.Category == category);
var viewModel = new ProductsListViewModel {
Products = productsToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
PagingInfo = new PagingInfo {
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = productsToShow.Count()
},
CurrentCategory = category
};
return View(viewModel); // Passed to view as ViewData.Model (or simply Model)
}
so later within the view master when the render action is called
<% Html.RenderAction("Menu", "Nav"); %>
it can pick up on the category parameter in the route i.e. {category}
public ViewResult Menu(string category)
{
// Just so we don't have to write this code twice
Func<string, NavLink> makeLink = categoryName => new NavLink
{
Text = categoryName ?? "Home",
RouteValues = new RouteValueDictionary(new {
controller = "Products", action = "List",
category = categoryName, page = 1
}),
IsSelected = (categoryName == category)
};
// Put a Home link at the top
List<NavLink> navLinks = new List<NavLink>();
navLinks.Add(makeLink(null));
// Add a link for each distinct category
var categories = productsRepository.Products.Select(x => x.Category);
foreach (string categoryName in categories.Distinct().OrderBy(x => x))
navLinks.Add(makeLink(categoryName));
return View(navLinks);
}
}
The Html.RenderAction call renders the action with method name "Menu", in the controller "Nav".
The routing file contains patterns for how to populate method parameters (and resolve overloaded actions). The routing file is usually found in ~/Global.asax, in the RegisterRoutes method. This file should contain multiple calls to RouteCollection#MapRoute, which maps a certain URL pattern to a new object with particular variables.
Your routes must include a single string mapping somewhere, which captures anything without a slash into a variable called category. That is then passed to the Menu action method.
Addendum: Try looking here for more information.

C#, ASP.NET MVC / MVC2: Add wildcard route for specific controller - help building route

lets say I have a controller named Store. There is an index method:
public ActionResult Index(string val, long? id, int? id2)
{
return View();
}
I want to fetch things like:
/my-store/some-text
/my-store/some-text/
/my-store/some-text/123456789012335
/my-store/some-other-text/4562343462345
/my-store/some-other-bla-text/4562343462345/1
/my-store/some-other-bla-bla-text/45345723554/2
So the requerements are:
+ the controller link-text is "my-store" and has to route to Store controller
+ if there is only some text and id, then just leave id2 null
+ if there is only some text and no id, then just leave id and id2 null
Is the following correct?
routes.MapRoute(
"Store", // Route name
"my-store/{val}/{id}/{id2}", // URL with parameters
new { controller = "Store", action = "Index", id = UrlParameter.Optional, id2 = UrlParameter.Optional }
);
Thank you for your help in advance!
Yeah looks good except you need to define a default value for {val} in your route:
routes.MapRoute(
"Store", // Route name
"my-store/{val}/{id}/{id2}", // URL with parameters
new { controller = "Store", action = "Index", val = "", id = UrlParameter.Optional, id2 = UrlParameter.Optional }
Just remember to cleanse the string in val as if you have some funky characters then your link won't work. Like let's say val equal "thisis/val", then your link will be:
/my-store/thisis/val/1212/12
Which would not hit your route and most likely return a 404

Categories

Resources