RedirectToAction and Routing not passing parameter correctly - asp.net-mvc-3

I am trying to use a RedirectToAction method after a selfposting Action (which passes IsValid). The redirect happens fine but the parameter I am attempted to pass to the action is always null.
[HttpPost]
public ActionResult UploadForm(UploadFormViewModel formVM)
{
if (!ModelState.IsValid)
{
return View(formVM);
}
return RedirectToAction("UploadConfirm", new { confirmVM = new UploadConfirmViewModel() });
}
public ActionResult UploadConfirm(UploadConfirmViewModel confirmVM)
{
return View(confirmVM);
}
And here is my routing
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"UploadConfirm",
"{controller}/{action}/{confirmVM}",
new { controller = "EnrollmentUpload", action = "UploadConfirm" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "EnrollmentUpload", action = "UploadForm", id = UrlParameter.Optional } // Parameter defaults
);

Remove the new{} in your return statement:
return RedirectToAction("UploadConfirm", new UploadConfirmViewModel());
I also did not add an extra route to the Routecollection

Related

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 }
);

Custom route does not work in ASP.net MVC 3

I am trying to implement my custom route in ASP.net MVC 3 but I get this error:
The resource cannot be found.
global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"mycontroller", // Route name
"{controller}/{name}", // URL with parameters
new { controller = "MyController", action = "Search" } // Parameter defaults
);
}
MyController.cs
public class MyController : Controller
{
public ActionResult Search(string name)
{
return Content(name);
}
}
Try this instead:
routes.MapRoute(
"mycontroller", // Route name
"mycontroller/{name}", // URL with parameters
new { controller = "My", action = "Search", } // Parameter defaults
);
MyController won't be found because you don't have a controller named MyControllerController. By virtue of inheriting from Controller the convention will be looking for the URL token + "Controller".
Cheers.
Try changing it to
routes.MapRoute(
"mycontroller", // Route name
"{controller}/{name}", // URL with parameters
new { controller = "My", action = "Search", } // Parameter defaults
);
Please register it in Global.asax file

ASP.net MVC routing with optional first parameter

I need to provide following functionality for one of the web sites.
http://www.example.com/[sponsor]/{controller}/{action}
Depending on the [sponsor], the web page has to be customized.
I tried combination of registering the routes with Application_Start and Session_Start but not able to get it working.
public static void RegisterRoutes(RouteCollection routes, string sponsor)
{
if (routes[sponsor] == null)
{
routes.MapRoute(
sponsor, // Route name
sponsor + "/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
}
Also, the default behavior without [sponsor] should also function.
Can someone please let me know if it is technically feasible to have an optional first parameter in the MVC3 URL. If yes, please share the implementation. Thank you.
Updated Code
After making the changes as suggested by Sergey Kudriavtsev, the code works when value is given.
If name is not provided then MVC does not route to the controller/action.
Note that this works only for the home controller (both and non-sponsor). For other controllers/actions, even when sponsor parameter is specified it is not routing.
Please suggest what has to be modified.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"SponsorRoute",
"{sponsor}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"NonSponsorRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional, sponsor = string.Empty }
);
}
Action Method
public ActionResult Index(string sponsor)
{
}
In your case sponsor should not be treated as a constant part of URL, but as a variable part.
In Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"SponsorRoute",
"{sponsor}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"NonSponsorRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
...
}
In your controllers, for example, HomeController.cs:
namespace YourWebApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index(string sponsor)
{
// Here you can do any pre-processing depending on sponsor value, including redirects etc.
}
...
}
}
Note that type of this parameter will always be System.String and the name of route template component {sponsor} must exactly match the name of action parameter string sponsor in your controllers.
UPD: Added second route for non-sponsor case.
Please note that such setup will complicate your logic, because you might confuse different urls, for example URL
http://www.example.com/a/b/c
could be matched by both routes: first one will have sponsor=a, controller=b and action=c; second one will have controller=a, action=b and id=c.
This situation can be avoided if you specify more strict requirements to URLs - for example, you may want IDs to be numerical only. Restrictions are specified in fourth parameter of routes.MapRoute() function.
Another approach for disambiguation is specifying separate routes for all of your controllers (usually you won't have much of them in your app) before generic route for sponsors.
UPD:
Most straightforward yet least maintainable way to distinguish between sponsor and non-sponsor routes is specifying controller-specific routes, like this:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"HomeRoute",
"Home/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
routes.MapRoute(
"AccountRoute",
"Account/{action}/{id}", // URL with parameters
new { controller = "Account", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
...
routes.MapRoute(
"SponsorRoute",
"{sponsor}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
...
}
Note that here all controller-specific routes must be added before SponsorRoute.
More complex yet more clean way is implementing RouteConstraints for sponsor and controller names as described in answer from #counsellorben.
In my case, I've resolved this issue using the following two routers:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "MultiCulture",
url: "{culture}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { culture = new CultureConstraint(CultureFactory.All.Select(item => item.UrlPrefix).ToArray()) }
).RouteHandler = new MultiCultureMvcRouteHandler();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
}
}
Where CultureConstraint class looks like below:
public class CultureConstraint : IRouteConstraint
{
private readonly string[] values;
public CultureConstraint(params string[] values)
{
this.values = values;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary routeValues, RouteDirection routeDirection)
{
string value = routeValues[parameterName].ToString();
return this.values.Contains(value);
}
}
And MultiCultureMvcRouteHandler like this:
public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
var culture = CultureManager.GetCulture(requestContext.RouteData);
if (culture != null)
{
var cultureInfo = new CultureInfo(culture.Name);
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);
}
return base.GetHttpHandler(requestContext);
}
}
In addition to adding a second route before the default route, as Sergey said in his answer, you also must add a RouteConstraint to the initial route, to enforce that the {sponsor} token is the name of a valid sponsor.
You can use the RouteConstraint in this answer: Asp.Net Custom Routing and custom routing and add category before controller
Remember that you must also enforce a rule that a sponsor name cannot be the same as any of your controller names.
i will show you in simple example you don't have to change in Route.config.cs
only you have to do in Route.config.cs just put in
Optional URI Parameters First and Default Values
Route.config.cs
routes.MapMvcAttributeRoutes();
Controller
[Route("{Name}/Controller/ActionName")]
public ActionResult Details(string Name)
{
// some code here
return View();
}
Results
localhost:2345/Name/controllername/actionname/id(optional)

URL Routing: How do I have a string as my id?

I would like to receive a string as the id in the URL. Here is an example:
http://www.example.com/Home/Portal/Fishing
I would like to have Fishing in my id. But I cannot achieve it with the following code:
Code from my Controller:
public ActionResult Portal(string name)
{
// some code
ViewData["Portal Name"] = name;
}
Code from Global.asax.cs:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Just change the argument to id:
public ActionResult Portal(string id)
{
// some code
ViewData["Portal Name"] = id;
}
The argument will be bound if it has the same name as the route value token. So an alternate approach would be to keep the argument named name and change the route:
public ActionResult Portal(string name)
{
// some code
ViewData["Portal Name"] = name;
}
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{name}", // URL with parameters
new { controller = "Home", action = "Index", name = UrlParameter.Optional } // Parameter defaults
);
I would choose using id, though, as it's the more standard approach.

Resources