So if I have a URL such as mysite.com/user/1234/show which object can I use in the view that is displayed to get at the 1234 int in the URL above? For instance I want to have an action link that uses the user id again for the next controller action call such as mysite.com/user/1234/edit.
Thanks
You shouldn't have to go directly to the URL for the data. Instead, add a parameter to your route and action. Your route could look something like "{controller}/{id}/{action}" and your action something like:
ViewResult Show(int userId){
var viewData = new MyViewData(){
UserID = userId
};
return View(viewData);
}
Then, in your view, use Model.UserID.
EDIT: This is initially more work than just doing a (dare I write it?)
Int32.Parse(Request["userId])
in your view. However, the approach I have shown above is the preferred approach, since you will be leveraging the power of MVC's routing and binding capabilities. If you want to change parameter name someday, you just need to fix your route and the action's parameter name, as opposed to having to sort through all of your application searching for places where you pull values directly from the Request collection.
I believe you can get at it via the RouteData property of the request context. But a much easier way would be to just add a field for it to your model and have the controller set it there.
It depends upon your your routing scheme.
have look at these tutorial
Creating Custom Routes
mysite.com/user/show/1234
routes.MapRoute(
"User", // Route name
"user/show/{ID}", // URL with parameters
new { controller = "User", action = "show" } // Parameter defaults
);
above url wiil hopefully call, 'show' Action for 'user' controller and pass the ID, 1234
Change the default route in your global.asax.cs to -
routes.MapRoute(
"Default", // Route name
"{controller}/{id}/{action}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
Create a UserController with a Show method like this -
public class UserController : Controller
{
public ActionResult Show(int id)
{
var model = new UserViewModel {Id = id};
// Retrieve user from data layer and update model with other user details here
return View(model);
}
public ActionResult Edit(int id)
{
// Deal with edit action in here
}
}
public class UserViewModel
{
public int Id { get; set; }
}
In your aspx view, make sure that you page inherits from ViewPage<UserViewModel> by declaring in the page directive of your aspx view -
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<UserViewModel>" %>
Then you can create an edit link in your page like this -
<%=Html.ActionLink("Edit User", "Edit", new { id = Model.Id }) %>
Related
Description
I am using only Attribute Routing in one of my MVC Controllers:
[RoutPrefix("Groups")] // NOTE: I want to use "Groups" with an "s" here.
public class GroupController : Controller
{
[HttpGet]
[Route("Edit/{id}")]
public ActionResult Edit(Guid id)
{
//...
}
[HttpPost]
[Route("Edit")]
public ActionResult Edit(GroupEditViewModel model)
{
// ...
}
}
In the Razor view, whenever using helpers such as:
#Html.ActionLink("Text", "Edit", new {controller = "Groups", id = "someId"})
or
#Html.BeginForm(actionName: "Edit", controllerName: "Groups")
{
}
The routes generated by the helper function are null:
<a href>Text</a>
and
<form action></form>
Question
Is this happenning because the controller's name is unknown maybe?
Is there a way I can set the name of the controller with using Only Attribute Routing ?
Common practices known for attribute routing are setting RoutePrefix with same name as controller's name:
[RoutePrefix("Group")]
public class GroupController : Controller
{
// ...
}
However, as I had seen here, custom name(s) can be set without altering/removing original route:
[RoutePrefix("Groups")]
public class GroupController : Controller
{
// ...
}
Remember that RoutePrefix attribute only giving alternative way to access corresponding controller, not changing its original name.
Route Prefixes are nothing but the prefix for any route that we want to apply, all we need to do is to define the route prefix on a controller so that all the action methods inside it can follow the prefix.
OTP, since "Groups" is just a RoutePrefix parameter value instead of controller class, all HTML helpers used "Groups" as controller name will return null value on respective HTML attributes.
Thus, original controller's name must be used instead of RoutePrefix value:
#Html.ActionLink("Text", "Edit", new {controller = "Group", id = "someId"})
#Html.BeginForm(actionName: "Edit", controllerName: "Group")
{
}
or use MapRoute inside RegisterRoutes method to manipulate URL together with original controller's name:
// note that "Group" controller declared with "Groups" as part of relative URL
routes.MapRoute("Groups", "Groups/{action}/{id}", new { controller = "Group", action = "Edit", id = UrlParameter.Optional });
To activate RoutePrefix effect when routing, MapMvcAttributeRoutes() should inserted into RegisterRoutes method.
CMIIW.
The route prefix attribute, [RoutePrefix("Groups")], controls the resulting URL. MVC, however will always use the class name as the name of the controller.
They don't have to be the same. You can simply use the route prefix to control the resulting URL and use the class name as the controller name where required and the resulting URL will be correct.
In your example:
[RoutePrefix("Groups")]
public class GroupController : Controller
{
[HttpGet]
[Route("Edit/{id}")]
public ActionResult Edit(Guid id)
{
//...
}
[HttpPost]
[Route("Edit")]
public ActionResult Edit(GroupEditViewModel model)
{
// ...
}
}
The following
#Html.ActionLink("Text", "Edit", new {controller = "Group", id = "someId"})
would result in a link that looked like this (note the "Groups" in the URL despite the "Group" controller name):
Text
I'm having trouble adding a URL parameter to every URL generated, or redirected to in an ASP MVC 4 application.
I want to generate an ID, and use this ID at any point throughout the application. Storing the id in session is not an option as a single session may have multiple browser windows/tabs open concurrently (each with a different id)
RouteConfig
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{customId}",
defaults: new { controller = "Home", action = "Index", customid = UrlParameter.Optional }
);
HomeController.cs
public class HomeController : Controller
{
public ActionResult Index()
{
var customId = Guid.NewGuid();
ControllerContext.RequestContext.RouteData.Values.Add("customId", customId);
//How do I get this redirect to add customid to the url?
//E.g. /Home/Start/{customId}
return RedirectToAction("Start");
//I could do this: But I want it this to happen for every URL,
//and I don't want to replicate this code everywhere
//return RedirectToAction("Start", new { customId = customId });
}
public ActionResult Start()
{
object customId;
//Redirect Loop
if (!Request.RequestContext.RouteData.Values.TryGetValue("customId", out customId))
{
//To generate the ID
return RedirectToAction("Index");
}
ViewData["customId"] = Guid.Parse(customId.ToString());
return View();
}
public ActionResult Next()
{
object customId;
//Redirect Loop
if (!Request.RequestContext.RouteData.Values.TryGetValue("customId", out customId))
{
//To generate the ID
return RedirectToAction("Index");
}
ViewData["customId"] = Guid.Parse(customId.ToString());
return View();
}
}
Not only do I want the ID to be automatically inserted into any Redirect results, but when a View is rendered #Url.Action() and #Html.ActionLink() should also add the ID to the generated URL's.
Start.cshtml
#*Both of these should generate an href="~/Home/Next/{customId}"*#
#Html.ActionLink("Go to Next", "Next", "Home")
Go to Next
How do I automatically add an ID to ALL outgoing routes in ASP MVC?
Create an action filter that will add the ID to the route data in the OnActionExecuting method? You can access the controller through the filter context (and the viewbag). As long as your viewbag contains the customId, you should be able to add it to the route data. At least this way you only need to remember to add the attribute on the controller.
OR
Create a base class that inherits from System.Web.Mvc.Controller and implement your own RedirectToAction. Then have all your controllers inherit form MyControllerBase. Something like this.
public class MyControllerBase : Controller
{
public RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, object>> actionExpression)
{
var custId = ControllerContext.Controller.ViewBag["customId"];
string controllerName = typeof(TController).GetControllerName();
string actionName = actionExpression.GetActionName();
return RedirectToAction(actionName, controllerName, new {cId = custId});
}
}
PART 2:
Another way I've modified a URL (I knew I had the code somewhere!) on every view, I needed the URL to link from a mobile site to a full browser site and read the mappings from the database. So in my footer, I have the following:
<a id="fullSiteLink" href="<%= ViewData[AppConstants.MainSiteUrl] %>">Visit our Full Browser site</a><br />
I then added a filter to the base controller class and onactionexecuting (before the action),
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var mainSiteUrl = _mobileToMainRedirect.GetMainSiteUrl(filterContext.HttpContext.Request.Url);
filterContext.Controller.ViewData.Add(AppConstants.MainSiteUrl, string.IsNullOrEmpty(mainSiteUrl) ? UrlHelperExtensions.FullBrowserSite(filterContext.HttpContext.Request.Url) : mainSiteUrl);
}
Complete shot in the dark....
You can set up the route so that if a value is not provided, you create the Id. This way, if the value is there, it will use the provided one. Otherwise, it will create one.
Since this is leveraging the routes, you will be able to generate the Id even when using:
#Html.ActionLink("Go to Next", "Next", "Home")
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{customid}",
defaults: new { controller = "Home", action = "Index", customid = Guid.NewGuid() }
);
NOTE: You would replace Guid.NewGuid() with your own Id generator.
We just started using ASP.NET MVC3 and we want to unit test our controller.
Here is my controller function:
[HttpGet]
Public ActionResult Action()
{
Guid Id = Guid.Empty;
string[] UrlSegments = Request.Url.Segments;
Guid.TryParse(UrlSegments[UrlSegments.Count() - 1], out Id);
if(Id == Guid.Empty)
return RedirectToAction("ErrorPage");
}
I want to test this controller function.I have put the matching route to match this function in my global.asax file. Basically I am trying to retrieve the guid from the URl and if it is not good then take him to error page.
HttpRequestBase class URl is no setter,so I am confused how to test this functionality?
As for testing read the link provided in the comments above but I would recommend doing some reading on MVC Routing or here, it does exactly what you are trying to achieve.
Have a look in your global.ascx.cs file you will find something like this:
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
);
This is the out of the box route for the MVC framework.
Routes will match a url based on the convention you give them.
So based on the default route above and given a url like this:
http://localhost:portnumber/MyController/MyAction/8B4B93DE-76CA-4552-B4AA-460400761EAD
It will try and match this url to a controller called MyController with an action called MyAction, which receives and Id. It will only hit this action if all the criteria match.
If there is no id in the URL and id defined on your action method is not a nullable type then it simple wont execute. It will rather try match another Url, if it cant find one it will give a 404.
Like this:
public class MyController : Controller {
[HttpGet]
Public ActionResult MyAction(Guid id)
{
}
}
I am creating a blog engine, and I need a custom route, like this:
localhost/blogname/posts/1
Where blogname should be handled by a BlogsController, and posts will be an action.
How would I define such a route?
I don't think you need to define {controller} in your Url if you define it as a constraint. I think this should work:
routes.MapRoute("Default",
"{action}/{id}",
new { controller = "Blogs", action = "Posts" },
new { controller = "Blogs"});
It might cause problems with other routes though, I'm not sure. If it doesn't work, David's answer of http://site.com/blogs/posts/id is the best way to go.
This feels like a weird approach. If you use the default routing in ASP.NET MVC, you would need one controller class per blog--not something you can easily create on the fly.
If you use the classname BlogsController, then the default routing would work for URLs of the form:
/Blogs/SomeAction/123
Maybe this is what you're looking for:
public class BlogsController : Controller
{
public ActionResult List()
{
return View(GetPostsOrSomething());
}
public ActionResult Posts(int id)
{
return View(new BlogViewModel(id));
}
[HttpPost]
public ActionResult Comment(int id, string comment)
{
// do comment
}
}
And your routing would need to look like this:
routes.MapRoute(
"Blogs", // Route name
"{blog}/{action}/{id}", // URL with parameters
new { controller = "Blogs", action = "List", id = UrlParameter.Optional }
);
Note
Bear in mind, this would match default style URLs, and everything might be routed to your BlogsController. Maybe you could consider a regular expression for the {blog} part of the pattern:
routes.MapRoute(
"Blogs", // Route name
"{blog}/{action}/{id}", // URL with parameters
new { controller = "Blogs", action = "List", id = UrlParameter.Optional },
new { blog = "(blogname1|blogname2|blogname3|etc)" }
);
But, this isn't very flexible either. Any time you added a blog to your site, this regular expression would require an update. I would probably reconsider your URL structure--something similar to the default style.
Something like this
routes.MapRoute(
"Blogs", // Route name
"{blogname}/{action}/{id}", // URL with parameters
new { controller = "Blog", action = "Posts", id = UrlParameter.Optional } // Parameter defaults
);
would work, meaning you could have your blog controller like this:
public class BlogController : Controller
{
public ActionResult Posts(string blogname, int id)
{
... get posts based on blog name and id and return view...
}
}
but then if you want a url like localhost/admin/dostuff/1 to go to an admin controller, how will MVC know that you don't just mean a 'blogname' called 'admin'?
You would need to do something like the regex matching that David suggests, or else specifically add a route for any other controllers you have before you add the Blog route
e.g.
routes.MapRoute(
"Admin Controller Routes", // Route name
"admin/{action}/{id}", // URL with parameters
new { controller = "Admin", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Account Controller Routes", // Route name
"account/{action}/{id}", // URL with parameters
new { controller = "Account", id = UrlParameter.Optional } // Parameter defaults
);
... etc - one for each controller ...
I found this approach to be best for my needs. Very simple, goal is to have the title of the blog as the parameter but also the URL (I didn't want a ? to set a parameter value, I have been told it is not helping SEO. Goal is MYURL.com/blog/my-blog-title
ASP.NET MVC
add to your RouteConfig.cs file
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Blog",
url: "blog/{title}",
defaults: new { controller = "Blog", action = "GetBlog" },
constraints: new { title = #"[\w\-]*" }
);
Add a Controller and call it Blog and then add the following Action to that Controller
[Route("blog/{title}")]
public ActionResult GetBlog(string title)
{
// do what ever code you need to do here to get the blog from the title and pass a model to the view using return View(MyBlogObject)
return View();
}
I have a page. (action) and a controller called Widget. im passing in client as a string.
I want to be able to pass in the client from one page to the next, as well as the other fields posted.
what am i doing wrong below? client is coming up as null
eg: Widet/Page2/clientABC
public ActionResult Page2(string client)
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Page2(string client, string sector)
{
return RedirectToAction("Page3", new { client = client, sector = sector });
}
public ActionResult Page3(string client, string sector)
{
return View();
}
Does this work?
Widet/Page2?client=clientABC§or=123
Since you have an action with multiple parameters, I think you need to name them in the query string. That's how I've done it. Unless the action has a single parameter, the default routing doesn't handle the way you are trying to call it.
Look in your Global.asax.cs file to see the routing config.
If it looks like this:
public class MvcApplication : System.Web.HttpApplication
{
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 = "" } // Parameter defaults
);
routes.MapRoute(
"Root",
"",
new { controller = "Home", action = "Index", id = "" }
);
}
You could fiddle with it and make it support multiple params like:
{controller}/{action}/{param1}/{param2}
Though I would just use named parameters in the query string.
If I understand this right your problem is that you don't post the client to your "Page2" action. You can ether post it as a post parameter (in a hidden field for example) or in the url (in the action of your form tag). My guess is that you want it in your url.
If you use the form html helper you can use it like this:
<%using(Html.BeginForm("Page2", "Home", new { client = "clientABC" })) { } %>