HttpPostedFileBase not binding to model - asp.net-mvc

here is my ViewModel
public class FaultTypeViewModel
{
[HiddenInput(DisplayValue = false)]
public int TypeID { get; set; }
[Required(ErrorMessageResourceType = typeof(AdministrationStrings), ErrorMessageResourceName = "FaultTypeNameRequired")]
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeName")]
public string TypeName { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeDescription")]
[DataType(DataType.MultilineText)]
public string TypeDescription { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeImageFile")]
public HttpPostedFileBase TypeImageFile { get; set; }
[HiddenInput(DisplayValue = false)]
public string TypeImageURL { get; set; }
}
Notice I have a "TypeImageFile" HttpPostedFileBase
I would expect that the model binder would bond that property from the form to the model passes to the controller bu I just keep receiving null.
here is the relevant code in the View:
#using (Html.BeginForm("AddFaultType","Administration", FormMethod.Post))
{
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×</button>
<h3 id="myModalLabel">#SharedStrings.Add #SharedStrings.FaultType</h3>
</div>
<div class="modal-body">
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.TypeName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeName)
#Html.ValidationMessageFor(model => model.TypeName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeDescription)
#Html.ValidationMessageFor(model => model.TypeDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeImageFile)
</div>
<div class="editor-field">
<input type="file" name="TypeImageFile" id="TypeImageFile" />
</div>
</div>
<div class="modal-footer">
<input type="submit" value="#SharedStrings.Add" class="btn btn-primary" />
#Html.ActionLink(SharedStrings.Cancel, "Index", "Administration", null, new { Class = "btn", data_dismiss = "modal", aria_hidden = "true" })
</div>
}
and here is the controller:
[HttpPost]
public ActionResult AddFaultType(FaultTypeViewModel i_FaultToAdd)
{
var fileName = Path.GetFileName(i_FaultToAdd.TypeImageFile.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
i_FaultToAdd.TypeImageFile.SaveAs(path);
return RedirectToAction("Index");
}

Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}

Completing Darin's answer:
Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
To ensure your <input> is transmitted to the controller as part of the model use the Html Helpers for Id and name like below:
<input type="file" id="#Html.IdFor(x=>x.HttpPostedFileBase)" name="#Html.NameFor(x=>x.HttpPostedFileBase)" accept=".csv,.txt"/>
Works in MVC5 sorry I cant find any reference to which helpers are available in MVC3

Related

How to use SweetAlert before return RedirectToAction by Form Post?

Model
public class Company{
[StringLength(30)]
public string Name{ get; set; }
[StringLength(15)]
public string RegisterNo { get; set; }
}
View
<form id="form" method="post" action="/Controller/Save" style="font-size:13px;">
<div class="row pb-3">
<div class="form-row">
<div class="col-md-3">
<div class="position-relative form-group">
<label class="">Company Name</label> #Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", #maxlength = "30" } })
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="position-relative form-group">
<label class="">Reg No</label> #Html.EditorFor(model => model.RegisterNo, new { htmlAttributes = new { #class = "form-control", #maxlength = "15" } })
</div>
</div>
</div>
</div>
</form>
<button name="submit">Save</button>
Controller
public ActionResult Save(Company CompDet){
string Name = CompDet.Name;
string RegNo = CompDet.RegisterNo;
//then Connect DB and Save DB
//HOW TO use SweetAlert before, return RedirectToAction
}
I'm currently using, return RedirectToAction Only.
How to add SweetAlert before RedirectToAction?
Using Ajax Post or Form Post?
Possible that using Ajax & Form Post Together?
After Saving -> Show Sweetalert success -> return redirectToAction
I'm still at the learning level. Please Help.
Thank You

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>

Upload image included in MVC model

I have the following model:
public class Photo
{
public int PhotoId { get; set; }
public byte[] ImageData { get; set; }
public DateTime DateUploaded { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
}
I would like the user to be able to enter the details for the photo then post the model the the controller. My controller action is as follows:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
My view is as follows:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Photo</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.ImageData, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="uploadImages" class="input-files" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateUploaded, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateUploaded)
#Html.ValidationMessageFor(model => model.DateUploaded)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsActive, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IsActive)
#Html.ValidationMessageFor(model => model.IsActive)
</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>
}
The view is displayed ok and allows the user to select a file from their local disk and enter the other model details.
My problem is that although the model is posted to the controller ok, the Description, Date and IsActive flags are populated ok - the Image data is null.
Could anyone please let me know what I need to change so that the byte array for the photo is included in the model posted to the controller?
The file input in your view has a name uploadImages. I can't see a property with this name in your view model. You seem to have some ImageData property which is a byte array, but there doesn't seem to be a corresponding input field with this name in your view.
This explains why you get null. You could make this work by respecting the convention. So for example if you intend to have such an input field in your view:
<input type="file" name="uploadImages" class="input-files" />
then make sure that you have a property on your view model with the same name. And of course of type HttpPostedFileBase.
public HttpPostedFileBase UploadImages { get; set; }
Also in your view make sure you are setting the proper content type of multipart/form-data:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
You probably might want to go through the following blog post to better familiarize yourself with the basics of how uploading of files work in ASP.NET MVC. I've also written a similar answer here that you might consult.
So once you add the HttpPostedFileBase property with the UploadImages name in your view model you could adapt your controller action to read the byte array and store it your ImageData property:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
photo.ImageData = new byte[photo.UploadImages.ContentLength];
photo.UploadImages.Read(photo.ImageData, 0, photo.ImageData.Length);
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
Now bear in mind that this is an absolutely awful solution. Never do that in a real world application. In a correctly designed application you will have a view model that your controller action will take as a parameter. You're never gonna directly use your autogenerated EF model as parameter to your controller action. You will have a view model with the HttpPostedFileBase property which will be mapped to your domain model.
So in a properly designed application you will have a PhotoViewModel view model class that your controller action will take.
Change this line:
#using (Html.BeginForm())
To this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
Then change:
<input type="file" name="uploadImages" class="input-files" />
To:
<input type="file" name="ImageData" class="input-files" />
Then change this line:
public byte[] ImageData { get; set; }
To this:
public HttpPostedFileBase ImageData { get; set; }
Finally, use some code like this to read the image into a byte array:
var bs = new byte[ImageData.ContentLength];
using (var fs = ImageData.InputStream)
{
var offset = 0;
do
{
offset += fs.Read(bs, offset, bs.Length - offset);
} while (offset < bs.Length);
}
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
<input type="file" id="ImageFile" name="ImageFile" .../>
...
}
Controller:
[HttpPost]
public ActionResult Create(Photo photo, HttpPostedFileBase ImageFile)
{
byte[] buf = new byte[ImageFile.ContentLength];
ImageFile.InputStream.Read(buf, 0, buf.Length);
photo.ImageData = buf;
...
}

ASP.NET MVC: Values are null when they reach the Controller

So I have the following Controller:
[HttpPost]
public ActionResult CreateSupport(CreateSupport model)
{
if (ModelState.IsValid && (model.Description != null))
{
model.CreatedById = UserId;
model.ModifiedById = UserId;
}
return View(model);
}
I have the following view:
#using (Html.BeginForm("CreateSupport", "Support", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="editor-label">
#Html.LabelFor(model => model.Subject, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Subject)
#Html.ValidationMessageFor(model => model.Subject)
</div>
<div class="support-form-left">
<div class="editor-label">
#Html.LabelFor(model => model.BrowserInfo, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.BrowserInfo)
#Html.ValidationMessageFor(model => model.BrowserInfo)
</div>
</div>
<div class="support-form-right">
<div class="editor-label">
#Html.LabelFor(model => model.DatabaseVersion, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.DatabaseVersion)
#Html.ValidationMessageFor(model => model.DatabaseVersion)
</div>
</div>
<div class="clearFloat"></div>
<div class="editor-label">
#Html.LabelFor(model => model.Description, new Dictionary<string, object>() { { "class", "req" } })
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="actionButtons">
<button id="btnCancel" class="myButtonCancel">Cancel</button>
<input type="submit" value="Submit" class="myButton" />
</div>
#if (ViewBag.SuccessMessage != null)
{
<div>
<label style="color: red;">#ViewBag.SuccessMessage</label>
</div>
}
</fieldset>
}
Here's the Model:
public class CreateSupport : SupportTicket
{
public CreateSupport()
{
ProductList = new List<Product>();
ProductVersionsList = new List<ProductVersion>();
EnviromentList = new List<InstallationEnvironment>();
content = new Content();
}
[Required]
[UIHint("tinymce_jquery_full"), AllowHtml]
public string Description { get; set; }
[Required]
[DisplayName("Browser version Info.")]
public string BrowserInfo { get; set; }
[Required]
[DisplayName("Database Version")]
public string DatabaseVersion { get; set; }
public Content content { get; set; }
}
The problem is that the values that reach the Controller are NULL even if you enter some value in them.
You should check your browser's developer tools to see if the form is properly posting its values. If it isn't, you should do two things:
A) Disabled javascript to see if there is a script that is interfering with the POST (typically either by disabling or clearing fields)
B) Ensuring your markup is valid using the W3C markup validation service
For input fields use
#Html.EditorFor(x => x.Subject)
For display fields use
#Html.DisplayFor(x => x.Subject)

DateTime required in ModelState, however never set to be

I have a DateTime field in my form. For whatever reason, everytime I submit the form ModelState comes back as invalid and a message that my date time field (called PostDate) is required, even though in the view model I don't have the required attribute set.
Does anyone know why this would be happening as I'm going around in circles trying to figure it out.
Here is the view model
public class BlogViewModel
{
[Required]
public string Title { get; set; }
[Required]
public string Content { get; set; }
public bool Published { get; set; }
public DateTime PostDate { get; set; }
}
Here is the controller actions
public ActionResult Create()
{
return View();
}
//
// POST: /Admin/Blog/Create
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(BlogViewModel model)
{
if(ModelState.IsValid)
{
model.Content = HtmlSanitizer.SanitizeHtml(model.Content);
Services.BlogService.CreateBlogPost(model.Title, model.Content, User.Identity.Name);
return RedirectToAction("Index");
}
return View(model);
}
Here is the View
#using Payntbrush.Infrastructure.Mvc.Extensions
#model Payntbrush.Presentation.Demo.MVC3.Areas.Admin.Models.BlogViewModel
#Html.Resource(Html.ScriptTag("Areas/Admin/js/plugins/wysiwyg/jquery.wysiwyg.js"), ResourceType.Js)
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm((string)ViewBag.Action, "Blog", FormMethod.Post, new { Id = "BlogEditor" }))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Content)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Content, new { #class = "wysiwyg blog-editor" })
#Html.ValidationMessageFor(model => model.Content)
</div>
<div class="editor-label">
Time of post (Only set this if you want to make a post appear to have been published at a different date.)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.PostDate, new{#class="datetimepicker"})
#Html.ValidationMessageFor(model => model.PostDate)
</div>
<div class="editor-label">
Published (Check to make this post live on the site)
</div>
<div class="editor-field">
#Html.CheckBoxFor(model => model.Published)
#Html.ValidationMessageFor(model => model.Published)
</div>
<p>
<input class="large awesome" type="submit" value="Create" />
#Html.ActionLink("Cancel", "Index", "Blog", null, new { #class = "large awesome cancel-button" })
</p>
}
<div>
</div>
If you leave the corresponding field empty your model will be invalid because DateTime is a value type. You should use a nullable DateTime if you want to allow empty values:
public DateTime? PostDate { get; set; }

Resources