How can I get both a model and HttpPostedFileBase into a controller? - asp.net-mvc

I've looked quite a bit online, and every example I see explains how to get IEnumerable<HttpPostedFileBase> into a controller, OR how to get a model, but not both.
What I want is something like:
<form>
<input type="text" name="Stored file name>
<input type="file" multiple="multiple" name="files>
<input type="submit">
</form>
with controller
[ActionName("Index"), HttpPost]
public ActionResult IndexPost(Models.MyModel mdl, IEnumerable<HttpPostedFileBase> files)
{
// Do some something with the data in mdl and files here
return RedirectToAction("Index");
}
But every way I try to implement this, it comes back 'no parameterless handler found'. It works fine if I don't include the model.
Am I missing something really obvious?

I guess you couldn't find Darin's this post. It's not exactly about passing model with HttpPostedFileBase but if you undrerstand the behavior the it's easy to do that.
See below example.
View
#model WebApplication2.Models.MyModel
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(m => m.Id)
<input type="file" name="file" />
<input type="submit" value="OK" />
}
Action Method
[HttpPost]
public ActionResult Index(MyModel model, HttpPostedFileBase file)
{
if (file != null)
{
//do your stuff here
}
return View();
}

Related

asp.net MVC upload file get null in action's parameter [duplicate]

I'm trying to do my first simple file upload in MVC 5. I'm following a bunch of examples I've found but for some reason in my "Create" ActionResult the uploadFile is always coming in as NULL so the upload code is never running. Anyone see what I might be doing wrong?
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Documents.</h2>
<h4>Upload a new document.</h4>
<div class="well">
#using (Html.BeginForm("Create", "Documents", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<h3>Select a file to upload. </h3>
<input type="file" name="files" value="" multiple="multiple" />
<input type="submit" value="Upload your file" title="Upload" />
<div style="color:Red;font-size:14px">#ViewBag.Message</div>
}
</div>
Here is my controller:
// POST: Documents/Create
[HttpPost]
public ActionResult Create(HttpPostedFileBase uploadFile)
{
try
{
if(uploadFile != null && uploadFile.ContentLength > 0)
{
string filePath = Server.MapPath("../SiteDocuments" + uploadFile.FileName);
uploadFile.SaveAs(filePath);
}
return RedirectToAction("Index");
}
catch (Exception ex)
{
return View();
}
}
Your file input element's name should match to your action method parameter name.
So update your HTML markup to have the same name attribute value.
<input type="file" name="uploadFile" value="" multiple="multiple" />
and your action method will be
[HttpPost]
public ActionResult Create(HttpPostedFileBase uploadFile)
{
// do something
}
Or change your action method parameter name to match with your file input element name.
<input type="file" name="files" value="" multiple="multiple" />
and your action method will be
[HttpPost]
public ActionResult Create(HttpPostedFileBase files)
{
if(files!= null && files.ContentLength > 0)
{
// do something
}
}
When you add multiple="multiple" attribute to the input element, the browser will allow the end user to select more than one file at a time. In that case If your action method parameter is a single instance of HttpPostedFileBase object, It will receive the first file from the selected n files. If you want all the files, You may change your parameter to a collection such as
[HttpPost]
public ActionResult Create(IEnumerable<HttpPostedFileBase> files)
{
if (files != null && files.Any())
{
foreach (var file in files)
{
if (file.ContentLength > 0)
{
//do something
}
}
}
}

What should I do when more than one submit cases exist

I have a form where I fill in a blank and then save it, which takes me to the read only version of the entry I've just saved. At this step I have 2 options:
Either "Confirm" or "Cancel". Both actions will update some values in the database, so they're both good candidates for submit button(HttpPost, of course). I know this shouldn't be tied to a particular web development technology as
all-in-all it's all about Request and Response, but still I'll talk in ASP.NET MVC "language" as that is what I'm using now. So far, the only idea that has come to my mind is to have a separate submit buttons with different names and on the server side check for the name and act accordingly. But this seems a bit ugly to me as I might have to have a huge and universal action method. Do you have any better approach? Maybe MVC has some built in way for this purpose?
huge and universal action method
That's entirely your responsibility. You write action methods to map HTTP requests to business logic.
public class PostModel<T>
{
public string SubmitAction { get; set; }
public T TheModel { get; set; }
}
public ActionResult HandleSomePost(PostModel<Foo> model)
{
switch (model.SubmitAction.ToUpperInvariant())
{
case "SAVE":
_yourBLL.Save(model.TheModel);
break;
case "CANCEL":
_yourBLL.Cancel(model.TheModel);
break;
default:
throw new ArgumentException("SubmitAction");
}
return View();
}
Then you can handle both submit buttons from this form:
#using (Html.BeginForm("HandleSomePost", "TheController", FormMethod.Post))
{
#Html.EditorFor(m => m.TheModel)
<input type="submit" name="SubmitAction" value="Cancel">
<input type="submit" name="SubmitAction" value="Save">
}
However, this will quickly become a mess. You can also use attributes to let submit buttons map to action methods (though for simplicity I removed the "action:" prefix used for localization, be sure to read that Q&A and the linked blog):
[HttpPost]
[MultipleSubmit(SubmitAction = "Save")]
public ActionResult Save(Foo model)
{
_yourBLL.Save(model);
return View();
}
[HttpPost]
[MultipleSubmit(SubmitAction = "Cancel")]
public ActionResult Cancel(Foo model)
{
_yourBLL.Cancel(model);
return View();
}
You could do this with 2 forms, each posting to different action methods:
<form method="post" action="/cancelAction">
<input name="id" type="hidden" value="some-id-value">
<input type="submit" value="Cancel">
</form>
<form method="post" action="/confirmAction">
<input name="id" type="hidden" value="some-id-value">
<input type="submit" value="Confirm">
</form>
Or using MVC Razor syntax since you mentioned it:
#using (Html.BeginForm("cancelAction", "MyController", FormMethod.Post))
{
#Html.HiddenFor(model => model.ID)
<input type="submit" value="Cancel">
}
#using (Html.BeginForm("confirmAction", "MyController", FormMethod.Post))
{
#Html.HiddenFor(model => model.ID)
<input type="submit" value="Confirm">
}
From the information above it seems hard to pin down the exact use case here, however it might not be neccessary to have both a post for the confirm and cancel.
Consider using the submit event for "confirmation" and then just call the cancel event using normal HTTP-GET and passing the item that needs to be cancelled's ID? Then you can either handle the confirm or cancel events in either of the Actions directly or do a RedirectToAction to the HugeUniversalAction.
#using (Html.BeginForm("Confirm","ExampleController",FormMethod.Post))
{
<input type/>
<input type="submit" value="confirm"/>
#Html.ActionLink("Cancel", "ExampleController", id = Model.Id)
}
Then in your controller you can call the larger universal method.
public ActionResult Cancel(int id)
{
// Cancel directly
// or
return RedirectToAction("HugeUniversalAction", new { confirm = "false", id = id });
}
[HttpPost]
public ActionResult Confirm(Foo model)
{
// Confirm Directly
// or
return RedirectToAction("HugeUniversalAction", new { confirm = "true", id = model.Id });
}
Then handle the two paths in whichever way you need to in your HugeUniversalAction
public ActionResult HugeUniversalAction(int id, confirm = false){ // If confirm confirm it else cancel it }

Update and ASP.NET MVC model on button click

I'm new to ASP.NET MVC. I'm trying to update model on button click with no success: every time I push the button an HttpGet controller method is invoked.
Here is my markup
#model DataInterface.Model.Entry
<button onclick="location.href='#Url.Action("Survey")'">Finish survey</button>
Here is Controller code
[HttpGet]
public ActionResult Survey()
{
var entry = new Entry();
return View(entry);
}
[HttpPost]
public ActionResult Survey(Entry newEntry)
{
// save newEntry to database
}
When I click button HttpGet method is invoked. Why?
It is bad to be a rookie)
Thanks to all!
If you access a URL without explicitly specifying the HTTP method, ASP.NET MVC will assume a GET request. To change this, you can add a form and send it:
#using (Html.BeginForm("Survey", "Controller", FormMethod.Post))
{
<input type="submit" value="Finish survey" />
}
If you do this, your POST method will be invoked. The Entry parameter, however, will be empty, since you do not specify any values to send along with the request. The easiest way to do so is by specifying input fields, e.g. text inputs, dropdown, checkboxes etc.
#using (Html.BeginForm("Survey", "Controller", FormMethod.Post))
{
#Html.TextBoxFor(m => m.Title)
<input type="submit" value="Finish survey" />
}
If you have the object stored on the server somewhere and only want to finish it off by writing it into the database or changing its status, you could pass the Id of the object (or some temporary Id) along the post request and make the controller method work only with the Id:
#using (Html.BeginForm("Survey", "Controller", FormMethod.Post))
{
#Html.HiddenFor(m => m.Id)
<input type="submit" value="Finish survey" />
}
[HttpPost]
public ActionResult Survey(Entry newEntry)
{
// newEntry.Id will be set here
}
#using (Html.BeginForm("Survey", "<ControllerName>", FormMethod.Post))
{
<input type="submit" value="Finish survey" />
}
you must declare your form
#model DataInterface.Model.Entry
#using (Html.BeginForm("action", "Controlleur", FormMethod.Post, new {#class = "form", id = "RequestForm" }))
{
<input type="submit" value="Finish survey" />
}

HttpPostedFileBase always return null in ASP.NET MVC

I have a problem when I upload a file in ASP.NET MVC.
My code is below:
View:
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index2</h2>
#using (Html.BeginForm("FileUpload", "Board", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" />
<input type="submit" />
}
Controller:
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase uploadFile)
{
if (uploadFile != null && uploadFile.ContentLength > 0)
{
string filePath = Path.Combine(Server.MapPath("/Temp"), Path.GetFileName(uploadFile.FileName));
uploadFile.SaveAs(filePath);
}
return View();
}
But uploadFile always returns null.
Can anyone figure out why??
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index2</h2>
#using (Html.BeginForm("FileUpload", "Board", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="uploadFile"/>
<input type="submit" />
}
you have to provide name to input type file to uploadFile in order to model binding work in ASP.net mvc and also make sure that name of your input type file and argument name of HttpPostedFileBase is identical.
I had tried most of the solutions posted online for this topic, but found it better to use a workaround instead..
It really didn't matter what I did the HttpPostedFileBase and/or HttpPostedFile were always null. Using the HttpContext.Request.Files collection seemed to work with no hassles at all.
e.g.
if (HttpContext.Request.Files.AllKeys.Any())
{
// Get the uploaded image from the Files collection
var httpPostedFile = HttpContext.Request.Files[0];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
// Get the complete file path
var fileSavePath =(HttpContext.Server.MapPath("~/UploadedFiles") + httpPostedFile.FileName.Substring(httpPostedFile.FileName.LastIndexOf(#"\")));
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
In the above example I only grab the first file, but it is just a matter of looping though the collection to save all files.
HTH
Rob
In my scenario the problem was with id attribute, I had this:
<input type="file" name="file1" id="file1" />
The soultion was to remove id:
<input type="file" name="file1" />
While not the answer to this specific user, I would like to point out that HTML requires that the form tag has an enctype attribute with the value multipart/form-data. And of course both the attribute and it's value must be correct.
For mvc, this means that when using beginform, you should use the version with the htmlAttributes parameter
There can be another scenario also. In my case, I was getting this issue because I was directly rendering script tag in my MVC view and IE is giving issue there.
Correct code in view should be as below:
#section scripts
{
<script>
$(document).ready(function () {
$('.fileinput').fileinput();
...
}
You need to use Razor code to set Name of the input file.
<input type="file" name="#Html.Namefor(m => m.propertyName)">

MVC Model validation on "HttpPostedFileWrapper"

Can someone help me with model validation on a HttpPostedFileWrapper object?
Model:
[Required(AllowEmptyStrings = false)]
public HttpPostedFileWrapper BlahFile { get; set; }
Controller:
[HttpPost]
public ActionResult LoadBlahData(BlahModel blahModel)
{
if (!ModelState.IsValid)
return RedirectToAction("Index");
}
cshtml:
#using (Html.BeginForm("LoadBlahData", "Admin", FormMethod.Post, new { #class = "blahhForm", enctype = "multipart/form-data", id = "uploadBlah" }))
{
<fieldset>
<legend>Upload Blah Information</legend>
#Html.LabelFor(x=>x.BlahFile, "Upload Blah file:")
<input size="26" class="uploader" type="file" name="BlahFile" />
<p><input class="ttButton" type="submit" value="Load Stuff" /></p>
</fieldset>
}
Problem:
Cannot see the "data-val*" attributes being added to the html.
Does not set the unobtrusive validation off (red border on input box)
Notes:
Other items in the Model are working fine with validation, its only the <input type="file"/> that seems to be having problems.
Comes into the action method fine - (i.e - i can access the InputStream if i want).
All scripts are referenced correctly (its working on normal text input's)
Thanks in advance,
Just for anyone else coming across this question you can also do this -
<%: Html.TextBoxFor(x => x.BlahFile, new { type = "file" }) %>
you have not added any data attributes to your element. Add it like,
<input data-pk="1" size="26" class="uploader" type="file" name="BlahFile" />
and there is no support of validation of <input type="file"

Resources