ASP.NET MVC, email address as parameter breaking routes - asp.net-mvc

I have the following actionresult:
public ActionResult Confirmation(string emailAddress)
When I try to access it:
http://localhost:8080/Signup/Confirmation?emailAddress=test%40test.com
I get this:
The view 'test#test.com' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Signup/test#test.com.cshtml
~/Views/Signup/test#test.com.vbhtml
What gives why isn't it looking for the correct view? If I go to "/SignUp/" it correctly shows me the index, along with the other ActionResults working correctly. Why does an address break it?

You shouldn't be passing that info in the URL anyway.
If this is kind of a "Confirmation" page from a signup, you could pass another identifier, e.g the UserId that has just been created, then fetch it from the repo.
E.g:
[HttpPost]
public ActionResult Signup(SignupViewModel model)
{
//.. code to save.. etc
return RedirectToAction("Confirmation", new { id = newUser.UserId });
}
[HttpGet]
public ActionResult Confirmation(int id)
{
var user = repo.FindById(id);
// map to model, etc...
return View(model);
}
So your URL would be (without a specialized route)
http://localhost:8080/Signup/Confirmation?id=123213
Putting user's email addresses in the URL is asking for them to be spammed.

Have you tried registering the route in the global.asax.cs?
Something like:
routes.Add("confirmation",
new Route("Signup/Confirmation/{email}",
new RouteValueDictionary(new { controller = "Signup", action = "Confirmation", email = UrlParameter.Optional }),
new MvcRouteHandler())
);

Related

User ID in ActionLink

I'm developing an app, and after user have signed in i want to redirect them to a page where they can update their profiles, for this I want to use the ID of the user who is logged in to retrieve their details.
Since I'm trying to use their ID like that, i don't know if the best method is still to show the ID in the URL or actionLink, my Edit ActionResult is:
// GET: ProfileFormViewModel/Edit/5
public ActionResult Edit(int id)
{
//My code to load the user details
return View(profileFormViewModel);
}
Could this mean that if a user changes the URL they would get the other user's details?
And since i'm trying to retrieve the user ID from the logged in user, do I even need to receive a int parameter in the ActionResult Method, could i just leave it like:
// GET: ProfileFormViewModel/Edit/5
public ActionResult Edit()
{
//get logged in user
return View(profileFormViewModel);
}
public ActionResult Edit(ProfileFormViewModel profileFormViewModel)
{
try
{
if (ModelState.IsValid)
{
//Go save it
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
My route config is as follows, so if i don't send an int ID in the ActionResult link it will give me an error
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Could this mean that if a user changes the URL they would get the
other user's details?
The answer is yes. If you don't want this to happen, you can do this:
Check the input id with current logged in user id.
If they match, show him his Profile and let him make any changes as he wishes.
If they don't match, it means he's trying to view another profile. You can return an error code, or just redirect him to another page.
However, from my viewpoint, when a user try to view another user's profile by changing the id in the URL, just let him see it. All he can do is reading, editing is not allowed.
do I even need to receive an int parameter in the ActionResult Method?
Yes, you should. But to be clear, as I said: "just let him see other profile", you should separate this into 2 methods:
public ActionResult ViewProfile(int id)
{
// This method allows user to view his or another user's profile
}
public ActionResult Edit()
{
// This method is called when user clicks Edit in his profile.
// It returns a View for editing (data in some controls like textboxes, dropdownlist,...)
}

2 Actions For Same Route in Asp.Net MVC 3

I have route defined as
routes.MapRoute(
"Company", // Route name
"Company/{companyname}", // URL with parameters
new { controller = "Company", action = "CompanyDetail", companyname = UrlParameter.Optional } // Parameter defaults
);
Now the problem is that i have made this route if now i made any request to company controller and pass a parameter it goes to CompanyDetail method , but in one condition i dont want to send to this method i want to send the control to another action CallCompany . How to solve this and note i also need to run both type of request .
you can set it in your controller method:
public ActionResult CompanyDetail(string companyname)
{
if (condition)
{
return RedirectToAction("ActionName", new { companyname = companyname});
}
return View();
}
As I understood your question, you want to realise the following behavior:
There is as set of company names (for example, "test") and they correspond with URL
yourhost/Company/test
They should be routed to CallCompany.
The other URL (such as yourhost/Company/another_company) should be routed to CompanyDetail.
I think, that the best way is to do redirect in CompanyDetail method
public ActionResult CallCompany(string companyname)
{
return View();
}
public ActionResult CompanyDetail(string companyname)
{
IEnumerable<string> myCompanies = GetSpecialCompany();
if (myCompanies.Contains(companyname))
{
return RedirectToAction("CallCompany", new { companyname = companyname });
}
return View();
}
private IEnumerable<string> GetSpecialCompany()
{
throw new NotImplementedException();
}
you should probabaly look into mvc route constraints. that would enable you to forward request on the simillar url to different action depending uopn different parameters which you can programatically set.
for example
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"},
new {productId = #"\d+" }
);
this would only go to controller:Product and action Details in product id is an int
in your case you will have to define the pattern in regex for which request should go to one route and place the second route next to this
so automatically every request which dosent fit the constraint for this route will be handeled by the next one.

MVC3 URL alternatives

After reviewing A LOT of questions and Internet data, I've solved a problem of mine with getting URL parameter from MVC3 application correctly.
Thing is that there wasn't a fault in coding, but in routing (I'm not so good with routing...).
Here's the current issue.
http://localhost:51561/Report/Details/1
This is the way my application presents Report details, which is good. But when it does it like this, I can't get value from URL parameter, like this
Request.QueryString["id"]
But, when I manually type in URL http://localhost:51561/Report/Details?id=1 it works...
Thing is i like the first URL type, but I don't know how to get parameter from it...
Help, please...
Update:
My Controller actions:
public ViewResult Details(int id)
{
Report report = db.Reports.Find(id);
ViewBag.TestID = Request.QueryString["id"].ToString();
return View(report);
}
public ActionResult Show(int id)
{
Report report = db.Reports.Find(id);
var imageData = report.Image;
return (File(imageData, "image/jpg"));
}
My View:
<div class="display-label">Picture</div>
<div class="display-field">
<img alt="image" src="<%=Url.Action("Show", "Report", new { id = ViewBag.TestID })%>" width="200px" />
</div>
First of all, you shouldn't use Request.QueryString in your application. Apart from that, in the first URL, you don't have a query string, and thus you can't access it (also read this article on msdn about Request.QueryString).
I also would like to suggest you to go through the basic tutorial of ASP.NET MVC3, to be found here. Many things like your question are thoroughly explained there.
To answer your question now, in your first URL example, the 1 in the URL is a parameter of your action (the Details action). You have to add this parameter to your method (action):
public ActionResult Details(int id)
UPDATE:
You have apparently the right action (method) declaration. Now, you can just use the parameter id. So change the Request.QueryString["id"] just by the variable (parameter) id.
public ViewResult Details(int id)
{
Report report = db.Reports.Find(id);
ViewBag.TestID = id;
return View(report);
}
There is no need to apply ToString() on the id, you shouldn't make it when it isn't necessary (you might need it somewhere else, later or so). Just put it in the ViewBag as the original type.
Your Show() method is good :). You have now the id parameter as you needed. (Try to avoid too many parentheses, it makes it look messy and now so clear.)
public ActionResult Show(int id)
{
Report report = db.Reports.Find(id);
var imageData = report.Image;
return File(imageData, "image/jpg");
}
You're not supposed to use Request.QueryString["id"] in MVC
Just add id parameter to your ReportController.Details action:
public ActionResult Details (int id)
The above is assuming you have a default route setup in Global.asax:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

Simple MVC routing problem

I have few pages that quite similar to the others but one of them doesn't work.
When I write 'http://localhost:2265/Segment/' I get annoying error message "Server Error in '/' Application.
The resource cannot be found."
Other pages like 'http://localhost:2265/User/' works very well AND also 'http://localhost:2265/Segment/Create'. So Index of the Segment is the problem. I have used ASP.NET Routing Debugger and on other pages I get correct mappings, but I get this same error message "resource cannot be found" also when using debugger.. I think this indicates that Default route doesn't catch it either..
Any ideas?
Here is my MapRoute commands.
routes.MapRoute(
"Maintenance",
"Maintenance/{action}",
new { controller = "Maintenance", action = "Index", id = "" }
);
routes.MapRoute(
"User",
"User/{action}",
new { controller = "User", action = "Index", id = "" }
);
routes.MapRoute(
"Segment",
"Segment/{action}",
new { controller = "Segment", action = "Index", id = "" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
Update:
Thank you for the quick reply!
I removed all routes except the default. It didn't solve the problem, but now the route list is shorter.
I have other classes inside my controller file like this:
public class SegmentFormViewModel
{
}
public class SegmentController : Controller
{
}
public class SegmentFormCreateModel : Segment
{
}
I tried to move it inside controller, but that didn't help either.
Is there any way to debug this problem?
Update:
Here is my controller (without contents of the methods)
public class SegmentController : Controller
{
//
// GET: /Segment/
public ActionResult Index()
{
}
//
// GET: /Segment/Details/5
public ActionResult Details(Guid id)
{
}
//
// GET: /Segment/Create
public ActionResult Create()
{
}
//
// POST: /Segment/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
}
//
// GET: /Segment/Edit/5
public ActionResult Edit(int id)
{
}
//
// POST: /Segment/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection)
{
}
}
To match your /Segment/ route you will need a "SegmentController" controller class to have an "Index" action that accepts GET requests, ie not restricted with [AcceptVerbs(HttpVerbs.Post)].
As Darin has already commented, the default route would handle all the other routes you have added so you don't need the extra routes.
After update:
Your problem is probably more to do with the actions in the Segment controller. It doesn't matter what classes you have in which file. What actions do you have in the Segment controller?
After 2nd update:
Your actions look ok so I suspect the problem is in code you have not listed.
Next steps:
1. Use the router debugger already mentioned.
2. Download the MVC source so you can step through it.
Last resort: Start a brand new project and only add the Segment controller. Then keep adding related code until you find the problem.
Use Phil Haack's RouteDebugger. It'll tell you what was matched, which often clears the problem up pretty quickly.
It's really easy to set up: drop RouteDebug.dll in your /bin folder and make this change to your App Start event:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}

ASP.NET MVC - Navigation Approach

I am new to ASP.MVC. My background is in ASP.NET Web Forms, I think this is what is causing my confusion. I understand that the "M" basically represents the data source, the "V" represents the resource I'm requesting and the "C" dictates what gets shown to an end-user. But then I get confused.
For instance, I'm just trying to create a Login screen. I envision a user visiting "http://www.myapp.com/Account/Login" and they will be presented with a traditional Login screen. To accomplish this, I have added the following in the RegisterRoutes method in my Global.asax file:
routes.MapRoute(
"Login",
"{controller}/{action}",
new { controller = "Account", action = "Login", id = "" }
);
The Login action executes, but this is where I get confused. You see, the first time the login screen loads, I would expect to just show a username/password field. Then on post, I would expect the form to be validated and processed. In an attempt to do this, I have created the following method:
public ActionResult Login()
{
bool isFormValid = ValidateForm();
if (isFormValid)
LoginUser();
else
ShowErrors();
return View();
}
My confusion rests with the Login action. Initially, there is no data. But the next time, I want to validate the data. How do I determine if the Action is a postback or not?
Thank you!
The easiest way to handle this is with two actions: one for get, one for post. Use the AcceptVerbs attribute to control which gets invoked depending on the method. BTW, the default routes should work just fine for this case since when the controller and action is supplied it gets directed as you would expect. I thought that this scenario was also covered in the project template -- did you set up a project using the template or an empty one?
[AcceptVerbs( HttpVerbs.Get )]
public ActionResult Login()
{
}
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult Login( string userName, string password )
{
}
You need two different methods, for Post and Get.
[AcceptVerbs (HttpVerbs.Get]
public ActionResult Login ()
{
return View ();
}
[AcceptVerbs (HttpVerbs.Post]
public ActionResult Login (FormCollection form)
{
if (AuthenticationSuccess ())
return RedirectToAction ("Account");
else
return View ();
}
For Post version you can use the model binding mechanism:
public ActionResult Login (LoginModel loginModel)
Or
public ActionResult Login (string LoginName, string Password)

Resources