Routing with areas and different parameters, misunderstanding - asp.net-mvc-3

I have been confused all day, i have a routing in area and it looks like this.
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRouteLowercase(null, "Account/{action}",
new {controller = "Account"},
new {action = #"LogOff|LogOn|Create|Update|Delete|List"},
new[] {"WebUI.Areas.Admin.Controllers"});
context.MapRouteLowercase( //this works
"AdminUpdateCategoryView",
"admin/{controller}/{action}/{cid}",
new {area = "admin", controller = "Main", action = "UpdateCategory", cid = ""},
new {cid = #"\d+"},
new[] {"WebUI.Areas.Admin.Controllers"}
);
context.MapRouteLowercase(//this not works
"AdminCategoryListView",
"admin/Main/{action}/{page}",
new { action = "Category", page = "1" },
new {page = #"\d+"},
new[] {"WebUI.Areas.Admin.Controllers"}
);
context.MapRouteLowercase(
"Admin_Default", // Route name
"admin/{controller}/{action}/{id}", // URL with parameters
new {controller = "Category", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
}
}
I have wrote what works and what not, but if change between them the one that doesn't work, works and the other that works, don't work?
example:
first case-> /admin/main/updatecategory/1 --> works
/admin/main/category/1 --> not works
result: /admin/main/category/1?page=1
second case-> /admin/main/category/1 --> works
/admin/main/updatecategory/1 --> not works
result: /admin/main/updatecategory/1?cid=1
Here is my controller actions:
public ActionResult Category(int? page)
{
int pageIndex = page.HasValue ? page.Value : 1;
return View("Category", CategoryViewModelFactory(pageIndex));
}
public ActionResult CreateCategory()
{
return View();
}
public ActionResult UpdateCategory(int cid)
{
return View();
}
public ActionResult DeleteCategory(int? cid)
{
return View();
}
What is this problem and how to solve it?
I'm totally confused, Routing in ASP.MVC3 is e-logical.
Help?!

When routes are searched, the first one that matches your URL is used. AdminUpdateCategoryView will match any admin controller, and action. You provide a default cid of "", but that shouldn't matter because you're requiring that cid be a number below that. AdminCategoryListView will match any url that enters main. Because you provide a default page of 1, it doesn't even matter if no page is provided.
So if AdminCategoryListView is on top: every single route in admin/main will use this route.
If AdminUpdateCategoryView is on top every route in admin that reaches this route and has a numerical cid value parameter will use it.
I'd recommend putting AdminCategoryListView on top because it's the more specific route. Either remove page="1" (depends on if you want to provide a default), or replace {action} with "category" so your other routes don't use this route. Also you should provide a default controller of main, otherwise it will assume the controller you're currently using is the correct one.
context.MapRouteLowercase(
"AdminCategoryListView",
"admin/Main/category/{page}",
new { action = "Category", controller = "Main" },
new {page = #"\d+"},
new[] {"WebUI.Areas.Admin.Controllers"}
);
//Put AdminUpdateCategoryView here

Related

MVC3 links SEO - how to make calls from one controller to other controller

I have one problem, I am not sure how to explain but I will try.
I followed this: http://www.deliveron.com/blog/post/SEO-Friendly-Routes-with-ASPnet-MVC.aspx
And I was able to achieve what they describe.
But if I have a page where i wish to call action from other controller, it doesn't work.
It doens't show the link in this way: "this-is-my-link" in the URL.
I don't know what do I do wrong?
in Global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default2",
"{controller}/{action}/{id}/{pageTitle}",
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
pageTitle = UrlParameter.Optional
}
);
}
Views/Help/FAQ
#Html.ActionLink(FaqStrings.ContactUs, "ContactUs", "Home", new { id = 1, pageTitle = "link text".ToSeoUrl() })
It says that it can't resolve the ContactUs. Instead of the actions from HomeController, it sees the actions of the HelpController.
In HomeController
[AllowAnonymous]
public ActionResult ContactUs()
{
var model = new ViewModelContactUs
{
Resultmessage = string.Empty,
Youremail = string.Empty,
Yourmessage = string.Empty,
Yourname = string.Empty
};
return View(model);
}
[AllowAnonymous]
[HttpPost]
public ActionResult ContactUs(ViewModelContactUs model)
{
Log.DebugFormat("HomeController-ContactUs()");
........
var model2 = new ViewModelContactUs
{
......
};
ModelState.Clear();
return View(model2);
}
Try moving your controller into the RouteValues dictionary part of your #Html.ActionLink
#Html.ActionLink(FaqStrings.ContactUs, "ContactUs", new {controller="Home", id = 1, pageTitle = "link text".ToSeoUrl() })
EDIT
To address the logic, it has to do with the available signatures of the Html.ActionLink method. Here is the MSDN for information but...
The method signature that you were using was Html.ActionLink("Link Text", "Action", "Contoller", RouteValues, HtmlAttributes). Since you were not passing HtmlAttributes, it was matching up wrong and generating the wrong link. By either moving the controller into the RouteValuesDictionary or passing a , null at the end of your call should solve it. But, I personally don't like throwing nulls around unless I need to, so I typically just define the controller in the RouteValuesDictionary.
If you were not passing any RouteValues, then a call to Html.ActionLink("Link Text", Action, Controller) works with no issues.
Hope that clears it up a little! :)
The underlying problem is that:
#Html.ActionLink(FaqStrings.ContactUs,
"ContactUs",
"Home",
new { id = 1, pageTitle = "link text".ToSeoUrl() })
Will produce an Anchor which when clicked will produce an HTTP GET, however you're method is requiring HTTP POST:
[AllowAnonymous]
[HttpPost] // <-----POST
public ActionResult ContactUs(ViewModelContactUs model)

url parameters missing after adding a comment and redirecting back to blog post

I am teaching myself asp .net mvc3 by creating a blog application. However, I have
problems with comment upload. It is a very subtle error in that everything works when a user leaves a comment. However, the url of the post changes.
So, a blog post has a url
http://localhost:49175/Blog/Details/3/Third-post
This is generated by the url route map here:
routes.MapRoute(
"BlogDetail", // Route name
"Blog/Details/{id}/{urlHeader}", // URL with parameters
new { controller = "Blog", action = "Details", id = UrlParameter.Optional, urlHeader = UrlParameter.Optional } // Parameter defaults
);
Now, when a user leaves a comment - he is directed to a comment controller:
[HttpPost]
public ActionResult Create(BlogDetailsViewModels viewModel)
{
if (ModelState.IsValid)
{
try
{
blogrepository.Add(viewModel.Comment);
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID });
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save comment. Try again, and if the problem persits then contact administrator.");
}
}
// If we got this far, something failed, redisplay form
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID });
}
}
However, when somebody leaves a comment - he is redirected back to
http://localhost:49175/Blog/Details/3
I know, as of now there is nothing in the RedirectToAction that passes the urlHeader info. However, I have tried a few things like:
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID, urlHeader = viewModel.Blog.UrlHeader });
However, it doesn´t seem to work.
This is the blog details controller:
//
// GET: /Blog/Details/5
public ViewResult Details(int id, string urlHeader)
{
var blogs = blogrepository.GetBlog(id);
var recentblogs = blogrepository.FindRecentBlogs(5);
var archivelist = blogrepository.ArchiveList();
BlogDetailsViewModels viewModel = new BlogDetailsViewModels { Blog = blogs, RecentBlogs = recentblogs, ArchiveList = archivelist };
return View(viewModel);
}
I am stuck for days on this.
-- Full route method as requested --
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"BlogDetail", // Route name
"Blog/Details/{id}/{urlHeader}", // URL with parameters
new { controller = "Blog", action = "Details", id = UrlParameter.Optional, urlHeader = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"BlogArchive", // Route name
"Blog/{year}/{month}", // URL with parameters
new { controller = "Blog", action = "Archive" }, // Parameter defaults
new { year = #"\d{4}", month = #"\d{1,2}", } // Parameter constraints
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
If your form does not contains data for viewModel.Blog.UrlHeader, it will be an empty string, even viewModel.Blog may be null.
You can add a parameter to your post action method, like this:
[HttpPost]
public ActionResult Create(BlogDetailsViewModels viewModel, String urlHeader)
And, in your view that renders the form, use this code to generate the form element:
#Html.BeginForm("Create","Blog",new{urlHeader=Model.Blog.UrlHeader})
Alternatively, you can add a hidden input in your form for the urlHeader. In this way, you don't have to do any of previous two updates.
#Html.HiddenFor(m=>m.Blog.UrlHeader)
Either way, make sure your Model.Blog.UrlHeader is not null or an empty string

Value not getting passed in url

//In Controller this is what I have
public class AjaxController : Controller
{
[AcceptVerbs(new string[]{"GET"})]
public ActionResult FindRooms(string selectedBuilding)
{
return Json(new { Room = "x"}, JsonRequestBehavior.AllowGet);
}
}
//In Global.asax.cs I have
routes.MapRoute
(
"AjaxRoute1",
"Ajax/FindRooms/{selectedBuilding}",
new { controller = "Ajax", action = "FindRooms", selectedBuilding = "" }
);
If I browse to URL localhost/Ajax/FindRooms/SOMEBUILDING. The SOMEBUILDING is not getting passed to selectedBuilding parameter of FindRooms method. It is always null?? How can I fix this so that it works in both POST and GET?
As Mr TA suggested the other routes affects this. The order of routing is important.
I put the default route route at the end and works like a charm.!!
routes.MapRoute
(
"AjaxRoute1",
"Ajax/FindRooms/{selectedBuilding}",
new { controller = "Ajax", action = "FindRooms", selectedBuilding = "" }
);
routes.MapRoute
(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Migrate custom route to ASP.NET MVC custom route to support List<string>

I have a URL in a Django based web app that looks similar to this:
/market/prices/2011-05-01/min/stocks/msft/dell/appl/
The application is being rewritten in ASP.NET MVC 3. I need to maintain the URL.
The crux of the problem is that I to support the multiple stock ticker symbols separated by forward slashes.
I want a custom route that looks like this:
routes.MapRoute(
"Stocks",
"{queryDate}/{minOrMax}/stocks/{listOfStocksSeparatedByForwardSlash}",
new { controller = "Market", action = "Prices" }
);
The controller would look something like:
public ActionResult Prices(string queryDate, string minOrMax, ICollection<string> listOfStocksSeparatedByForwardSlash) {
var model = repository.List(queryDate, minOrMax, listOfStocksSeparatedByForwardSlash);
return View(model );
}
My current solution is as follows:
routes.MapRoute(
"Stocks",
"{queryDate}/{minOrMax}/stocks/{*listOfStocksSeparatedByForwardSlash}",
new { controller = "Market", action = "Prices" }
);
public ActionResult Prices(string queryDate, string minOrMax, string listOfStocksSeparatedByForwardSlash) {
var list = listOfStocksSeparatedByForwardSlash.Split('/').ToList();
var model = repository.List(queryDate, minOrMax, list);
return View(model );
}
Although this works, I'm interested to know if there is a better way to do this?
Okay, this is an option, although I think your approach is easier.
You can provide a RouteHandler attached to a route, like so:
routes.MapRoute(
name: "Test",
url: "Test/{someDate}/{*tickerSymbols}",
defaults: new { controller = "Home", action = "Test" }).RouteHandler = new SlashSeparatedTrailingParametersRouteHandler("tickerSymbols", "tickers");
with the route handler being
public class SlashSeparatedTrailingParametersRouteHandler : IRouteHandler
{
private readonly string catchallParameterName;
private readonly string actionTargetParameter;
public SlashSeparatedTrailingParametersRouteHandler(string catchallParameterName, string actionTargetParameter)
{
this.catchallParameterName = catchallParameterName;
this.actionTargetParameter = actionTargetParameter;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
IRouteHandler handler = new MvcRouteHandler();
var vals = requestContext.RouteData.Values;
vals[this.actionTargetParameter] = vals[this.catchallParameterName].ToString().Split('/');
return handler.GetHttpHandler(requestContext);
}
}
If this is your controller action:
[HttpGet]
public ActionResult Test(DateTime someDate, string[] tickers)
{
if (tickers == null)
{
throw new ArgumentNullException("tickers");
}
return Content(string.Format("{0} and {1}", someDate, tickers.Length.ToString(CultureInfo.InvariantCulture)));
}
and this your post:
http://localhost/Test/12-06-2012/Foo/Bar
then your output is:
12/6/2012 12:00:00 AM and 2
On the elegance this this improves the parameter on the action method at the expense of having to write your own route handler.

How to get url parameter from current route in asp.net mvc?

I have a route like this:
http://localhost/c/61/legetoj
its defined as:
routes.MapLocalizedRoute("Category",
"c/{categoryId}/{SeName}",
new { controller = "Catalog", action = "Category", SeName = UrlParameter.Optional },
new { categoryId = #"\d+" },
new[] { "Nop.Web.Controllers" });
Now, on all the pages having this url, I want to get SeName value (here is `legetoj')
In my view (header) I've tried this with: ViewContext.RouteData.Values["SeName"]
but it returns empty..
Do you know what I am doing wrong?
Just set up an action with the same name parameter as you would like to accept such as:
public ActionResult Category(int categoryId, string SeName) {
// do stuff
}
It should automatically insert that value inside the variable.

Resources