Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Post Causes QueryString to Be Lost on Reload of Same View

Please let me explain the setup.

I have a Change Password Controller/Action and View. Here are the Action Signatures in my Account Controller:

public ActionResult ChangePassword(ChangePasswordMessageId? message)

[HttpPost]
public ActionResult ChangePassword(ChangePasswordViewModel model)

When change password is first loaded, I have some data in the querystring. Here is an example:

https://www.mywebsite.com/Account/ChangePassword?mobile=1

Here is the Form declaration from the view.

@using (Html.BeginForm("ChangePassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()

The form is submitted with a simple submit button:

<div class="form-group">
  <div class="col-md-offset-2 col-md-4">
    <input type="submit" value="Change Password" class="btn btn-primary btn-block" />
  </div>
</div>

The form has 3 fields: Current Password, New Password, and Confirm Password. If the user fills in all the data correctly, and passes all the client side validation the form works fine. Everything works fine except for one use case.

Suppose the user enters an Old Password value that is not correct. When I get to the HTTPPOST ChangePassword action above, it will fail. This is what that code looks like.

[HttpPost]
            public ActionResult ChangePassword(ChangePasswordViewModel model)
            {   
                if (ModelState.IsValid)
                {
                    try
                    {
                        MembershipUser user = Membership.GetUser();
        //The NEXT line is the one that fails if they supply the wrong Old Password value.
        //The code then falls to the catch condition below.
                        bool changePassword = user.ChangePassword(model.OldPassword, model.NewPassword);
                        if (changePassword)
                        {
                            string path = Url.Action("ChangePassword", new { Message = ChangePasswordMessageId.ChangePasswordSuccess });
                            temp = Request.UrlReferrer.ToString();
                            pos = temp.IndexOf("?");
                            if (pos > 0) path += "&" + temp.Substring(pos + 1);
                            return RedirectToLocal(path);    
                       }
                        else
                        {
                            ModelState.AddModelError("", "Change Password failed.");
                        }
                    }
                    catch //(Exception ex)
                    {
                        ModelState.AddModelError("", "Change Password failed.");
                        //ModelState.AddModelError("", ex.Message);
                    }
                }

                // If we got this far, something failed, redisplay form
    //The original query string will be gone. The URLwill now only show
    //https://www.mywebsite.com/Account/ChangePassword
                return View(model);
            }

Is there away to call "return View(model);" so that the original query string will still be there? I need to maintain the query string from page to page. I have it working everywhere except for this one use case.

Thanks!

like image 210
Bobby Ortiz Avatar asked Jul 17 '14 12:07

Bobby Ortiz


4 Answers

If somebody is still looking for this solution, try not supplying controller and action names.

 @using (Html.BeginForm(null, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
like image 92
Ahuman Avatar answered Oct 21 '22 14:10

Ahuman


One way you could do it is to make your Mobile flag part of your View Model. And, in your Get controller action, make sure you get the Mobile flag from your query string when you populate your View Model.

On your form, create a hidden input field for the fields you want posted back. E.g.:

@Html.Hidden(Model.Mobile)

On post, the Mobile value will be set correctly in your ChangePasswordViewModel so you have access to it.

like image 31
Brad Rem Avatar answered Oct 21 '22 14:10

Brad Rem


return RedirectToAction("Index", Request.QueryString.ToRouteValues());
like image 30
user7788684 Avatar answered Oct 21 '22 15:10

user7788684


My code is in VB, in the view, create a helper function to load all URL query parameters into the form:

@Functions
Public Function GetRouteValues() As RouteValueDictionary
    Dim RouteValues As New RouteValueDictionary
    For Each Qstr As String In Request.QueryString
        RouteValues.Add(Qstr, Request.QueryString.GetValues(Qstr).FirstOrDefault)
    Next
    Return RouteValues
End Function
End Functions

then declare the form head as following

@Using (Html.BeginForm("MultipleHandler", "Files", GetRouteValues, FormMethod.Post))

This way, you passed your collection of query string to the action.

In the action, use the same function:

Private Function AllRouteVlaues() As RouteValueDictionary
        Dim RouteValues As New RouteValueDictionary
        For Each Qstr As String In Request.QueryString
            RouteValues.Add(Qstr, Request.QueryString.GetValues(Qstr).FirstOrDefault)
        Next
        Return RouteValues
    End Function

Usage:

Return RedirectToAction("Index", AllRouteVlaues)

Hope that helps :)

like image 28
M.Mourad Avatar answered Oct 21 '22 14:10

M.Mourad