Passing Image to Controller ASP.NET MVC - c#

I am trying to send Image and ImageName from View to Controller.
Here's how my controller looks:
[HttpPost]
public ActionResult Add(BoxAddViewModel image)
{
//TODO: Add new box
return RedirectToAction("Index");
}
This is the model:
public class BoxAddViewModel : BaseViewModel
{
public int Id { get; set; }
[Required]
[DisplayName("Name")]
public string Name { get; set; }
[Required]
[DisplayName("Source")]
public HttpPostedFileBase Source { get; set; }
}
And finally the view:
#using (Html.BeginForm("Add", "BoxManagement", new { #class = "form-horizontal", enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "col-sm-2 control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control", #name = "Name"})
#Html.ValidationMessageFor(m => m.Name)
</div>
#Html.LabelFor(m => m.Source, new { #class = "col-sm-2 control-label" })
<div class="col-sm-10">
<!--Html.TextBoxFor(m => m.Source, new { type = "file",}) -->
#Html.ValidationMessageFor(m => m.Source)
<input type="file" name="Source" id="Source" />
</div>
</div>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span>Add</button>
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn btn-default" })
}
It comes into the Add method and the Name property value is correct but the Source is null.
Anyone know how to solve this?

Your using the wrong overload of BeginForm() and adding route values, not html attributes (always inspect the html your generating). Use
#using (Html.BeginForm("Add", "BoxManagement", FormMethod.Post, new { #class = "form-horizontal", enctype = "multipart/form-data" }))
Side note: Remove new { #name = "Name"} from your TextBoxFor() method. Never attempt to override the name attribute generated by the HtmlHelper methods unless you want binding to fail (and is this case it does nothing anyway)

Related

Why HttpPostedFile is coming null in action method from view?

I am trying to upload a file but while saving it throws error in action i.e. object reference not set and also the value is NULL.
It's the same class that is getting passed and saved but stil it throws error. Why it's null? It is binded to the same viewmodel.
#model VAILCertificates.UI.ViewModels.AddInspectionReportViewModel
#using (Html.BeginForm("Create", "InspectionReport", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(model => model.File, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.TextBoxFor(model => model.File, new { #class = "form-control", type = "file" })
#Html.ValidationMessageFor(model => model.File, "", new { #class = "text-danger" })
</div>
</div>
<button class="btn btn-success btn-outline btn-block" type="submit"><i class="fa fa-save" style="font-size:medium"></i> Add</button>
}
Action:
public ActionResult Create(AddInspectionReportViewModel model, HttpPostedFileBase FileName)
{
string FilePath = Server.MapPath("~/Downloads/Certificates/");
model.File.SaveAs(FilePath + Path.GetFileName(model.File.FileName));
model.InspectionReport.FileName = Path.GetFileName(model.File.FileName);
InspectionReportDAL.AddInspectionReport(model.InspectionReport);
return RedirectToAction("Index");
}
Viewmodel:
public class AddInspectionReportViewModel
{
public HttpPostedFile File { get; set; }
public InspectionReport InspectionReport { get; set; }
}
Class:
public class InspectionReport
{
//General Details
public int InspectionReportID { get; set; }
public string FileName { get; set; }
}
Update: the internal error is
The parameter conversion from type 'System.Web.HttpPostedFileWrapper' to type 'System.Web.HttpPostedFile' failed because no type converter can convert between these types.
HttpPostedFileBase FileName
model.File
The parameter name the method is expecting is FileName.
Edit this #Html.TextBoxFor(model => model.File, new { #class = "form-control", type = "file" })
to
#Html.TextBoxFor(model => model.File.FileName , new { #class = "form-control", type = "file" })
Based on the comments I would do the following if possible.
If I were you I would remove the #Html.TextBoxFor(model => model.File, new { #class = "form-control", type = "file" }) and replace it with:
<input type="file" name="FileName" id="fileUpload or whatever" accept="//Whatever file you want to accept">
At the current state you are never passing anything to the HttpPostedFileBase parameter, just the model and so it is always null.
Change viewModel to:
public class AddInspectionReportViewModel
{
[DataType(DataType.Upload)]
public HttpPostedFileBase File { get; set; }
public InspectionReport InspectionReport { get; set; }
}
change view to:
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(model => model.File, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.TextBoxFor(m => m.File, new { type = "file" })
#Html.ValidationMessageFor(model => model.File, "", new { #class = "text-danger" })
</div>
</div>
<button class="btn btn-success btn-outline btn-block" type="submit"><i class="fa fa-save" style="font-size:medium"></i> Add</button>
}
Changing HttpPostedFile to HttpPostedFileBase has worked.
public class AddInspectionReportViewModel
{
public HttpPostedFileBase File { get; set; }
public InspectionReport InspectionReport { get; set; }
}

How to post objects in ViewModel?

I've got this viewmodel class with an integer Id and an object type.
public class MyViewModel
{
[Required]
public int MyId { get; set; }
public MyObject MyObject { get; set; }
}
MyObject Model is:
public class MyObject
{
[Key]
public int ObjId { get; set; }
public int Number { get; set; }
public string Name { get; set; }
}
This controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MyId, MyObject.Number, MyObject.Name")]MyViewModel vm)
{
if (!ModelState.IsValid)
{
return View();
}
//do something!
}
The view is:
#model MyProject.Models.MyViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.LabelFor(model => model.MyId , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("MyId", null, "Select", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.MyId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MyObject.Number, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.MyObject.Number, new { #class = "form-control", type = "number", min = "1", max = "3", step = "1" })
#Html.ValidationMessageFor(model => model.MyObject.Number, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.MyObject.Name , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.MyObject.Name , new { #class = "form-control", type = "number", min = "1", max = "3", step = "1" })
#Html.ValidationMessageFor(model => model.MyObject.Name , "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" formaction="#Url.Action("Create")" />
</div>
</div>
}
When I post data to controller, it recognize the MyId value but don't fill MyObject parameter. Any suggests to how to post an object with ViewModel?
Assuming that you have #model MyViewModel at the top of the view, there's no need to complicate it.
Make your submit on the view
<input type="submit" value="Create" class="btn btn-default" />
and change the signature of the post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(MyViewModel vm)
{
if (!ModelState.IsValid)
{
return View();
}
//do something!
}
The view model should then be bound.
Try below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MyId,MyObject, MyObject.Number, MyObject.Name")]MyViewModel vm)
{
if (!ModelState.IsValid)
{
return View();
}
}
You aren't including MyObject in Bind(include)

MVC Razor DropDownListFor value not set in model

I'm trying to make a razor view that can post a model to my controller.
I've added a dropdown, however platform is always null when i post to my controller.
What am i doing wrong?
This is my view
#using (Html.BeginForm("Register", "Home", FormMethod.Post, new { #class = "form-horizontal", #id = "form" }))
{
#{
var platformList = new List<SelectListItem>() {
new SelectListItem(){ Value="I", Text="iOS"},
new SelectListItem(){ Value="A", Text="Android"},
};
}
<div class="form-group">
#Html.LabelFor(model => model.platform, "Plattform", new { #for = "inputPlatform", #class = "col-lg-3 control-label" })
<div class="col-lg-9">
#Html.DropDownListFor(model => model.platform, platformList, new { #class = "form-control", #id = "inputPlatform" })
</div>
</div>
}
This is my model
public class RegistrationModel
{
public String platform { get; set; }
}
My Controller
[HttpPost]
[AllowAnonymous]
public ActionResult Register(RegistrationModel RegistrationModelViewModel)
{
}
I couldn't get your view to work. There appears to be a formatting issue with the drop down declaration. It has an extra comma and was missing a end }. I kept getting a parse error, which is odd as you say you can get the post to work.
Anyway, I've created an example below which works and so I hope is of some use.
Model
public class RegistrationModel
{
public string platform { get; set; }
}
View
#model TestMVC.Models.RegistrationModel
#using (Html.BeginForm("Register", "Register", FormMethod.Post, new { #class = "form-horizontal", #id = "form" }))
{
var platformList = new List<SelectListItem>() {new SelectListItem(){ Value="I", Text="iOS"}, new SelectListItem(){ Value="A", Text="Android"}};
<div class="form-group">
#Html.LabelFor(model => model.platform, "Plattform", new {#for = "inputPlatform", #class = "col-lg-3 control-label"})
<div class="col-lg-9">
#Html.DropDownListFor(model => model.platform, platformList, new {#class = "form-control", #id = "inputPlatform"})
</div>
</div>
<button type="submit"> Submit</button>
}
Controller
public class RegisterController : Controller
{
[HttpGet]
public ActionResult Register()
{
return View();
}
[HttpPost]
public ActionResult Register(RegistrationModel model)
{
//Do something here;
}
}

mvc begin form cant make routing override work

simnilar to the answer of this question
Html.BeginForm with html attributes asp.net mvc4
I have a viewmodel for a view that contains collections that are used to populate drop downs and lists. so i dont watn to return them, i just want to return the model object. Well actually i just want to return 4 fields in that model - but that's the next problem.
I've dodged that rpeviously by doing this appraoch but im having no luck unless i submit the entire viewmodel which on this form is ridiculous as 95% of info is discarded.
Anyway the problem i get here is that i cannot get the game event that is returned in the create post to be anything other than null. The gameEvent parameter on create is NULL.
Also kinda suprised i haven't been able to find a ton of info on this.
The controller:
public ActionResult Create()
{
...
var createEventViewModel = new CreateEventViewModel()
{
Places = places,
Characters = characters,
Event = new GameEvent()
};
return this.View(createEventViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Description,EventType,Duration")] GameEvent gameEvent)
{
...
}
The View:
#model Sisyphus.Web.Models.CreateEventViewModel
#{
ViewBag.Title = "Create Event";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Event</h2>
<div class="row">
<div class="col-lg-8">
<section id="createEvent">
#using (Html.BeginForm("Create", "Event",
new
{
GameEvent = Model.Event
}, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Event.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Event.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Event.Description, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextAreaFor(m => m.Event.Description, 10, 30, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Event.Duration, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Event.Duration, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Duration, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Event.EventType, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(m => m.Event.EventType)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create Event" class="btn btn-default" />
</div>
</div>
}
</section>
</div>
</div>
The Model:
public class GameEvent
{
public string Name { get; set; }
public string Description { get; set; }
public int Duration { get; set; }
public EventType EventType { get; set; }
}
The viewmodel: (edited down have removed members that are irrelevant
public class CreateEventViewModel
{
public GameEvent Event { get; set; }
}
Edit:
Ok i just tried this
#using (Html.BeginForm("Create", "Event",
new RouteValueDictionary()
{
{"GameEvent", Model.Event}
}, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
Game event is now not null (All values in it are) - so not really any closer
Your inputs for postback are based on class CreateEventViewModel, for example
#Html.TextBoxFor(m => m.Event.Name, ...
#Html.TextAreaFor(m => m.Event.Description, ...
which would generate the following html
<input id="Event_Name" name="Event.Name" value=....
However the parameter of your post action method is typeof GameEvent, not CreateEventViewModel. If you inspect the Response.Form.Keys you will see Event.Name, Event.Description etc, but class GameEvent has properties Name, Description etc so the values cant be matched up by the ModelBinder
You need to change your post method to
public ActionResult Create(CreateEventViewModel model)
{
GameEvent event = model.GameEvent;
// do whatever with GameEvent
You should also remove new {GameEvent = Model.Event} from theHtml.BeginForm` method
Note I excluded the BindAttibute because I don't think its necessary in this case - you appear to want all the properties of GameEvent, and unless you create inputs for properties of Places and Characters, they will be null anyway, and since you are not accessing the other properties there is no mass assignment vulnerability.
Other alternative are to create the inputs manually so that the properties are correctly mapped, either direct html
<input name="Name" value=#Model.Event.Name />
<input name="Description" value=#Model.Event.Desciption />
or using helpers
var Description = Model.Event.Description;
#Html.TextBoxFor(m => Description)

Mvc ActionResult not passing back object on Post request

My resister works perfectly when passing back the form, but one that I built it passes back nothing. I am not sure what I am doing differently than the register page that visual studio provided. It calls the right http post method but the value passed is null. thanks for any help. I just want that model to post back to the controller action result.
Controller
[HttpGet]
public ActionResult Support()
{
ViewBag.Message = "Your app description page.";
return View();
}
[AllowAnonymous]
[HttpPost, ValidateSpamPrevention]
public ActionResult Support(QuickEmailModel email)
{
if (ModelState.IsValid)
{
return View("thankyou", email);
}
return View(email);
}
Model
public class QuickEmailModel
{
[DataType(DataType.EmailAddress)]
[EmailAddress]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Display(Name = "Company (Optional):")]
public string Company { get; set; }
}
}
Top of View
#using PoliteCaptcha
#model Models.QuickEmailModel
#{
ViewBag.Title = "Support";
}
Bottom where the form is
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div class="control-group">
<label class="control-label">
#Html.LabelFor(x => x.Company, "Company (Optional):", new { #class = "control-label" })</label>
<div class="controls">
#Html.TextBoxFor(x => x.Company, new { #class = "span4" })
</div>
</div>
<div class="control-group">
#Html.LabelFor(x => x.Email, "Email:", new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(x => x.Email, new { #Class = "span4" })
</div>
</div>
<div class="control-group">
<label class="control-label">
#Html.LabelFor(x => x.Subject, "Subject:", new { #class = "control-label" })</label>
<div class="controls">
#Html.TextBoxFor(x => x.Subject, new { #class = "span4" })
</div>
</div>
<div class="control-group">
<label class="control-label">
#Html.LabelFor(x => x.Subject, "Description:", new { #class = "control-label" })</label>
<div class="controls">
#Html.TextAreaFor(x => x.Description, new { #class = "span4", #rows = "6", id = "txtDescription" })
</div>
</div>
#Html.SpamPreventionFields()
<input type="submit" class="btn btn-success" id="btnSubmit" value="Submit" style="margin-right: 15em;
float: right;" />
}
I believe the problem is that your parameter name is the same as one of your field names (email). Rename the Method parameter name to model (or something else besides email).
What I believe is happening is that the MVC model binder sees a posted value named "email" and it's trying to assign it to the parameter of the same name, but since it's not a string it can't do it. Thus it assigns a null.
Also, model binding is case insensitive, so Email and email are the same.
The problem is that the model mapper cannot map form name/value pairs from the POST request to an instance of QuickEmailModel object, and simply ignores them. Examine the POST request being sent in Firebug or other tool, what are the names of name/value pairs?
You should probably change your view like so:
#{ var email = ....; }
<label class="control-label">
#Html.LabelFor(x => email.Company, "Company (Optional):", new { #class = "control-label" })
</label>
<div class="controls">
#Html.TextBoxFor(x => email.Company, new { #class = "span4" })
</div>
because the name of your POST action param is "email". I think that this should force form param names to be "email.Company", etc. If not, try the other variant
#Html.TextBox("email.Company", model.Company)

Categories

Resources