in ASP.NET MVC3 how can I see the request? - c#

I'm using a simple route as
routes.MapRoute(
"Default2", // Route name
"{cliurl}/{id}", // URL with parameters
new { cliurl = "none", controller = "ABook", action = "Index", id = "none" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{cliurl}/{controller}/{action}/{id}", // URL with parameters
new { cliurl = "none", controller = "ABook", action = "Index", id = "none" } // Parameter defaults
);
and when I debug the website (VS2010 SP1), I have a breakpoint in my ABook Controller, inside the Index action method witch contains only:
//
// GET: /ABook/
public ActionResult Index()
{
if (currentClient == null)
return RedirectToAction("Empty");
return View();
}
//
// GET: /Empty/
public ActionResult Empty()
{
return View();
}
The thing is that, when I insert this in the browser:
http://localhost:14951/client_name/hashed_id
I get 3 breaks in that breakpoint.
How can I see what in the world is going on? why 3 times when I just requested 1, what is exactly the browser requesting?
I can only get the Route Parameters and I do get the first correct, but 2nd and 3rd are using the default values, and I tried to navigate through the RequestContext and I can't see anything useful :(
Just want to know if there is a way to really see what's been requested.

I ended up using Glimpse
http://getglimpse.com/
http://www.balexandre.com/temp/2011-05-28_1854.png

If you have breakpoint inside controller you can use watch where you can simply create new watch. Type in Request and search it...

In every Controller there exists a property called Request. It is actually defined in System.Web.Mvc.Controller which is the superclass of all controllers. The property returns the acutal Request object as HttpRequestBase and exposes fields like InputStream, Headers, HttpMethod so on and so forth.
As for why you are hitting the index method 3 times, I'm sure that other requests made by the browser, say for example for images and javascript and other existing files, also are handled by your route defined. In short your route defenition is too generic and handles unexpected requests. You can correct this by using Route.IgnoreRoute("Path/to/Existing/Files") or by making your route more specific by adding RouteConstraints. Leave a comment if you want to know how to do that.

You can use fiddler to see what the browser requests or you could try the routdebugger download from Nuget.

I know others have sort-of made a stab at this... they are correct:
Use the Request object to find out what is being requested. It's probably something incorrectly being handled by your controller. Shovel some output while debugging from Request in that method, such as the raw url. That will likely answer the question.

As a suggestion, why not hook up the BeginRequest event handler for the application which will allow you to see every request coming through. There is also the HttpContext.Current.Request.Url object which can be inspected
// Global.asax
public MvcApplication()
{
BeginRequest += new EventHandler(MvcApplication_BeginRequest);
}
void MvcApplication_BeginRequest(object sender, EventArgs e)
{
Debug.WriteLine("[Start] Requested Url: " + HttpContext.Current.Request.RawUrl);
}

Related

ASP.NET MVC5 - append querystring to all URLs

My team is building a simple MVC site for very low end "feature" phones. One problem we have is that certain phone aggressively cache HTML, so what the user actually gets to see is not what we intend. An extreme example is, a user registers and gets a "thank you page". Another user on the same device then tries to register. The phone simply serves the cached page without creating a new account.
In the past I've dealt with this by adding a "cache buster" querystring to things I don't want cached, eg all pages will be served in the format
http://domain.com/controller/route?cb=somerandomstringofnumbers
In this case we'd need to do this for all URLs in the site - this includes the URLs auto-generated by Controller actions such as RedirectToAction or Redirect and also the Razor Url.Action, Html.BeginForm, Html.ActionLink etc.
Now obviously I could decorate the Razor HTML helpers (or extend them) and add the argument to an controller action, but it seems to me that because the actual URLs generated by these built in methods are auto-generated from the Controller/Action params passed in, there should be a way to hijack that process.
Unfortunately the MS classes are protected - I'm mostly looking in System.Web.Routing.Routes.
I've tried a few things I've found online but they are not MVC5 (dating back to 2008) and it seems the framework has changed significantly.
eg, from
http://forums.asp.net/t/1216840.aspx?Append+value+to+all+urls+built+by+RouteCollection+GetUrl
public class SessionAppendingRouteHandler : IRouteHandler
{
public IHttpHandler GetHandler(RequestContext context)
{
SessionAppendingHttpHandler handler = new SessionAppendingHttpHandler();
handler.RequestContext = context;
return handler;
}
}
public class SessionAppendingHttpHandler : MvcHandler
{
public override ProcessRequest(RequestContext context)
{
//append your sid here
}
}
// and in the route setup
RouteTable.Routes.Add( new Route
{
Url = "/[controller].mvc/[action]/",
Defaults = new { action = "index" },
RouteHandler = typeof(SessionAppendingRouteHandler)
});
This I cant get to work as the framework has changed too much, but it looks very close to what I would like to achieve.
I feel like I'm in the right area, but I've hit a brick wall.
Any suggestions?
This is quite old, but let me answer based on how I solved a similar problem:
Instead of having it as query string, have the cb as a route value just as action and controller are route values. You can do this by registering a route; for instance:
routes.MapRoute(
name: "CB",
url: "{cb}/{controller}/{action}/{id}",
defaults: new { cb = "3600", area = "", id = UrlParameter.Optional }
);
If the value for cb is not a constant, then you can find a convenient point to set the cb for each user session. A good place will be after a successful login. With this, you'll now need to provide just two custom methods for RedirectToAction and ActionLink. Your implementation will simply package a RouteValueDictionary and then pass it in to MVC's own implementation using the appropriate overloads.

RedirectToRoute causes redirect loop

public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToRoute("Home");
}
I expect this action to redirect the user to the homepage but instead, a redirect loop occurs (according to Chrome).
The aforementioned action belongs to a controller in the "admin" area as where the "Home" route is defined for the default area - I suspect this to be relevant.
Here is the route for good measure:
routes.MapRoute(
"Home",
"{controller}/{action}/{slug}",
new { controller = "Posts", action = "Index", slug = UrlParameter.Optional },
new[] { "GoBlog.Controllers" }
);
Update
Replacing the return statement with the following will cause the action to work as expected:
return RedirectToRoute("Home", (RouteTable.Routes["Home"] as Route).Defaults);
I want to know why this is the case. Ideally I can omit the (cumbersome) second argument.
This issue is particular to the call RedirectToRoute(string) and your particular route:
routes.MapRoute(
"Home",
"{controller}/{action}/{slug}",
new { controller = "Posts", action = "Index", slug = UrlParameter.Optional },
new[] { "GoBlog.Controllers" }
);
What you expected was RedirectToRoute would populate your arbitrary route definition of {controller}/{action}/ with the route's defined defaults:
controller = "Posts", action = "Index"
But RedirectToRoute like all redirect methods, is a part of your controller, and uses your current Controller and Action as Default Values where ever possible. This is a "feature" of the ASP.NET MVC framework -- re-using routes to create new routes. This approach is valuable because your current Controller and Action are strong hints as to your intention.
A great example of this is RedirectToAction(string) which assumes your current instantiated Controller is the default.
Contrast this with:
return RedirectToRoute("Home", (RouteTable.Routes["Home"] as Route).Defaults);
Rather than directing the framework to use it's best guess about how to populate the ambiguous wild card route, you have specifically instructed the framework to use the Defaults from the RouteTable for "Home" route to construct a route and return it as a RouteObject.
When you call RedirectToRoute(string)
This method converts the route name that is passed in routeName to a URL by using the RouteCollection.GetVirtualPath method.
where pass null as parameter RouteValueDictionary. So in this case this parameters was getting from current RequestContext with values for controller, action and etc, i.e. you get url like this
"{controller}/{action}/{slug}"
"Login/Logout/"
and got redirecting loop.
when you call
return RedirectToRoute("Home", (RouteTable.Routes["Home"] as Route).Defaults);
instead RedirectToRoute(string) used RedirectToRoute(String, RouteValueDictionary) and in RouteCollection.GetVirtualPath method you pass defaults values for that route.
It is a general best practice in MVC to allow your controllers as much control as possible. There are multiple controller methods which you can use, and you should generally use the one that is the closest fit to the process you are performing.
return View(). tells the controller to generate and return the specified HTML, similar to a Server.Transfer(). Use for matching HTML with Actions, or when delivering static HTML without interest in the URL string.
Return RedirectToAction(). Tells the controller to build a new URL string based on the Action. This will parse the route dictionary for the Action, and handle the defaults. It acts as a Response.Redirect(), and will return a new generated URL. In general, this is the most commonly used redirect. This also has the benefit of not changing if the route dictionary is changed.
return RedirectToRoute(). Similar to RedirectToAction(), however tied to the Route Dictionary. This will search the Route Dictionary for an exact route match, using whatever route parameters are passed. This does not look for Actions, and thus does not build a route or inject default values. Used when an exact route with specific non-default values must be processed.
return Redirect(). This redirect requires a full URL, and will redirect to the new URL. Usually used when redirecting to a URL on a totally different domain.

RedirctToAction takes me to base controller of the present controller instead of directed controller

I have two controllers named as forms and mobile, In the forms controller I had a return statement calling a action in mobile controller like below:
public class formscontroller: customcontroller
{
public ActionResult submit(int? id = null, string jsString = null)
{ code code
if (CSConfigurationMgr.IsMobileUrl(Request.UrlReferrer.AbsoluteUri.PathFromURL()))
return RedirectToAction("home", "mobile", new { success = true });
return code
}
}
home is an action
public ActionResult home(string res)
{
code code
}
But I am not able to hit that function, it is directly taking me to the base controller of forms, which is named as custom.
Is there a work around?
It is possible that "condition" never evaluates to true.
Please check that and ensure that the code execution gets to that point.
Might be more helpful if you post both controllers here (if they aren't too large).
It seems like a routing problem to me.
Maybe you haven't defined a route to your mobile/home/res controller and then the routing engine is redirecting you to your default one param route?
Assuming home is the action on the mobile controller, then perhaps your signature is incorrect on the home action because it declares a string argument and you're sending a boolean.

ASP.NET MVC route handling for misspelled URLs?

How can we create a route that will detect that the URL matches a route but that the parameter to that route is wrong?
I've had several cases recently where a user has mistyped a URL and it has been hard to debug because the route table doesn't handle these misspellings very gracefully:
This route handles the URL /Widgets/guid
routes.MapRestfulRoute(
"WidgetsItem",
"Widgets/{idString}",
new { controller = "Widgets", action = "Item" },
new { idString =
#"^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$"}
);
routes.MapRoute("WidgetsList", "Widgets", new { controller = "Widgets", action = "List" });
routes.MapRestfulRoute("CatchAll","{*url}", new {controller = "Error", action = "Http404"});
And the user entered the URL /Widgets/25246810-4b60-4db8-8567-8db8826 which looks correct but is not (there's only 28 characters in that guid provided).
Because the URL didn't match the route, the response was 404. But this is deceptive and caused a debugging problem: it looks like the Widgets/Item action is returning 404. And we spent hours trying to figure out what was wrong with that action, when in fact the action wasn't even called. It wasn't until I installed RouteDebugger that I noticed the guid was malformed.
How can we create another error route that will detect that the URL does actually match a route but that the parameter to that route is wrong?
EDIT: I can "fix" it by adding a new route that matches /Widgets/anything, and placing that route after the normal route:
routes.MapRestfulRoute(
"WidgetsItemError",
"Widgets/{idString}",
new { controller = "Widgets", action = "Item" },
new { idString = #"^.+$" } // match if it has any param at all
);
But this seems to be very inefficient - I will have to add a similar error route for every single route eg /Foo, /Bar /FooBar etc (and I've got quite a lot of routes). Isn't there a more generic way to do this?
When I dealt with guid's in my routes, I would accept a string, try to parse the string value to a guid, if it didn't parse, i'd return a different view with a message indicating that the id was invalid and to try again
The only thing you should have to change here would be to remove the route constraint and change your Action to accept a string instead of a guid (unless your action is already accepting a string)
The easiest solution is to remove the route constraint, then check for a null parameter in your controller and do whatever you want from that point.

How to define this route in ASP.NET MVC?

I have a controller named Movie, with an action named ByYear, which takes the year as a parameter :
public ActionResult ByYear(int year)
{
ViewData["Title"] = string.Format("Movies released in {0}", year);
var repository = MvcApplication.GetRepository();
var movies = repository.Medias
.OfType<Movie>()
.Where(m => m.Year == year);
return View("Index", movies);
}
I'd like to access this action with the following URL : /Movie/ByYear/{year}, but the only valid route for this action is this : /Movie/ByYear?year={year}.
I tried to add new routes in my application's RegisterRoutes method, but I can't find a way to get the desired result...
Could anyone tell me how to achieve that ?
Note: this is actually very similar to this question, but no answer was accepted, and the highest voted answer makes no sense to me as I'm completely new to MVC...
Change the name of your parameter year to id and this will match the default route that MVC adds to your project.
So for further clarification, let's take a look at the default route added by ASP.NET MVC:
routes.MapRoute(
"default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
In this route you can see three tokens that are named specifically for controller, action, and the third token which is passed to the action is id. When a request comes into your application, ASP.NET MVC will analyze the routes that are currently mapped and try to find a method signature that matches them by using reflection against your controllers.
When it looks at your Movie controller, it sees an action called ByYear, however that method takes an integer called year, not id. This is why you end up with something like /Movie/ByYear?year={year} when you create an ActionLink for that particular Action. So to fix this, you have two options:
The first and most simple method to fix this is to just change the method signature for your Action to accept a parameter named id which is what I recommended above. This will work fine, but I can see where it might cause a little bit of confusion when you go back to that source later and wonder why you called that parameter id.
The second method is to add another route that matches that method signature. To do this, you should open your Global.asax and just add the following (untested, but should work):
routes.MapRoute(
"MoviesByYear",
"Movies/ByYear/{year}",
new { controller = "Movie", action = "ByYear" }
);
This route is hard-coded, yes, but it won't break the other routes in your system, and it will allow you to call the method parameter year.
EDIT 2: Another thing to note is that the routing engine will stop on the first route it finds that matches your request, so any custom routes like this should be added before the default route so you are sure they will be found.
OK, I just found out how to do it. I just had to create the new route before the default route... I didn't think the order had any significance
routes.MapRoute(
"MovieByYear", // Route name
"Movie/ByYear/{year}", // URL with parameters
new { controller = "Movie", action = "ByYear" } // Parameter defaults
);
EDIT: Isn't there a simpler way ? (not involving renaming the parameters). I'd like to be able to do something like that :
[Route("/Movie/ByYear/{year}")]
public ActionResult ByYear(int year)
{
...
Design considerations aside, if you did not want to rename the parameter, you could add something like the route below, which enforces having the year parameter
routes.MapRoute(
"MovieByYear", // Route name
"Movie/ByYear/{year}", // URL with parameters
new { controller = "Movie", action = "ByYear" },
new { year = #"\d+" } // Parameter defaults
);

Categories