When I validate a form with Asp.Net Mvc and there's an error on the form, Asp.Net return me the form with the correct error message but clear the content of the input. I would like the content of the input to stay even if this content is wrong. How can I do that ?
UPDATE
Maybe this is because I don't use the default validation. I use the Fluent Validation library.
Here what I do :
var validator = new UserValidator();
var results = validator.Validate(user);
results.AddToModelState(ModelState, "");
if (!ModelState.IsValid)
{
return View("SignUp", user);
}
The problem might be in how you "bind" the model you are passing in the view. If you use a strongly typed view and create the input fields with for example
<%=Html.TexboxFor(m=>m.UserName)%>
or
<%=Html.TextBox("UserName", Model.UserName)%>
then you should see the values after posting.
Regards
In addition to what #Germán Ubillos posted, you can also store the post values in TempData and send them back through.
<%=Html.TextBox("UserName", TempData["UserName"])%>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SomeAction(string UserName)
{
TempData["UserName"] = UserName;
// Do your validation
var validator = new UserValidator();
var results = validator.Validate(user);
results.AddToModelState(ModelState, "");
if (!ModelState.IsValid)
{
return View("SignUp", user);
}
//return some view
}
Related
Sorry for the title, but don't know how to explain it. (.NET ASP MVC)
So what I'm trying is to create a payment request via TripleA API(redirect on their page), if the payment is successful, they will redirect on my success page with some parameters, how can I handle those parameters?
What I've tried:
public IActionResult ErrorPage(string payment_reference, string status)
{
return View(payment_reference,status);
}
https://developers.triple-a.io/docs/triplea-api-doc/dd286311a5afc-make-a-payment-request
(scroll down to success_url for more info)
To expand on Steve's comment, create a record (less code than a class) as follows...
public record ErrorViewModel(string PaymentReference, string Status);
...then use this when you send data to the view...
public IActionResult ErrorPage(string payment_reference, string status)
{
return View(new ErrorViewModel(payment_reference,status));
}
You'll need to update your view to have the following line at the top...
#model ErrorViewModel
That should be all you need.
Based on the documentation, you expect a request like this,
https://www.myshop.com/payment-success?status=paid&payment_reference=ASDDF...&order_currency=USD&order_amount=10
And you translate that into a controller method,
[HttpGet("payment-success")]
public IActionResult ResultPage(string payment_reference, string status, string order_currency, decimal order_amount)
{
var result = new ResultViewModel(payment_reference,status, order_currency, order_amount);
return View(result);
}
I also noticed that the doc says,
Note: This field is required if integrating using External URL Payment Form. For other integrations, either insert the field with a url, or remove the field completely.
So if you use External URL Payment Form integration, then I don't think you will be able to get the status and reference.
The same applies for cancel_url.
I have this code snippet in my Controller class:
var userId = _userService.GetUserByActivationCode(model.ActivationCode);
if (userId != null)
{
var verifiedUser = VerifyUserActivationCode(model.ActivationCode, model.DateOfBirth);
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, new { user = verifiedUser});
}
verifiedUser is populated correctly, but when passed to this action, user is null.
[AllowAnonymous, HttpGet]
public ActionResult AccountInformationForUserJourney(User user) { // code here }
Why is the verifiedUser not passed to the new ActionResult?
Change
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, new { user = verifiedUser});
To
resultModel.RedirectUrl = Url.Action(AccountInformationForUserJourneyAction, verifiedUser);
You are not binding a model to a view, you are creating a URL that will, in this case, need a querystring to pass any data to the new action. If you want to POST a model, you would need a form with a Javascript auto-post function. Anything you pass in the querystring needs to be URL safe and will then bind to the new action using name=value.
This doesn't seem like what you want to do. Either just pass a user id that you can reload in the new action or return the view that you want the user to see (AccountInformation) directly from the action you are already in, then you can bind your model directly to the view.
Iam a beginner in MVC.
If iam using below code then Model.IsValid is not validating the object which in this case is Customer.
public ActionResult Submit()
{
Customer custObj = new Customer();
custObj.CustomerCode = Request.Form["CustomerCode"];
custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
While if Iam passing the Customer object in parameter then Model.IsValid is working perfectly.
public ActionResult Submit(Customer obj)
{
//Customer custObj = new Customer();
//custObj.CustomerCode = Request.Form["CustomerCode"];
//custObj.CustomerName = Request.Form["CustomerName"];
if (ModelState.IsValid)
return View("Load", obj);
else
return View("EnterCustomer");
}
Can any1 help me in getting to know the reason.
It doesn't work beause MVC never bound to the model itself. You manually overrode it so MVC has no clue whether the model is valid or not. It doesn't event know that custObj is the model.
ModelState.IsValid is set before your action method is called, so in your second example, when you allow MVC to bind to the model itself, it works. In the first, it doesn't work because you create the model and do manual binding to it.
Update
You can, however, also manually run the model validation by calling ValidateModel or TryValidateModel on the controller.
Documentation:
ValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.validatemodel(v=vs.100).aspx
TryValidateModel: https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryvalidatemodel(v=vs.100).aspx
As mentioned in other answers, your model is already validated before the action 'Submit' is called. So, when you are changing the model from inside your action, you will have to manually validated the model. You may use below code for it.
var context = new ValidationContext(custObj, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(custObj, context, validationResults, true);
if (isValid)
return View("Load", obj);
else
return View("EnterCustomer");
use below url for further details.
http://odetocode.com/blogs/scott/archive/2011/06/29/manual-validation-with-data-annotations.aspx
I'm working on an MVC5 application. On the home screen is a grid allowing users to view Data and be transferred to a number of Views for various actions on each record. One of these is an [EDIT].
The issue I'm encountering is as follows: due to the amount of data it is convenient to Filter the data down (say to a specific location) and then Edit records from there. The filter on this grid (Grid.MVC from CodePlex) performs filtering partially by modifying the URL (http://homeURL/?grid-filter=Location.DEPT__1__accounting) such as 1 being Equals, 2 being Cotains, 3 being StartsWith, and 4 being EndsWith and then after the next 2 underscores being the search criteria.
This functions fine, however upon [POST] return from the Edit the user currently is returned to main Index view without the filtering criteria still set (forcing them to go in over and over and add filtering criteria before performing the similar EDIT on records of the same criteria).
My POST-EDIT method is currently setup to include:
if (ModelState.IsValid)
{
collection.MODIFIED_DATE = DateTime.Now;
collection.MODIFIED_BY = System.Environment.UserName;
db.Entry(collection).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index", "Home");
}
For my attempts I had first thought to return the View with the updated collection (return View(collection)) but this of course just takes me back to the EDIT view, not the home view with the data grid filtered down as previously specified. I considered adding a field in the database, something like LAST_FILTERED_URL, but this just feels like an overgrown band-aid.
Does anyone know of a clean way to go about this?
EDIT:
I had thought to do something similar to Andrea's suggestion early on, but had not thought of doing an explicit redirect with the Parameter of the url-filter passed in the Redirect. Below is my current code for the GET/POST Edit:
// GET: ENITTY_Collection/Edit/5
public async Task<ActionResult> Edit(int id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ENTITY_COLLECTION entity_Collection = await db.ENTITY_COLLECTION.FindAsync(id);
if (entity_Collection == null)
{
return HttpNotFound();
}
// Other code for Controls on the View
return View(entity_Collection);
}
// POST: ENTITY_Collection/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,One_Id,Two_Id,Three_Id,Four_Id,Five_Id,Six_Id,field7,field8,field9,...field18,created_date,created_by,modified_date,modified_by")] ENTITY_COLLECTION entity_Collection)
{
if (ModelState.IsValid)
{
entity_Collection.MODIFIED_DATE = DateTime.Now;
entity_Collection.MODIFIED_BY = System.Environment.UserName;
db.Entry(entity_Collection).State = EntityState.Modified;
await db.SaveChangesAsync();
//return RedirectToAction("Index", "Home");
return View(entity_Collection);
}
// Other code for if Model is Invalid before returning to View.
return View(entity_Collection);
}
I like Andrea's suggestion, but I still need a good way to store the URL the user has when they first navigate to the GET-Edit View, and then use that filtered URL value to return the user to that previous location & filter option when the POST-Edit completes and changes have saved.
Any thoughts?
I'm not sure if this is the most correct way of going about what I'm after, but what appears to be working for me is the use of a Session value.
In my GET method I store the URL:
Session["returnURL"] = Request.UrlReferrer.AbsoluteUri;
Then in my POST I use this value in a Redirect() after saving changes to the record:
var returnURL = (Session["returnURL"] != null) ? Session["returnURL"].ToString() : Url.Action("Index", "Home");
return Redirect(returnURL);
So far all initial testing is resulting in a return to the main view with all sorting/filtering criteria in place before the record was entered into for update.
Have you tried changing passing the current filter to redirect to action as follows?
Note: I am assuming that:
you are redirecting to the same controller
you have a controller parameter called currentFilterValue
RedirectToAction("Index", "Home",new { grid-filter = currentFilterValue });
The default LoginController for an MVC project from Microsoft includes a bunch of methods that use a returnUrl parameter. Using this practice, you could include a return URL when opening the editor, that when editing is done, returns the user back to the prior screen with the filters intact (assuming they are in the URL).
My base class for view models has a property for storing the ReturnURL, which is then stored as a hidden if set.
#Html.HiddenFor(model => model.ReturnUrl)
My action that posts from edit then has this logic:
if (viewModel.ReturnUrl.IsNotNullOrEmptyTrimmed())
{
return RedirectToLocal(viewModel.ReturnUrl, "ActionName", "ControllerName");
}
else
{
// Default hard coded redirect
}
In order to prevent some injections, you will want a validation method (called above) like this for ensuring the URL is valid:
protected ActionResult RedirectToLocal(string returnUrl, string defaultAction, string defaultController)
{
try
{
if (returnUrl.IsNullOrEmptyTrimmed())
return RedirectToAction(defaultAction, defaultController);
if (Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
Uri returnUri = new Uri(returnUrl);
if (Url.IsLocalUrl(returnUri.AbsolutePath))
{
return Redirect(returnUrl);
}
}
catch
{
}
return RedirectToAction(defaultAction, defaultController);
}
Hopefully this gets you some ideas and on the right track.
I have an extremely confusing case. I currently have the following code
//get method
public ActionResult EmailValidation(string email, string token)
{
EmailValidationViewModel model = new EmailValidationViewModel();
model.email = email;
model.token = token;
Member existingMember = workflow.GetMemberByEmail(model.email, vendor.VendorID);
if(existingMember != null)
{
return View();
}
}
[HttpPost]
public ActionResult EmailValidation(EmailValidationViewModel model)
{
}
Now the above code, which I have already tested on a different page, loads the information in the model to the post method automatically. I believe there is a problem with the new view with the code I posted below that is inhibiting it from taking the information from the get method and sending it to the post method.
I believe it has something to do with the following code?
[[Form_Start]]
<form action="/Mobile/Home/EmailValidation" id="EmailValidation" method="post" data-transition="slideup">
[[/Form_Start]]
if you guys might know why it returns the view with the data from the get method in one controller, but it doesnt in the next, please let me know. In addition if you require more information, also just tell me and I'll try to provide you with as much code as possible. Thanks !
This:
if(existingMember != null)
{
return View();
}
is returning a View with no Model.
Change it to this:
if(existingMember != null)
{
return View(existingMember);
}