How to accept email parameter in ASP.Net MVC 3 - asp.net-mvc-3

I'm using ASP.Net MVC 3 and am trying to pass an email address as a parameter in a URL like this:
www.myapp.co.uk/customers/changedetails/john#doe.com
The parameter value is null when passed in. If I use parameters then it works;
www.myapp.co.uk/customers/changedetails/?email=john#doe.com
My controller looks like this:
public class CustomerController {
[HttpGet]
public ViewResult ChangeDetails(string email)
{
var model = GetModel(email);
return View(model);
}
}
My register routes looks like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
What am I missing to be able to use the email as the {id} parameter (www.myapp.co.uk/customers/changedetails/john#doe.com)?:

Don't have e-mail as last route param. Or if you do, add a trailing slash.
/my/route/ee#mail.com -> fail
/my/route/ee#mail.com/ -> success
/my/ee#mail.com/route -> success
Reasoning behind this is quite complicated but here's a good article about these things http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx
What I am sure of is, that many times if you have e-mail as last without trailing slash, the request won't even reach the asp.net pipeline. It looks like a file request. That being said, It's *.com extension looks indeed very dangerous. Having an # in the file name certainly does not decrease it's suspiciousness.
You might get it working. You might need to loosen the security in order to do so but it almost certainly will break at some point.
So, the best option is to keep it as query string parameter. Second best option to trail it with a slash.

You need to add another route:
routes.MapRoute(
"ChangeEmail",
"customers/changedetails/{email}",
new { controller = "Customers", action = "ChangeDetails", email = UrlParameter.Optional }
);
The URL parameter name needs to match the action method parameter name.

You can try add the following code into your web.config.
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
This is because the IIS Express think ".com" as an extension and looks for related module to handle it. However, there isn't related module, so it call the static module to handle it. Then it will search for a static content file path "/customers/changedetails/john#doe.com", then it report the 404 not found error.
Add above code in your web.config will make your request contain email handled by MVC module instead of StaticFile Module.

Related

MVC3 MapRoute to convert aspx query params into the route

I'm working on a project to rewrite an aspx site as MVC3. I want to make the old URLs work on the new site. I have named my controllers and actions such that the URLs actually contain enough info in the query string to route correctly but I'm having trouble getting the routing to work since it doesn't like the ? in the URL.
Basically I have old URLs like this:
www.example.com/Something/SomethingElse/MyPage.aspx?Section=DetailSection&TaskId=abcdef
I tried to create a route using:
routes.MapRoute(
"OldSite",
"Something/SomethingElse/MyPage.aspx?Section={action}Section&Id={id}",
new { controller = "Task", action = "Index", id = UrlParameter.Optional }
);
I want it to route to the correct new URL which is:
www.example.com/Task/Detail/abcdef
I know that all traffic to the MyPage.aspx page should go to my new Task controller and the beginning of the Section parameter always matches one of a few corresponding actions on that controller.
Unfortunately I have found that I get an error that a route can't contain a question marks. How should I handle this? Would it be better to use URL rewriting? Because this is a private site I'm not concerned with returning permanent redirects or anything - no search engine will have links to the site anyway. I just want to make sure that customers that have a URL in an old email will get to the right page in the new site.
In this one case I think the simplest way would be to have your old page mapped to a route:
routes.MapRoute(
"MyPage",
"Something/SomethingElse/MyPage.aspx",
new { controller = "Task", action = "MyPageHandler" }
);
And have this route mapped to an action method in TaskController:
public ActionResult MyPageHandler(string section, string taskId)
{
if (section.Contains("Detail"))
{
// execute section
}
}
This way you're treating your old site's query string for what it is: a query string. Passing those parameters straight into an action method is the most MVC-y way to interpret your old site.

MVC3 Routing Basics

I am learning MVC routing. Hope my question doesn't look silly, and please help :)
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
);
Msdn reference says it takes a String, String, Object, so I try to make a small change (added a "my" in front of everything just to mod the names and see if it works):
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{myresource}.axd/{*mypathInfo}");
routes.MapRoute(
"myDefault", // Route name
"{mycontroller}/{myaction}/{myid}", // URL with parameters
new { mycontroller = "Home", myaction = "Index", myid = UrlParameter.Optional } // Parameter defaults
);
It doesn't work any more. What is the format of these strings in "{}" curly brackets, and the anonymous object value formats.
{controller}/{action}/{id}
/Products/show/beverages
{table}/Details.aspx
/Products/Details.aspx
blog/{action}/{entry}
/blog/show/123
{reporttype}/{year}/{month}/{day}
/sales/2008/1/5
{locale}/{action}
/US/show
{language}-{country}/{action}
/en-US/show
{controller}/{action}/{id}
http://server/application/Products/show/beverages
{resource}.axd/{*pathInfo}
http://server/application/WebResource.axd?d=...
I google'd around, but all the posts seem to assume I know the formats, and couldn't find any detail explanation.Do they have to be fixed names like {controller} {action} {id} etc, or they won't work? Does the default anonymous object value names need to match them, too? Further, what does the "*" mean in {*pathInfo} I couldn't find the explanation for that, neihter. Thank you.
First, we need some definitions here.
Let's break down the default route.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
In this case, Default, on line 2, is merely a text name used to identify the route.
On line 3 is the url pattern. This defines how the route is matched. the things in braces are placeholders. They map to parameter names. so {controller} maps to the controller name. {action} maps to action name, and {id} maps to parameter called id.
On line 4 is the defaults object. This object supplies default values when they cannot be inferred from the url.
So, if we take all that together, here are some conclusions we can draw:
the default objects only supply values when they cannot be inferred from the url string. Thus, when the incoming request is just /, the dfault values from line 4 are used for controller and action. If the incoming request is /Blah, then the default controller supplied on line 4 is ignored, and instead MVC looks for BlahController. However, since no action was specified, the default action from line 4 is used Index.
The key thing to remember here is that the values on line 4 are only used if nothing matches the url in line 3.
So, when you changed everything, you threw everything for a loop. It's a meaningless route, because nothing defines which controller or action to use, and those two values are required in order to complete the route. As such, MVC can't figure out what controller you want. Or action method for that matter.
Another example:
routes.MapRoute(
"Example",
"Home/{action}/{myid}",
new { controller = "NotHome", action = "Index", myid = UrlParameter.Optional }
);
Because there is no {controller} placeholder in the url, then the default of "NotHome" is used, which makes MVC look for NotHomeController. So a url of /Home/About/3 would mean that controller = "NotHome", action = "About", and myid = 3.
All in all, in an incoming route, something must fill in the value for controller and action at a minimum. id is optional, and can be renamed to whatever you like. But, something must set controller and action parameters or MVC doesn't know how to route things.
Also, remember, the default route (or an effective default route) must come last in the list, otherwise no other routes will be matched.
The {*pathInfo} bit is called a slug. it's basically a wildcard saying "everything after this point is stuffed into a parameter called pathInfo". Thus if you have "{resource}.axd/{*pathInfo}" and a url like this: http://blah/foo.axd/foo/bar/baz/bing then two parameters get created, one called resource, which would contain foo and one called pathInfo which contains foo/bar/baz/bing.

mvc3 IModelBinder and url

I'm having a problem using iModelBinder with url in the format of
http://localhost/controller/action/id/value
the action would be the function in the controller
the id/value is ie. id=12
When I try the above link i receive a 404 error page not found, and looking at the stack I can understand that MVC is looking for a path it does not understand.
using the following works
http://localhost/controller/action?id=value
If anyone as any idea if this problem can be resolved, I would really like to be able to use "/" as separators.
Vince
The url should really be in the format:
http://localhost/controller/action/id
For example:
http://localhost/products/index/1
And the id should then be specified in the controller action. For example:
public ActionResult Index(int id)
{
...
The route specified in the global.asax file will specify the format of the url. For the above url the default route will suffice:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Then the default model binder will automatically bind your id (i.e. 1 in the above url) to the int id in the action.
Like Adam was suggesting, I don't think you should specify the name of the id in the url as it is automatically bound to for you by the default model binder.

MVC3 Area +Authorize attribute + Role strange issue

I really don't know what title should I use to describe my problem. To simplify my problem. Here is my test. I create a mvc3 site from scratch. I then add area called "admin". Inside admin, I have a controller named "Search" and has "Authorize" attribute decorated. I then changed my Global.ascx.cs route setting to append my controller namespace. Now I start my test.
Question 1
When I am accessing to http://localhost:xxx/Search page, it redirects me back to /Account/Logon page, it makes me confuse first, why it redirects me to logon page? it shouldn't reach to Admin search controller at all as I understand. If I removed the Authorize attribute, it display the yellow screen said can't find the view as I expected.
Question 2
If I add Authorize attribute with role, e.g. (Roles="Admin"), then I try access to Search page again, no matter login succeed or not, I always get redirect back to logon page. Why it doesn't give me the yellow screen, coz I am trying to request the search controller index view in the main site not the admin area's one. quite confuse.
I am a newbie in MVC development, can someone give me a solution regarding to my problem?
Thanks
Global.ascx.cs
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 },
new string[]{"TestAreaRouting.Controllers"}
);
}
You could constrain the default controller factory to look only inside the specified namespace for controllers in the RegisterRoutes method of Global.asax by setting the UseNamespaceFallback data token to false:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "TestAreaRouting.Controllers" }
).DataTokens["UseNamespaceFallback"] = false;
}
If you don't do this when you request /search the admin area route doesn't match because the url doesn't start with the Admin prefix.
So it is the default route that matches. The default controller factory starts scanning the assembly for a class called SearchController that derives from Controller and since it finds one it instantiates it and uses it to serve the request. Obviously it doesn't find a corresponding Index view because it looks in ~/Views/Search/Index.cshtml which obviously doesn't exist. The actual view is located in the area.
Now that we have constrained the controllers to their respective locations you could decorate them with the Authorize attribute and it should behave consistently.

Routes and Url helpers confusion

I'm a little confused on the MVC front for this reason, I have the following default route defined;
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
When I use a Url helper, i.e.
#Url.Action("MyAction")
it generates this url;
/?action=MyAction&controller=MyController
and never finds my action method. How are the Urls generated by the helpers and how do I correct this?
Just use the overload to specify the action and controller:
#Url.Action("MyAction", "MyController")
If you use the overload which only takes the action name, the controller is taken from the current route data. Default routing doesn't come into it.
i.e.
#Url.Action("MyAction")
is equivalent to
#Url.Action("MyAction", (string)ViewContext.RouteData.Values["controller"])
I had the same issue, I had a web app that was built used WebForms and slowly migrating parts to MVC, to support both I had a route entry which ended up breaking the routing evaluation code and caused the funny action urls
this blog post fixed my issue

Resources