I want to redirect to an action in other Controller but it doesn't work
here's my code in ProductManagerController:
[HttpPost]
public ActionResult RedirectToImages(int id)
{
return RedirectToAction("Index","ProductImageManeger", new { id=id });
}
and this in my ProductImageManagerController:
[HttpGet]
public ViewResult Index(int id)
{
return View("Index",_db.ProductImages.Where(rs=>rs.ProductId == id).ToList());
}
It redirect to ProductImageManager/Index without parameter very well(no error) but with above code i get this:
The parameters dictionary contains a null entry for
parameter 'ID' of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ViewResult Index(Int32)' in
'...Controllers.ProductImageManagerController'.
An optional parameter must be a reference type, a nullable type, or be
declared as an optional parameter. Parameter name: parameters
This error is very non-descriptive but the key here is that 'ID' is in uppercase. This indicates that the route has not been correctly set up. To let the application handle URLs with an id, you need to make sure that there's at least one route configured for it. You do this in the RouteConfig.cs located in the App_Start folder. The most common is to add the id as an optional parameter to the default route.
public static void RegisterRoutes(RouteCollection routes)
{
//adding the {id} and setting is as optional so that you do not need to use it for every action
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Now you should be able to redirect to your controller the way you have set it up.
[HttpPost]
public ActionResult RedirectToImages(int id)
{
return RedirectToAction("Index","ProductImageManager", new { id });
//if the action is in the same controller, you can omit the controller:
//RedirectToAction("Index", new { id });
}
In one or two occassions way back I ran into some issues by normal redirect and had to resort to doing it by passing a RouteValueDictionary. More information on RedirectToAction with parameter
return RedirectToAction("Index", new RouteValueDictionary(
new { controller = "ProductImageManager", action = "Index", id = id } )
);
If you get a very similar error but in lowercase 'id', this is usually because the route expects an id parameter that has not been provided (calling a route without the id /ProductImageManager/Index). See this so question for more information.
This should work!
[HttpPost]
public ActionResult RedirectToImages(int id)
{
return RedirectToAction("Index", "ProductImageManeger", new { id = id });
}
[HttpGet]
public ViewResult Index(int id)
{
return View(_db.ProductImages.Where(rs => rs.ProductId == id).ToList());
}
Notice that you don't have to pass the name of view if you are returning the same view as implemented by the action.
Your view should inherit the model as this:
#model <Your class name>
You can then access your model in view as:
#Model.<property_name>
Try this,
return RedirectToAction("ActionEventName", "Controller", new { ID = model.ID, SiteID = model.SiteID });
Here i mention you are pass multiple values or model also.
That's why here i mention that.
return RedirectToAction("ProductImageManager","Index", new { id=id });
Here is an invalid parameters order, should be an action first
AND
ensure your routing table is correct
Related
I have the following code:
GET method:
public async Task<ActionResult> EditUser(string username)
{
// something here
}
POST method:
[HttpPost]
public async Task<ActionResult> EditUser(UserEditViewModel model)
{
if (ModelState.IsValid)
{
// all right, save and redirect to user list
return RedirectToAction("UserList");
}
// something wrong with model, return to form
return View(model);
}
It works fine, but get parameter in URL of browser username=bla-bla-bla is lost. So, user can't copy that link to open this page again. Is is possible to restore URL param? If I do redirect then I lost model with mistakes...
To "forward" query string parameters to another URL, you can add them as route values. When a route encounters parameters that are undefined, it will add them as query string parameters.
The only thing extra you need to do is convert the query string parameters back into route values for the requested route, and to ensure that the parameters you want to use as query string values are not defined in the route.
NameValueCollectionExtensions
Unfortunately, the Request.QueryString parameter is a NameValueCollection, but the route values have a separate structure RouteValueDictionary that the query string needs to be translated to. So we make a simple extension method to make this easier.
public static class NameValueCollectionExtensions
{
public static RouteValueDictionary ToRouteValueDictionary(this NameValueCollection col)
{
var dict = new RouteValueDictionary();
foreach (var k in col.AllKeys)
{
dict[k] = col[k];
}
return dict;
}
}
Usage
[HttpPost]
public async Task<ActionResult> EditUser(UserEditViewModel model)
{
if (ModelState.IsValid)
{
// all right, save and redirect to user list including
// query string parameters.
return RedirectToAction("UserList", this.Request.QueryString.ToRouteValueDictionary());
}
// something wrong with model, return to form
return View(model);
}
This assumes you are using the Default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Since it has no username route value defined, passing a username route value to RedirectToAction will cause it to add it to the URL as a query string parameter.
/SomeController/UserList?username=bla-bla-bla
I am trying to overload the MVC controllers, how can I overload it properly?
I want to list all companies in my website in a ListCompanies() controller like below
http://localhost:21047/Home/ListCompanies
and I want to add search criteria if user makes a search like below
http://localhost:21047/Home/ListCompanies/sera
"sera" is my search criteria. if search criteria exist, I want to filter my search result according to the search criteria.
here are my controllers
public ActionResult ListCompanies()
{
return View(db.AY_COMPANIES);
}
[ActionName("ListCompaniesFilter")]
public ActionResult ListCompanies(string filter)
{
var filtredCompanies = from c in db.AY_COMPANIES
where c.COMPANY_FULL_NAME.StartsWith(filter)
select c;
return View(filtredCompanies);
}
and here is my ROUTING which behaves not correctly.
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(
"Home", // Route name
"{controller}/{action}/{filter}", // URL with parameters
new { controller = "Home", action = "ListCompanies", filter = UrlParameter.Optional} // Parameter defaults
);
}
My MapRoutes are not correct because it doesnt get the search criteria properly. how can I fix this?
You can't overload in this manner because there is dispatch issue due to two routes on having the same three parts, they are essentially the same.
"{controller}/{action}/{id}"
and
"{controller}/{action}/{filter}"
One way is to collapse the two action methods into one
public ActionResult ListCompanies(string filter)
then if filter is null return the full list else return the filtered list.
I am not fond of this approach.
A better yet simple way to fix this is:
1) remove the second route (the one with the filter)
2) change the signature of the action method from
public ActionResult ListCompanies(string filter)
to
public ActionResult ListCompanies(string id)
allowing the first route to match the parameter on name.
3) Since you can't overload methods you'll need to use your ActionName when requesting the filtered result,
http://localhost:21047/Home/ListCompaniesFilter/sera
First these routes are exactly the same:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Home", // Route name
"{controller}/{action}/{filter}", // URL with parameters
new { controller = "Home", action = "ListCompanies",
filter = UrlParameter.Optional} // Parameter defaults
);
They are exactly the same because the number of parameters exactly match (what you name parameters nor the parameter type does not change the signature of the http request method).
There are few different ways to make this work. You could write a MapRoute Constraint for Default that would prevent it from working if the controller/action/id matched a specific set of criteria. Probably not the best course of action.
Since
http://localhost:21047/Home/ListCompanies
http://localhost:21047/Home/ListCompanies/sera
http://localhost:21047/Home/ListCompanies/text
http://localhost:21047/Home/ListCompanies/search
are programmatically the same request with {id} having a null value for your method ListCompanies() I would probably write it like:
public ActionResult ListCompanies(string id)
{
var query = db.AY_COMPANIES;
if (!string.IsNullOrWhiteSpace(id))
{
query.Where(c => c.COMPANY_FULL_NAME.StartsWith(id))
}
var model = query.ToList();
return View(model);
}
And remove your "Home" MapRoute, it won't be needed.
What happens if you have only one action with an optional parameter? Something like as the following:
public ActionResult ListCompanies(string filter = null)
{
var filteredCompanies = string.IsNullOrEmpty(filter)
? db.AY_COMPANIES
: from c in db.AY_COMPANIES
where c.COMPANY_FULL_NAME.StartsWith(filter)
select c;
return View(filteredCompanies);
}
I would like to have an Index action with an optional string parameter. I'm unable to make it work.
I need the following routes:
http://mysite/download
http://mysite/download/4.1.54.28
The first route will send a null model to the Index view, and the second one will send an string with the version number.
How can I define the route and the controller?
This is my route definition:
routes.MapRoute(
name: "Download",
url: "Download/{version}",
defaults: new { controller = "Download", action = "Index", version = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And this is the controller:
public ActionResult Index(string version)
{
return View(version);
}
Why does this not work? I'm not an expert in ASP MVC but this seems to be a very simple problem.
The error
When I go to http://mysite/downloads it works fine
When I go to http://mysite/downloads/4.5.6, the controller is correctly called, and the parameter is correctly passed. But then seems that the view is not found. This is the error I found:
string? will not work because string is not a value type.
You can set a default value to your parameter:
public ActionResult Index(string version="")
{
return View(version);
}
The issue is fixed passing the parameter to the view in the following way:
public ActionResult Index(string version)
{
return View((object)version);
}
Or
public ActionResult Index(string version)
{
return View("Index", version);
}
When you pass a string model to the view, if the model is a string parameter, it is interpreted as the view name due to the following overload method
View(String viewName)
Your Download route is conflicting with your Default route. Comment out the Download route and it will probably work.
BTW you can install RouteDebugger to figure out these kind of problems for yourself.
Your controller is looking for a view with the same name as the version attribute entered in the url (e.g. 4.1.54.28). Are you intentionally looking for a view with that name, in which case it should be in the Views/Download folder or your project. If however you simply want to pass it to the default view as a variable to be used on the page your best off sticking it in a model or you can just stick it in ViewBag if it's a one off.
Also you don't need to use:
Public ActionResult Index(string version)
You can use routedata instead e.g.
Public ActionResult Index()
{
string version = RouteData.Values["Version"].ToString();
ViewBag.version = version;
return View();
}
Hope this of some help
You are not set action name in url like {action}
you can try:
routes.MapRoute(
name: "Download",
url: "Download/{action}/{version}",
defaults: new { controller = "Download", action = "Index", version = UrlParameter.Optional }
);
I'm pretty sure it's because in the View you state it is an optional parameter, but your controller says that it is mandatory. Change the signature of your index method to expect a nullable param
public ActionResult Index(string? version)
{
return View(version);
}
Why not have two methods in your download controller:
public ActionResult Index()
{
return View();
}
[HttpGet, ActionName("Index")]
public ActionResult IndexWithVersion(string version)
{
return View(version);
}
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.
Using the default route provided, I'm forced to name my parameters "id". That's fine for a lot of my Controller Actions, but I want to use some better variable naming in certain places. Is there some sort of attribute I can use so that I can have more meaningful variable names in my action signatures?
// Default Route:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
// Action Signature:
public ActionResult ByAlias(string alias)
{
// Because the route specifies "id" and this action takes an "alias", nothing is bound
}
Use the [Bind] attribute:
public ActionResult ByAlias([Bind(Prefix = "id")] string alias) {
// your code here
}
This still works, your query string will just look like "/Controller/ByAlias?alias=something".
You can customize the routes with whatever identifiers you like..
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{alias}", // URL with parameters
new { controller = "Home", action = "Index", alias = "" } // Parameter defaults
);
Edit: Here's an overview from the ASP.NET site
Just because your route uses the name "id" for the ID variable doesn't mean that you have to use the same name in your controller action methods.
For example, given this controller method...
public Controller MailerController
{
public ActionResult Details(int mailerID)
{
...
return View(new { id = mailerID });
}
}
...and this action method call from the view...
<%= Html.ActionLink("More Info", "Details", new { mailerID = 7 }) %>
...you can use whatever naming convention you wish for the ID parameter in your controller action methods. All you need to do is resolve the new name to the default, whether it's "id", "alias", or whatever.
The above example should resolve to :
More Info