Add parameter to all Incoming/Outgoing URL's - c#

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.

Related

ASP.NET MVC redirect to action Redirect Loops

I am learning asp.net MVC and I was trying Redirect to action and I tried the following code and I keep getting Redirect Loop Error.
This is the Controller class in which I am getting an error
public class CuisineController : Controller
{
// GET: Cuisine
public ActionResult Search(string name)
{
return RedirectToAction("About","Cuisine");
}
public ActionResult Index()
{
return Content("This is Index");
}
public ActionResult About()
{
return Content("this is About");
}
}
I have also created a route of my own other than the Default route
routes.MapRoute(name: "Cuisine",
url: "cuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search", name = UrlParameter.Optional });
When I try to access the Cuisine controller it gives me a redirect loop error.
Thank you
In your routeConfig, you have a route defined for "cuisine/{name}" and it will be sent to the Search action method.
In your Search action method, You are redirecting to the About, Which is cuisine/About. This is matching with the route you defined cuisine/{name} so it will send the request to Search action again. The same process will keep running. That is why you are getting the redirect loop
You should either delete this specific route you defined or rename the url pattern for cusine search to prevent the redirect loop.
routes.MapRoute(name: "Cuisine",
url: "searchcuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search",
name = UrlParameter.Optional });
This should be pretty obvious:
routes.MapRoute(name: "Cuisine",
url: "cuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search", name = UrlParameter.Optional })
Says all urls that start with cuisine/ use the Search method on the CuisineController.
/Cuisine/About starts with that url, so it willl always use the Search method.
It looks like you want to make {name} part of URL. You can use attribute routing instead of changing default routing, it does not have such global "destructive" effects (explained in other answers) at least, and looks it's what you are actually after :)
[Route("Search/{name}")]
public ActionResult Search(string name)
{
return RedirectToAction("About", "Home");
}
See more about attribute routing here (for this to work, don't forget to add routes.MapMvcAttributeRoutes() if it is not there yet).
You need to restore controller action pattern. Just change url: "cuisine/{name}" to url: "cuisine/{action}/{name}"

How to set up routing so that Index does show?

So I know google can penalize a site if you have the same content on multiple urls... unfortunately, in MVC this is too common i can have example.com/, example.com/Home/ and example.com/Home/Index and all three urls would take me to the same page... so how do I make sure that whenever Index is in the url, that it redirects to the same without the Index and of course the same thing with the Home
Perhaps this little library may be useful for you.
This library is not very convinient in your case, but it should work.
var route = routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.Redirect(r => r.MapRoute("home_index", "/home/index")).To(route);
routes.Redirect(r => r.MapRoute("home", "/home")).To(route);
The way I handle this is for default pages like Index is to simply create an explicit route for only one of them. I.e. "example.com/People" would be the route for People/Index, and there would be no valid page at the url "/example.com/People/Index".
The Home example is unique in that it has potentially three different URLs. Again in this case I'd simply create a route for "example.com" for that Index action, and not support the other two urls. In other words, you would never link to the other forms of the URL, so their absence should never cause a problem.
We use a Nuget package called AttributeRouting to support this. When you specifiy a GET route for a page, it overrides the defaults for MVC.
Using AttributeRouting usually you'd map the index to [GET("")] but for the special case of Home where you also want to also support the root URL that omits the controller name , I think you'd also add an additional attribute with IsAbsoluteUrl:
public class HomeController : BaseController
{
[GET("")]
[GET("", IsAbsoluteUrl = true)]
public ActionResult Index()
{...
So I found a way to do it without any external Library...
In my RouteConfig I had to add these two routes at the top, just below the IgnoreRoute
routes.MapRoute(
"Root",
"Home/",
new { controller = "Redirect", action = "Home" }
);
routes.MapRoute(
"Index",
"{action}/Index",
new { controller = "Redirect", action = "Home" }
);
Then I had to create a new Controller called Redirect and I created a method for each of my other Controllers like this:
public class RedirectController : Controller
{
public ActionResult Home()
{
return RedirectPermanent("~/");
}
public ActionResult News()
{
return RedirectPermanent("~/News/");
}
public ActionResult ContactUs()
{
return RedirectPermanent("~/ContactUs/");
}
// A method for each of my Controllers
}
That's it, now my site looks legit. No more Home, no more Index in my URLs, this of course has the limitation of not being able to accept parameters to any of the Index methods of your Controllers though if it was really necessary, you should be able to tweak this to achieve what you want.
Just an FYI, if you want to pass an argument to your Index Action, then you can add a third route like this:
routes.MapRoute(
name: "ContactUs",
url: "ContactUs/{id}/{action}",
defaults: new { controller = "ContactUs", action = "Index", id = UrlParameter.Optional }
);
This will create a URL like this: /ContactUs/14

Redirect to another controller/Action not registered in route

iam having controller like below which is not registered in routes table
public class InternalController : Controller
{
/*this controller is not registered in routes table*/
public ActionResult Foo()
{
return Content("Text from foo");
}
}
From another controller which is registered in Routes table i want to call/redirect action of previous controller, one which is not registered in routes table.
public class AjaxController : Controller
{
/*this controller is registered in routes table*/
public ActionResult Foo()
{
/*FROM HERE HOW DO I RETURN CONTENTS OF
controller=InternalController, action = Foo
*/
/*
i tried below piece of code but that doesnt seem to work
*/
return RedirectToAction("Foo", "InternalController ");
}
}
Defined Routes (only one item added)
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Ajax","ajax/{action}",new {
controller="Ajax",
action="Index"
});
}
If you are choosing not to register a route... then you probably have the file/controller in a specific location that will not change.
In that event, just use the "Redirect" method, instead of "RedirectToAction".
For example:
return Redirect("~/Internal/Foo");
Now that you have shown your routes definition, it is obvious that you can never invoke any other controller than AjaxController. You simply forbid them in your routes, so InternalController could never be served. You will have to change your route definition.
Depending on what you want to achieve and how you want your urls to look like you have a couple of possibilities:
Leave the default route
Modify your existing route definition like so:
routes.MapRoute(
"Ajax",
"{controller}/{action}",
new { controller = "Ajax", action = "Index" }
);
You can create RedirectController for redirecting more Url and pages:
public class RedirectController : Controller
{
public ActionResult Index()
{
var rd = this.RouteData.Values;
string controller = rd["controller2"] as string;
string action = rd["action2"] as string;
rd.Remove("controller2");
rd.Remove("action2");
rd.Remove("controller");
rd.Remove("action");
return RedirectToActionPermanent(action, controller, rd);
}
}
And then you can define redirect from old url in routing tables:
routes.MapRoute(
null, // Name
"ajax/foo",
new { controller = "Redirect",
action = "Index",
controller2 = "InternalController",
action2 = "Foo"}
);
This pattern is also useful if you redirect old url to new one. For example:
routes.MapRoute(
null, // Name
"default.aspx", // redirect from old ASP.NET
new { controller = "Redirect",
action = "Index",
controller2 = "Home",
action2 = "Index" }
);

asp.net mvc pass a variable to another action method with a form post

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&sector=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" })) { } %>

Access data from Asp.Net MVC URL

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

Categories

Resources