Client validation did not work - asp.net-mvc

In my project used ASP.NET MVC 5. I've used System.ComponentModel.DataAnnotations for validation.
I expect when I don't enter value for a mandatory field,be prevent to continue my action.
But this expectation does't satisfy.
How can I solve this problem?
#model Jahan.Blog.ViewModel.ArticleViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Article</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.UserId)
#*There are some codes*#
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
</div>
#*There are some codes*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
ViewModel:
[ModelBinder(typeof(ArticleViewModelBinder))]
public class ArticleViewModel : IArticleViewModel
{
public ArticleViewModel()
{
}
public virtual int Id { get; set; }
[Required]
[StringLength(256)]
public virtual string Title { get; set; }
}
Controller:
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Prefix = "")]ArticleViewModel articleViewModel, List<int> availableTags)
{
if (ModelState.IsValid)
{
// There are some codes.
}
return View(articleViewModel);
}

open your web.config and find ClientValidationEnabled in appSettings and check if it's set as true :
<add key="ClientValidationEnabled" value="true" />

According to Fiskeboss's comment, jquery.validate.unobtrusive.js library added.Then it works correctly.

Related

Why is the view model passed to my action method null?

I can't seem to pass the form values using a model. I do not want to resort to using individual parameter/FormCollection/Request and then instantiate the model class with the values.
My model
//JcSpaceAccount.cs
namespace JcSpaceEntities
{
public class JcSpaceAccount
{
public string FirstName;
public string LastName;
public string Email;
public DateTime DateOfBirth;
}
}
My View
//Registration.cshtml
#model JcSpaceEntities.JcSpaceAccount
<!DOCTYPE html>
<div class="form-horizontal">
<h4>JcSpaceAccount</h4>
<hr />
#using (Html.BeginForm("Registration", "Registration", FormMethod.Post))
{
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.LabelFor(m => m.FirstName)
</div>
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(m => m.FirstName)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.LabelFor(m => m.LastName)
</div>
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(model => model.LastName)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.LabelFor(m => m.Email)
</div>
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(model => model.Email)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.LabelFor(m => m.DateOfBirth)
</div>
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(model => model.DateOfBirth)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
</div>
My controller
namespace JcSpace.Areas.Registration.Controllers
{
public class RegistrationController : Controller
{
// GET: Registration/Registration
[HttpGet]
public ActionResult Registration()
{
return View();
}
[HttpPost]
public ActionResult Registration(JcSpaceAccount entity)
{
return View();
}
}
}
You should change your model to:
public class JcSpaceAccount
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime DateOfBirth { get; set; }
}
Problem is default MVC ModelBinder works with properties and you have fields in your model now. So default model binder just can't fill them.
Change your JcSpaceAccount fields to properties and you get your data on post.
And in your Post contoller method you should set your model as #haim770 said:
[HttpPost]
public ActionResult Registration(JcSpaceAccount entity)
{
ViewData.Model = entity; //This line
return View();
}
On your Registration() method decorated with HttpGet.
[HttpGet]
public ActionResult Registration()
{
return View(new JcSpaceAccount());
}

How to make file upload required in asp.net mvc?

I am uploading file using asp.net mvc with file upload required but unable to upload file using this. How to make file upload required with validation using ASP.NET MVC?
Here is my Model class code.
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string FileURL { get; set; }
}
Here is my Create Controller:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "SliderId,Title,FileURL")] HttpPostedFileBase file, Slider slider)
{
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
db.Sliders.Add(slider);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(slider);
}
Here is my View:
#model Test.Models.Slider
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Create", "SliderManager", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title,"Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Changes to make
1st model
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
public string FileURL { get; set; }
}
Removed required on file Url as this is not coming from user but you should be populating it
2nd Upload action
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(HttpPostedFileBase file, Slider slider)
{
//Add validation if file is not present and fail model
if (file == null)
{
ModelState.AddModelError("FileURL", "Please upload file");
}
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
//db.Sliders.Add(slider);
//db.SaveChanges();
return RedirectToAction("Index");
}
return View("~/Views/Home/Index.cshtml", slider);
//return View(slider);
}
Also I am not sure why you have specified additional bindings, but I guess you had some reason for that
3rd the view
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, "Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
I have added validation message. This validation message of course can have its own property and I would modify it so it fits with your business logic.
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)
I think the solution from this tutorial is better because it doesn't need an extra property:
https://www.aspsnippets.com/Articles/Fileupload-validation-using-Model-Data-Annotations-in-ASPNet-MVC.aspx
Model:
public class FileModel
{
[Required(ErrorMessage = "Please select file.")]
public HttpPostedFileBase PostedFile { get; set; }
}
View:
#model FileUpload_Validation_MVC.Models.FileModel
#{
Layout = null;
}
<div>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<span>Select File:</span>
#Html.TextBoxFor(m => m.PostedFile, new { type = "file"})
<br/>
#Html.ValidationMessageFor(m => m.PostedFile, "", new { #class = "error" })
<hr/>
<input type="submit" value="Upload"/>
}
</div>

Store View form in existing database

Forgive the newbie ASP.NET MVC question. I am used to tutorials where Code First is used with the Entity Framework. Here, this is not the case. I have a form that I want the user to fill out. When it has been filled out, I want to use EF to write the values to an existing database. I can't figure out how to "trap" the values in the view so I can write my EF code. I used a model and I redirected the BeginForm to an "Edit" action method but I don't know how to get my filled in class. Here is the HomeController methods:
[HttpGet]
public ActionResult Trial()
{
UserAccount account = new UserAccount();
return View(account);
}
public ActionResult Edit()
{
}
Here is the model class:
public class UserAccount
{
public int AccountID { get; set; }
public string AccountName { get; set; }
public string RegistrationCode { get; set; }
public DateTime Created { get; set; }
}
}
Here is the View the wizard generated. When I hit the "Create" button, I want to go to the "Edit" action menu or someplace I can use EF to write to the existing database table. How do I do this?
#model AlphaFrontEndService.Models.UserAccount
#{
ViewBag.Title = "Trial";
}
<h2>Trial</h2>
#using (Html.BeginForm("Edit", "Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>UserAccount</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.AccountID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountID)
#Html.ValidationMessageFor(model => model.AccountID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AccountName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountName)
#Html.ValidationMessageFor(model => model.AccountName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RegistrationCode, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RegistrationCode)
#Html.ValidationMessageFor(model => model.RegistrationCode)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Created, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Created)
#Html.ValidationMessageFor(model => model.Created)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
You need a POST action for Trial method like below:
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
//Store the form data into your database
}
return View(model);
}
Then in your view, add a submit button element inside the form, also instead of Edit, you just need to use Trial for the postback.
#using (Html.BeginForm("Trial", "Home")) {
//
<input type="submit" value="Submit"/>
}
Note: You don't need to create other Edit action method if you don't have some other reasons.
If you don't know how to save the data to your database, below is an example:
Create your DbContext class
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=YourDbConnection")
{
}
public DbSet<UserAccount> UserAccounts { get; set; }
}
Then the action method will looks like:
public class HomeController : Controller
{
//
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
using (var db = new MyDbContext())
{
db.UserAccounts.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(model);
}
}

IValidatableObject.Validate does not fire if DataAnnoations add ValidationResult

With a standard ASP.NET MVC controller and view and a model that both implements IValidatableObject and has DataAnnotations, the Validate method never fires if the DataAnnotations generate an exception.
Here's the model...
public class ModelStaticDA : IValidatableObject {
public long Id { get; set; }
[EmailAddress]
public string EmailAddress { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
yield return new ValidationResult("MODEL NOT VALID!")
}
}
Here's the view (client validation is disabled for this demo)...
#model BindingAndValidation.Models.ModelStaticDA
#{
ViewBag.Title = "Create";
HtmlHelper.ClientValidationEnabled = false;
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ModelStaticDA</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.EmailAddress, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If you post something like "invalid" to EmailAddress, only the DataAnnotation message displays. If you post a valid e-mail address, the message from Validate displays.
Is this the correct behavior? If so, why? If not, what am I doing wrong?
You are doing everything right, that's the behavior. My guess it was designed this way to avoid having to validate again while working with the properties inside the Validate method, you know that when it's called you are working with valid data, and you can do things that require valid data.

ASP.NET MVC5 - Image upload to SQL Server 2008R2 Standard

I am semi-new to ASP.NET MVC, I have used it in the past but not to the extent of what I have been planning to use it for and to learn from for some projects I am working on. I know this question has been asked a lot over the internet and there are many solutions so I will try and keep this as specific as possible regarding uploading images and storing them in a SQL Server database.
I am currently using .NET 4.5 with MVC5 (VS 2013) to do all my coding.
To begin I found a great tutorial that got me up and running with being able to upload images to my SQL Server 2008 database: http://www.mikesdotnetting.com/Article/125/ASP.NET-MVC-Uploading-and-Downloading-Files
Everything works great after I figured out how the functions worked from the site, but the issue I am having is that I have multiple text fields that I want to include data from into the SQL Server database along with including an image upload feature that I created based off the tutorial I found online.
The first part of my code is the Models (BeerList.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace YBPP_Production.Models
{
public class BeerList
{
public int ID { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required(ErrorMessage = "Company is required.")]
public string Company { get; set; }
[Required(ErrorMessage = "Type is required.")]
public string Type { get; set; }
[Required(ErrorMessage = "City is required.")]
public string City { get; set; }
[Required(ErrorMessage = "State is required.")]
public string State { get; set; }
[Required(ErrorMessage = "Country is required")]
public string Country { get; set; }
public string ABV { get; set; }
public string IBU { get; set; }
}
public class Info : DbContext
{
public DbSet<DBName> MoreDB { get; set; }
}
}
The string listed there are the text fields I am trying to pull into my database and I can do that but when I try to mix in the image upload feature either the text will upload or the image will upload depending on how I do the calls.
The Controllers section of my code (BeerListController.cs)
// GET: /BeerList/Create
public ActionResult Create()
{
return View();
}
// POST: /BeerList/Create
// 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 ActionResult Create([Bind(Include = "ID,Name,Company,Type,City,State,Country,ABV,IBU")] BeerList beerlist)
{
if (ModelState.IsValid)
{
db.BeerListDB.Add(beerlist);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(beerlist);
}
public ActionResult FileUpload(HttpPostedFileBase file)
{
//Begin the Image Uploading Process
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string mimeType = Request.Files[upload].ContentType;
Stream fileStream = Request.Files[upload].InputStream;
string fileName = Path.GetFileName(Request.Files[upload].FileName);
int fileLength = Request.Files[upload].ContentLength;
byte[] fileData = new byte[fileLength];
fileStream.Read(fileData, 0, fileLength);
const string connect = #"Server=localhost;database=<database-name>;uid=<username-here>;pwd=<there-a-passwordhere>";
using (var conn = new SqlConnection(connect))
{
var qry = "INSERT INTO BeerLists (FileContent, mimeType, FileName) VALUES (#FileContent, #mimeType, #FileName)";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("#FileContent", fileData);
cmd.Parameters.AddWithValue("#MimeType", mimeType);
cmd.Parameters.AddWithValue("#FileName", fileName);
conn.Open();
cmd.ExecuteNonQuery();
}
}
return View();
}
The view part of my code (Create.cshtml)
#model YBPP_Production.Models.BeerList
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BeerList</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Company, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Company)
#Html.ValidationMessageFor(model => model.Company)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Type, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Type)
#Html.ValidationMessageFor(model => model.Type)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.City, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.State, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.State)
#Html.ValidationMessageFor(model => model.State)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Country, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Country)
#Html.ValidationMessageFor(model => model.Country)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ABV, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ABV)
#Html.ValidationMessageFor(model => model.ABV)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IBU, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IBU)
#Html.ValidationMessageFor(model => model.IBU)
</div>
</div>
<div class="form-group">
<p class="control-label col-md-2">Image Upload:</p>
<div class="col-md-10">
<input type="file" id="ImageUpload" name="ImageUpload" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" name="Submit" id="Submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
So what I am looking to hopefully get help with is being able to merge the function of Create and FileUpload into one function so that the form will take both the text in the text-fields and the image that gets uploaded with it. I have read a bunch of other people's posts and code and they all seem to have their own way to doing things but everyone example never includes text-fields or any other form functions just the basic image upload.
Thank you very very much in advance for any direction/suggestions/help to this issue.
You can create model that contains HttpPostedFileBase, then save your entire form with selected file.
Model:
public class BeerListModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required]
public HttpPostedFileBase file { get; set; }
}
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="form-group">
<p class="control-label col-md-2">Image Upload:</p>
<input type="file" id="file" name="file" />
</div>
<div class="form-group">
<input type="submit" name="Submit" id="Submit" value="Create" class="btn btn-default" />
</div>
</div>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(BeerListModel model)
{
if (ModelState.IsValid)
{
//your logic
}
return View("Create");
}
#Szakus' answer is the overall direction you want to go with respect to uploading a file, however when you asked, " I am still a bit confused as to what I should put as far as my logic for the Controller" you inadvertently got to the root of your problem.
I would strongly recommend you do some research into separation of concerns. Putting raw SQL code in your controller is not a great idea and will open your application to a host of the other issues.
I know this doesn't really answer your question but having been where you are now I can tell you that the old adage, "an ounce of prevention is worth a pound of cure" will save you major headaches down the road.
If you want to see an example of a really good .net MVC project that you can use as sort of a template in building your skills I would recommend the open source shopping cart nopCommerce.

Resources