I'm trying to check for multiple errors on my form. Here is the code I have:
var hasErrors = false;
var sb = new StringBuilder();
if (string.IsNullOrEmpty(creditCard.CardNumber))
{
hasErrors = true;
sb.AppendLine("Credit card number is required.");
//ModelState.AddModelError("PaymentAmount", "Credit card number is required.");
}
if (string.IsNullOrEmpty(creditCard.ExpirationDateMonth) || string.IsNullOrEmpty(creditCard.ExpirationDateYear))
{
hasErrors = true;
// ModelState.AddModelError("PaymentAmount", "Expiration date is required.");
sb.AppendLine("Expiration date is required.");
}
if (string.IsNullOrEmpty(creditCard.NameOnCard))
{
hasErrors = true;
// ModelState.AddModelError("PaymentAmount", "Name is required.");
sb.AppendLine("Name is required.");
}
decimal amt = 0;
creditCard.PaymentAmount = creditCard.PaymentAmount.Replace("$", string.Empty);
if (!decimal.TryParse(creditCard.PaymentAmount, out amt))
{
hasErrors = true;
//ModelState.AddModelError("PaymentAmount","Amount is invalid.");
sb.AppendLine("Amount is invalid.");
}
if (hasErrors)
{
ModelState.AddModelError("PaymentAmount", sb.ToString().Replace(Environment.NewLine,"<br>"));
return View("CreditCard", creditCard);
}
I'm trying to get AddModelError to display in multiple lines but I'm not having any luck. It's displaying the <br> as text on the screen instead of rending a break.
I had it where the error was being submitted individually but you'd have to submit the form multiple times before you got the errors on screen. That's why the AddModelError is commented out in each line.
Is there a way to display multiple lines on the AddModelError or is there a better way to handle this?
Thanks for the help!
You should call ModelState.AddModelError for each of the errors you have in your controller, IMHO, it is not a good practice to mix your validation logic with the way things are rendered in the user interface. In fact, the MVC pattern is all about separating the three concerns, the model (data), the controller (logic, such as validation) and the views (the user interface).
So I would do something like this:
if (string.IsNullOrEmpty(creditCard.CardNumber))
{
ModelState.AddModelError("PaymentAmount", "Credit card number is required.");
}
if (string.IsNullOrEmpty(creditCard.ExpirationDateMonth) || string.IsNullOrEmpty(creditCard.ExpirationDateYear))
{
ModelState.AddModelError("PaymentAmount", "Expiration date is required.");
}
if (string.IsNullOrEmpty(creditCard.NameOnCard))
{
ModelState.AddModelError("PaymentAmount", "Name is required.");
}
[…]
Then in your view, you can use the following HTML helper to render each error in a list:
If you are using ASP.NET Core:
<div asp-validation-summary="ValidationSummary.ModelOnly"></div>
If you are using the previous versions of ASP.NET MVC:
#Html.ValidationSummary()
This will generate HTML that you can style using CSS.
See here for more info if you are using asp.net core or here for an example if you are using the previous version of ASP.NET MVC.
If you want to display the errors in a different way you can access the errors directly in your view or even better, roll your own helper, see the answers to this question: How do I get the collection of Model State Errors in ASP.NET MVC?
Related
I am currently trying to obtain some printed text from an automated test website after I have completed a form. The returned values are displayed to the user on the screen after submitting the form, but for some reason I cannot obtain 2 of the 4 text values with Selenium's WebElement, I have tried using .Text and GetAttribute("value"), and they both return blank. Yet the first 2 pieces of text returned, I am able to retrieve. Please see screenshot below along with the code.
//Then I verify the form submitted
Constants.confirmationName = driver.FindElement(By.CssSelector("#name"));
if (Constants.confirmationName.Text == "Name:QA Automation")
{
Console.WriteLine(Constants.confirmationName.Text);
}
else
{
Console.WriteLine("We have a different name stored for you.");
}
Constants.confirmationEmail = driver.FindElement(By.CssSelector("#email"));
if (Constants.confirmationEmail.Text == "Email:automation#hotmail.com")
{
Console.WriteLine(Constants.confirmationEmail.Text);
}
else
{
Console.WriteLine("We have a different email stored for you.");
}
Thread.Sleep(2000);
//NOT WORKING
Constants.confirmationCurrentAddress = driver.FindElement(By.CssSelector("#currentAddress"));
Thread.Sleep(2000);
if (Constants.confirmationCurrentAddress.Text == "Current Address :Cedars 2 ")
{
Console.WriteLine(Constants.confirmationCurrentAddress.Text);
}
else
{
Console.WriteLine("We have a different current address stored for you.");
}
//NOT WORKING
Constants.confirmationPermanentAddress = driver.FindElement(By.CssSelector("#permanentAddress"));
if (Constants.confirmationPermanentAddress.Text == "Permananet Address :63 Wheat Drive")
{
Console.WriteLine(Constants.confirmationPermanentAddress.Text);
}
else
{
Console.WriteLine("We have a different permanent address stored for you.");
}
The code confirms the name that is printed, and I can see it returned, same goes for the email address, but the current address and permanent address both return blank, I've tried to add in wait times too, but to no avail.
The website in question is https://demoqa.com/text-box, if you fill in the fields, then click Submit, you will see the printed information underneath.
Any help would be greatly appreciated as it's driving me insane!
Check the HTML for any other Elements that share the same Id of currentAddress and permanentAddress, I reckon this is the problem as it's likely finding that first element matching that Id and that element has no text (hence the empty string and no exception).
Your code looks fine to me, and the fact that email and name work correctly suggests your code is also fine.
try changing this
Constants.confirmationCurrentAddress = driver.FindElement(By.CssSelector("#currentAddress"));
to
Constants.confirmationCurrentAddress = driver.FindElement(By.CssSelector("p#currentAddress"));
that should help narrow down and find the element you're looking for.
With in the ToolsQA Text Box webpage once you click on the Submit button as the values of Full Name and Email are immediately rendered within <p> tags:
You can use the .Text property to extract the innerText as follows:
Constants.confirmationName = driver.FindElement(By.CssSelector("#name"));
if (Constants.confirmationName.Text == "Name:QA Automation")
{
Console.WriteLine(Constants.confirmationName.Text);
}
But the values of Current Address and Permanent Address are not rendered immediately within <textarea> tags
Hence, to extract the value of Current Address and Permananet Address instead of using the Text property you need to use GetAttribute("value") method as follows:
To extract Current Address:
Constants.confirmationCurrentAddress = driver.FindElement(By.CssSelector("#currentAddress"));
if (Constants.confirmationCurrentAddress.GetAttribute("value") == "Current Address :Cedars 2 ")
{
Console.WriteLine(Constants.confirmationCurrentAddress.Text);
}
To extract Permananet Address:
Constants.confirmationPermanentAddress = driver.FindElement(By.CssSelector("#permanentAddress"));
if (Constants.confirmationPermanentAddress.GetAttribute("value") == "Permananet Address :63 Wheat Drive")
{
Console.WriteLine(Constants.confirmationPermanentAddress.Text);
}
I was trying to add a break line in string.Format. However it didn't work no matter I added \r\n or Environment.NewLine. Can anyone advise what is the proper way to add a break line into the statement? Thanks.
public async Task<IViewComponentResult> InvokeAsync()
{
var claimsIdentity = (ClaimsIdentity) User.Identity;
var claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
var userFromDb = await _db.ApplicationUser.FirstOrDefaultAsync(u => u.Id == claim.Value);
var role = await _userManager.GetRolesAsync(userFromDb);
string r = string.Format("{0} \r\n ({1})", userFromDb.Name, role[0]);
return View("Default",r);
}
Your controller (or view component) shouldn’t actually be responsible for deciding how something is displayed. Whether you need for example a line break, a <br /> tag, or separate <p> tags is the responsibility of the view. So you should not attempt to solve this in the controller but instead pass the view just everything necessary to take care of the visual representation itself.
You can do that by create a custom view model that contains the values you need to pass the view. You then create this view model and set its properties and pass on the object to the view:
public async Task<IViewComponentResult> InvokeAsync()
{
var claimsIdentity = (ClaimsIdentity) User.Identity;
var claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
var userFromDb = await _db.ApplicationUser.FirstOrDefaultAsync(u => u.Id == claim.Value);
var role = await _userManager.GetRolesAsync(userFromDb);
// pass a complex object with separate properties to the view instead
return View("Default", new MyViewComponentModel
{
Name = userFromDb.Name,
Role = role[0],
});
}
public class MyViewComponentModel
{
public string Name { get; set; }
public string Role { get; set; }
}
Then, in the view, specify this MyViewComponentModel as the view model type and access the properties to render the values properly:
#model MyViewComponentModel
<div>
<strong>#Model.Name</strong><br />
#Model.Role
</div>
This approach has another huge benefit over creating the HTML code in the controller code: If you were to pass HTML to the view, then the view would have to render that text as raw HTML. This means that the code is taken as it is and no HTML encoding happens with the content. This may work with save values that are coming from your database but can get dangerous very quickly as soon as there is some user-defined content in there. Then, you are basically allowing malicious users to decide what kind of HTML gets rendered on your website which is a very common security issue. So it’s best to avoid that and have as little raw HTML in your views as absolutely necessary.
You are adding the line-break and sending the string to an HTML view. HTML is white space agnostic. Line-breaks are white space. Therefore the line-break has zero effect on the displayed string.
To place a line-break in HTML use the <br> tag. Or, better still send the string as two strings and let the view decide how it is displayed.
\r\n this work in C# code
string Name = "Alex";
var role = "manager";
string newString = string.Format("{0} \r\n ({1})", Name, role);
Console.WriteLine(newString);
however if you want to return view so it must be html break line <br />
public static class BreakLine
{
public const string LineBreak = #"<br />";
}
string htmlString = string.Format(#"{0} {1} ({2})", Name, BreakLine.LineBreak, role);
Console.WriteLine(htmlString);
Can we check whether the input form in an adaptive card is filled or not with a warning message.
I am currently using an adaptive card to gather user input in bot application,I have already added isRequired for input validation but it doesnot give any warning message instead when I click on submit it doesnot go to the next method.
As soon as the user presses submit I want to make sure that the form is not empty
If you have an Adaptive Card like this (notice the ID given to the input):
var card = new AdaptiveCard
{
Body =
{
new AdaptiveTextBlock("Adaptive Card"),
new AdaptiveTextInput { Id = "text" },
},
Actions = {
new AdaptiveSubmitAction { Title = "Submit" } },
},
};
You can validate the value sent through the submit action like this:
if (string.IsNullOrEmpty(turnContext.Activity.Text))
{
dynamic value = turnContext.Activity.Value;
string text = value["text"]; // The property will be named after your input's ID
var emailRegex = new Regex(#"^\S+#\S+$"); // This is VERY basic email Regex. You might want something different.
if (emailRegex.IsMatch(text))
{
await turnContext.SendActivityAsync($"I think {text} is a valid email address");
}
else
{
await turnContext.SendActivityAsync($"I don't think {text} is a valid email address");
}
}
Validating email with regex can get very complicated and I've taken a simple approach. You can read more about email Regex here: How to validate an email address using a regular expression?
I took totally different approach than the accepted answer here. If you are going to use adaptive cards a lot in your bot than it makes sense to create card models and have validation attributes applied to each field that needs validation. Create custom card prompt inheriting from Prompt<object> class. Override OnPromptAsync and OnRecognizeAsync and check the validation of each field there.
I have a question about ModelState.AddModelError method and about the ValidationMessage method.
I am new to ASP.NET MVC and I am a little confused.
I wrote this code:
public ActionResult update(FormCollection collection)
{
int oos = 0;
try
{
oos = int.Parse(collection[0]);
}
catch
{
}
data d = new data();
TryUpdateModel(d , collection.ToValueProvider());
if (ModelState.IsValid)
{
return View("index",d);
}
else
{
ModelState.AddModelError("Date", "Wronge Date");
d.Id = 50;
return View("index",d);
}
}
and this code in the view side
#{
ViewBag.Title = "index";
}
<h2>index</h2>
#TempData["Hi"]
#Html.ValidationMessage("fullname")
#using (Html.BeginForm())
{
#Html.AntiForgeryToken() #Html.TextBox("id", 70)
#Html.TextBox("Date", "3/2/1991 12:00:00 ص")
#Html.ValidationMessage("Date","Please insert the correct Date Format")
<input type="submit">
}
My questions are, why the message Please insert the correct Date Format appears directly when I rune the index while still I did not submit the form, why when I submit the form with error in the date format,the same message appear but not the message that I set to the Date Key in the update method which is Wronge Date.
maybe still I do not understand those two methods so I hope to find somebody to explain them to me.
explnation with example or reference would be appreciated
Please look at http://www.asp.net/mvc/overview/getting-started/introduction/adding-validation
Because you have already entered a message in the View it will use that over your error that you are adding from the controller. As for your fullname you do not have a message set yet, only the placehoder for the field.
One of my error message renders a link. However, Html.ValidationSummary() encodes it and therefore it displays as follow:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
Instead, it should render as:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
The error is added to the ModelState inside view as follows:
if (...)
{
ViewData.ModelState.AddModelError(string.Empty, string.Format("An account with the mobile or email you have specified already exists. If you have forgotten your password, please {0} it.", Html.ActionLink("Reset", "Reset")));
}
In short, how should I prevent Html.ValidationSummarry() to selectively/entirely encoding html in errors.
The current HTML helpers for displaying error messages do not support this. However, you could write your own HTML helpers that display the error message without HTML escaping it, i.e. they would treat the error message as raw HTML.
As a starting point, you could use the ASP.NET MVC source code from Codeplex, specifically the ValidationSummary method of the ValidationExtensions class:
public static string ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes) {
// Nothing to do if there aren't any errors
if (htmlHelper.ViewData.ModelState.IsValid) {
return null;
}
string messageSpan;
if (!String.IsNullOrEmpty(message)) {
TagBuilder spanTag = new TagBuilder("span");
spanTag.MergeAttributes(htmlAttributes);
spanTag.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else {
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
unorderedList.MergeAttributes(htmlAttributes);
unorderedList.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
foreach (ModelState modelState in htmlHelper.ViewData.ModelState.Values) {
foreach (ModelError modelError in modelState.Errors) {
string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
if (!String.IsNullOrEmpty(errorText)) {
TagBuilder listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
unorderedList.InnerHtml = htmlSummary.ToString();
return messageSpan + unorderedList.ToString(TagRenderMode.Normal);
}
You can then change this method to treat the error message as raw HTML.
Two warnings though:
You're changing the meaning of certain properties of the ModelState class. While you get away with using your own HTML helpers now, a future version of ASP.NET MVC might introduce changes that no longer work with this approach.
Be very careful about not using error messages that aren't properly escaped so you don't expose your web app to XSS attacks. Certain standard validation annotation might not work any longer since they don't HTML escape the error message.