MVC4 Action only triggers while debugging locally - asp.net

I have an action I use to reset the password for my users:
public class Password : Controller{
public ActionResult Reset(string id)
{
//...
}
}
The id is actually the encrypted email address provided by the user when asked which email address the password recovery is for.
So, if I click on the "Forgot Password?" link, and provide the email address user#domain.com the resulting url sent to the user's email would be something like:
http://www.myapp.com/admin/password/reset/euPdxABQEgE0JDuv6OHSLnk1QBYf73YseBUZwR9+MJA=
That would be routed to the Reset action on the Password controller included above.
This is working fine while I'm debugging the app locally.
This is NOT working on my test server; I'm getting a 404 instead.
Interesting facts:
If I remove the Id from the url
http://www.myapp.com/admin/password/reset/ I get redirected to the
index action of the password controller with the proper message
"Invalid link" as expected.
I if include any other string in the id (ie: http://www.myapp.com/admin/password/reset/abc123) I get redirected as expected; as described in 1).
So it seems that this is somehow related with the anatomy of the id I'm passing around. I tried url encoding it but that didn't work.
This is the route definition for the Admin Area:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Login", action = "Index", id = UrlParameter.Optional }
);
}
The main routes are the default ones.
Any ideas?

I suspect that this is because of the + sign in the path portion of your url. You may take a look at the following blog post in which Scott Hanselman explains the difficulties that you will encounter by attempting to pass such special characters in the path portion.
I will quote only his conclusion:
After ALL this effort to get crazy stuff in the Request Path, it's
worth mentioning that simply keeping the values as a part of the Query
String (remember WAY back at the beginning of this post?) is easier,
cleaner, more flexible, and more secure.
So simply drop that and use the following url:
http://www.myapp.com/admin/password/reset?id=euPdxABQEgE0JDuv6OHSLnk1QBYf73YseBUZwR9%2BMJA%3D
Notice how I url encoded the string which is what you should be doing when passing parameters as query string values in an url.

Related

Show a message after redirecting after a successful POST request without using TempData

I am using the Post-Redirect-Get pattern.
In my asp.net core MVC web app, this is what happens:
User submits a form via POST which adds an item to db.
Controller adds the new item and redirects with 302/303 to "/Home/Index/xxxx", where xxxx is the id of the item.
The new request (/Home/Index/xxxx) is served by the controller, and it displays the item. And the item url in the address bar is something the user can copy and share.
At step 3 above, I would like to show the user a message saying "Item was successfully added".
This is my code (without the success message):
public async Task<IActionResult> Index(string id)
{
ItemView itemView = null;
if (string.IsNullOrEmpty(id))
itemView = new ItemView(); // Create an empty item.
else
itemView = await itemService.GetItemAsync(id);
return View(itemView);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ItemView itemView)
{
string id = await itemService.AddItemAsync(itemView);
return RedirectToAction("Index", "Home", new { id = id });
}
There are few ways to do this that I found in other answers on stackoverflow.
Redirect to "/Home/Index/xxxx?success=true". When action sees a success=true param, it can display the success message. But I don't want to use an extra param because I would like users to be able to just copy the url from the address bar and share it. And I don't want them sharing the url that has success param, because then everyone who clicks on the shared link will see the message "Item was successfully added".
This post suggests using TempData, which is a good solution. I think that would need me to enable sticky behavior on the server, which I would like to avoid if possible.
I can probably use referrer url to determine if the request came after a form submission, and in that case I can show the message.
The original answer by "snoopy" did point me in the right direction. But for some unknown reason, that answer no longer exists, so I am posting the answer myself in the hope it would benefit someone in future.
ASP .NET Core 1.1 and higher supports Cookie based Tempdata provider called CookieTempDataProvider. Link to Microsoft Docs.
This is similar to Session based Tempdata, but no data is stored on the server side. The response from the server set's a cookie in the browser with the data you want to store. The next request from the browser will include this cookie. The framework automatically parses this and populates this in TempData, which the controller can use. Once the controller reads this data, then the CookieTempDataProvider automatically adds the appropriate headers in the response to clear this cookie.
In your Startup class's ConfigureServices method, you need to register CookieTempDataProvider:
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
To store some data in cookie based temp data, you simple set the value like this in your controller:
TempData["key"] = "value";
To read the data in your controller, you read it like this:
string value = TempData["key"];
if (value != null)
{
// Do something with the the value.
}
The check for non-null tells you if that key exists in TempData or not. Note that you can also check using .ContainsKey() method, but that is not counted as a read. The data (& the cookie) will not be cleared unless you read it. For example this will not clear the data:
if (TempData.ContainsKey("key"))
{
// Do something without actually reading the value of TempData["key"].
}

Spring MVC - How to make sure that form submit path is not there in URL post form submit?

I have a question on Spring MVC. I have a form where I take some values from user. And then on submit call action (say addTaskAction). It calls the controller below. The controller does what it needs to do (add Task to database) and calls the view showTasks. Till now,everything is fine. But here is my problem. The URL still shows /addTaskAction. So if I do a refresh or F5, the controller is again called and another task is added to database.
What is the cleanest/industry standard way of avoiding this? How to make sure that post form submit the URL shows /showTasks and not /addTaskAction.
...
Add
#RequestMapping(value = "/addTaskAction", method = RequestMethod.POST)
public String addTaskAction(#RequestParam("issueType") String issueType,
#RequestParam("priority") String priority,
#RequestParam("summary") String summary,
#RequestParam("reporter") String reporter,
#RequestParam("assignee") String assignee,
#RequestParam("status") String status,
#RequestParam("comments") String comments,
Model model) {
System.out.println(issueType);
Task task = new Task(issueType,priority,summary, reporter,
assignee,status,comments);
taskRepository.save(task);
model.addAttribute("tasks", taskRepository.getAllTasks());
//model.addAttribute("task", task);
System.out.println("Tasks added-"+taskRepository.getAllTasks().size());
return "showTasks";
}
You're not supposed to return a page from a POST, you're supposed to redirect to a different page, thus changing the url. In your example, you should probably have something like this:
return "redirect:showTasks";
This returns a redirect instruction to the browser, that then requests the page you've specified.

ASP.NET MVC - Preserve POST data after Authorize attribute login redirect

I have a blog post page with comments.
Any user (logged in or not) can see a form at the bottom of the page to post a comment.
When user enters the comment and she is not authorized - the user is redirected to a login/signup page.
After logged in, the user is redirected back to the action, but the POST data, containing the comment body, is lost.
I use the ASP.NET MVC Authorize attribute to require authorization on some actions:
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(int blogPostID, string commentBody) {
var comment = new Comment {
Body = commentBody,
BlogPostID = blogPostID,
UserName = User.Identity.Name
}
// persist the comment and redirect to a blog post page with recently added comment
}
How do you solve this problem?
Making user loggin before displaying the comment form is a bad idea here I think.
Thanks.
I would probably just save off the siteId and comment into the Session. Then create another overload for Create that doesn't take any parameters. It checks to see if these variables exist in the session - if so, pass it off to your original Create method.
To do that, you'd have to remove the Authorize attribute and just do the security check yourself. Something like this:
var user = HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
Session["Comment"] = comment;
Session["SiteId"] = siteId;
return RedirectToAction("LogOn", "Account",
new { returnUrl = "/ControllerName/Create"} );
}
Then your overloaded Create:
public ActionResult Create()
{
var comment = (Session["Comment"] ?? "").ToString();
int siteId = 0;
if (Session["siteId"] != null)
siteId = (int)Session["siteId"];
return Create(siteId, comment);
}
Of course, this isn't really all that generic and doesn't handle more complex scenarios, but it's an idea. (hopefully the above code works, I haven't had a chance to test it). It seems like you could maybe do something like this via an action filter but I don't have any sample code for that.
You can use hidden field on your authorization form. Put your user's comment to that field (your initial POST data). After that you still can not use the data on your comment form if authorization form simply redirects to your comments form. So make your authorization form post to comments form, data in hidden field will be posted also, so you can use it.

Redirect to last visited page (stored in a cookie)

In our MVC application we want the user, after he is logged in, to be redirected to the page he visted last in a previous session.
What is a good approach for achieving this?
I am thinking of an httpmodule-->begin request or via the global.asax
And at which point in the requestprocess should I put the logic to check whether the cookie exists and do the redirect? In the Application.init?
Any advice would be really appreciated!
You could create a custom action filter that saves the currently requested URL to the cookie. Then check for the cookie value in your login action method and redirect if necessary.
In doing this you could decorate only the controllers and actions that you want that are potential entry points. e.g. not actions that return partial views etc.
That is correct, there is no event on click. However, there is a much simpler soluction, MVC handles form submits and redirects quite well. To store the last visited URL, you could use an action filter on your controller. Then to handle the redirect, create two Login functions. One handles the GET request, the other the POST request. In the POST request, after having verified authentication, retrieve the URL (or action) from the cookie and redirect the user.
It would be something like this:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginViewModel model)
{
if (authenticated)
{
//get cookie information
HttpCookie cookie;
cookie = Request.Cookies["StoredURLFromLastSession"];
String StoredURLFromLastSession = cookie.Value;
//Choose one of these redirect methods
//returns to a hard coded URL
//return Redirect(StoredURLFromLastSession);
//redirects to a route (using routes created in global.asax
//return RedirectToRoute(StoredURLFromLastSession);
//redirects to a specific action/controller
//return RedirectToAction(StoredURLFromLastSession);
}
}
Hope this helps.

Sending of url to confirm user registration in asp.net mvc 2.0

I am developing a site in asp.net mvc 2.0 .Here i want to send the url link to the user email on user registration.I want to send the url format in such a way that if user clicks on that link it will take him to the index page of user module where he can enter his user name and password and login to the user module.
Please tell me how to do it or give me the links of these type of posts.
Thanks in advance,
You could use the UrlHelper.Action method:
public ActionResult Email()
{
// Notice the last parameter which contains the protocol:
// this one is important if you want the helper to generate
// an absolute url. If you don't specify this parameter the
// generated url will be relative
string url = Url.Action("index", "home", null, "http");
// TODO: Send url by email
_repository.SendMail(url);
return View();
}

Resources