mvc3 file upload - asp.net-mvc-3

Whenever I try to upload a file to a server the current View is redirected to a different View from the controller. How can I upload a file and stay on the same View.
I have tried the following code:
public Action Result(HttpPostedFileBase file)
{
return new EmptyResult();
}

Return View();
Should work as you'd expect, returning the View named Result.
If the current Action Method isn't the view you'd like to return you can use:
return RedirectToAction("actionmethodname");

I would suggest using something like plupload for async upload. That way you can upload without redirect and even view image/document when upload is complete.
It allows multiple upload and fallback through different methods to successfully upload a file.
For implementation you just create another controller just for handling uploads.

Check my code for article submission in MVC architechture.
public ActionResult Submit(ArticleViewModel newSubmit, HttpPostedFileBase uploadFile)
{
if (ModelState.IsValid)
{
//Upload File
if (uploadFile != null)
{
string fileName = uploadFile.FileName;
newSubmit.Article.image = fileName;
uploadFile.SaveAs("~/Content/Uploads/Images");
string savedFileName = Path.Combine(Server.MapPath("~/Content/Uploads/Images"), uploadFile.FileName);
}
// The HTML comes encoded so we decode it before insert into database
newSubmit.Article.content = HttpUtility.HtmlDecode(newSubmit.Article.content);
//Set article flags
newSubmit.Article.flagged = true;
newSubmit.Article.finished = false;
newSubmit.Article.submitStoryFlag = true;
//Insert article in the database _repository.AddArticle(newSubmit);
return View("Submitted");
}
else
{
// Invalid – redisplay with errors
return View(newSubmit);
}
}

Suppose your view name is UploadView.cshtml and from there, you are uploading file.
UploadView.cshtml
#using (Html.BeginForm("UploadFile", "MyController", FormMethod.Post, new { enctype = "multipart/form-data", id = "frm", name = "frm" }))
{
<input id="FileAttachments" type="file" name="FileAttachments" />
<input type="submit" value="upload" />
}
Your Controller would be
MyController.cs
[HttpGet]
public ActionResult UploadView()
{
Return View();
}
[HttpPost]
public ActionResult UploadFile(HttpPostedFileBase FileAttachments)
{
if (FileAttachments != null)
{
string fileName = System.Guid.NewGuid().ToString() + Path.GetFileName(FileAttachments.FileName);
fileName = Path.Combine(Server.MapPath("~/Content/Files"), fileName);
FileAttachments.SaveAs(fileName);
}
return View("UploadView");
}

Related

Model is null on postback in ajax loaded partial view

I'm using the following pattern https://github.com/filamentgroup/Ajax-Include-Pattern
to load partial views through ajax.
View:
#using(Html.BeginUmbracoForm("PostContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
<div data-append="#Url.Action("RenderJoiningContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })"></div>
}
With Action:
public ActionResult RenderContactInformation(int ContentId)
{
var viewModel = ContactViewModel();
viewModel.Content = Umbraco.TypedContent(ContentId);
return PartialView("RenderContactInformation", viewModel);
}
Loads partial view perfectly.
// No need to add partial view i think
Post action works correctly as well:
public ActionResult PostContactInformation(ContactViewModel model)
{
//code here
return RedirectToUmbracoPage(pageid);
}
The problem is, that i need to add model error to CurrentUmbracoPage if it exists in post...
For example:
public ActionResult PostContactInformation(ContactViewModel model)
{
ModelState.AddModelError(string.Empty, "Error occurred");
return CurrentUmbracoPage();
}
In this case i get null values for current model. And this happens only when i use ajax.
If i load action synchronously like that:
#using(Html.BeginUmbracoForm("PostJoiningContactInformation", "JoiningSurface", null, new Dictionary<string, object> { { "class", "joinform" } })) {
#Html.AntiForgeryToken()
#Html.Action("RenderContactInformation", "JoiningSurface", new { ContentId = CurrentPage.Id })
}
everything works like it should.
But i need to use ajax. Is there a correct way to pass values on postback in this case? I know that i can use TempData, but i'm not sure that this is the best approach.
Thanks for your patience
The problem is that Umbraco context is not accessible when you're trying to reach it through ajax call. Those calls are a little bit different.
Check my answer in this thread: Umbraco route definition-ajax form and I suggest to go with WebAPI and UmbracoApiControllers to be able to access those values during the Ajax call.

How to send data from view to controller in MVC3

Hello everyone I would like to ask how to send data from view to controller ? I would like to describe my question with my controller and view as you can see below
Here is the login action controller
[HttpPost]
public ActionResult Login(Panel model, string Username, string Password, string CaptchaValue, string InvisibleCaptchaValue)
{
bool cv = CaptchaController.IsValidCaptchaValue(CaptchaValue.ToUpper());
bool icv = InvisibleCaptchaValue == "";
if (!cv || !icv)
{
ModelState.AddModelError(string.Empty, "The Captcha Code you entered is invalid.");
}
if (ModelState.IsValid)
{
if (model.Username == Username && model.Password == Password)
{
FormsAuthentication.SetAuthCookie(model.Username, false);
return RedirectToAction("index", "Home");
}
else
{
ModelState.AddModelError("", "Check your name or password");
}
}
return View();
}
So when user login, redirect to Home/index view. At this point everything is okey.
Here is my index view:
[Authorize]
public ActionResult index()
{
return View();
}
My question is how can I hold user's password parameter and send from index view to different controller to use this parameter in controller method but how ? For example I would like to use password parameter at my index_test controller method in where clause but first of all I need to send this data from index.
public ActionResult index_test()
{
return View(db.contents.Where(x => x.test_parameter== password).ToList());
}
You have to add a parameter to your action method:
public ActionResult index_test(string password) { ...
In Your view you can either send data to the action via a standard link:
#Html.ActionLink("Click me", "index_test", "Controller",
new { password = "StringOrVariable")
Or by doing a form post:
#using(Html.BeginForm("index_test")) {
<input type="hidden" name="password" value="mypassword" />
add some fields
<input type="submit" value="Send" />
}
In your view post the form back to the controller, for example
<form action = "yourcontroller/youraction" method = "POST" enctype = "multiparrt/form-data">

Rendering same View after Http Post in MVC

Trying to repost a view but im getting null to the field I want.
Heres the controller...
public ActionResult Upload()
{
VMTest vm = new VMTest();
return View(vm);
}
[HttpPost]
public ActionResult Upload(VMTest vm, String submitButton)
{
if (submitButton == "Upload")
{
//do some processing and render the same view
vm.FileName = "2222"; // dynamic creation of filename
vm.File.SaveAs(#vm.FileName); // save file to server
return View(vm);
}
else if (submitButton == "Save")
{
//read the file from the server
FileHelperEngine engine = new FileHelperEngine(typeof(PaymentUploadFile));
PaymentUploadFile[] payments = (PaymentUploadFile[])engine.ReadFile(#vm.FileName); // the problem lays here #vm.FileName has no value during upload
//save the record of the file to db
return View("Success");
}
else
{
return View("Error");
}
}
I already had a #Html.HiddenFor(model => Model.FileName) inside my view.
But still I got a null value for Model.FileName.
Any help pls
Thanks
If you intend to modify some values of your view model in the POST action you need to remove the old value from modelstate first:
ModelState.Remove("FileName");
vm.FileName = "2222";
The reason for this is that Html helpers such as TextBox, Hidden, ... will first use the value in the modelstate when binding and after that the value in your view model.
Also instead of:
#Html.HiddenFor(model => Model.FileName)
you should use:
#Html.HiddenFor(model => model.FileName)
Notice the lowercase m in the expression.
The above answer is a good one. You can also do the following
public ActionResult Upload()
{
VMTest vm = new VMTest();
ModelState.Clear();
return View(vm);
}
I usually call ModelState.Clear() before loading a new view. The sytntax for HiddenFor should be
#Html.HiddenFor(m => m.FileName);
I hope this helps.

RedirectToAction after validation errors

If I have the usual Edit actions, one for GET to retrieve an object by it's ID and to display it in an edit form. The next for POST to take the values in the ViewModel and update the object in the database.
public virtual ActionResult Edit(int id)
[HttpPost]
public ActionResult Edit(VehicleVariantEditSaveViewModel viewModel)
If an error occurs during model binding in the POST action, I understand I can RedirectToAction back to the GET action and preserve the ModelState validation errors by copying it to TempData and retrieving it after the redirect in the GET action.
if (TempData["ViewData"] != null)
{
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
How do I then convert that ViewData, which includes the previous invalid ModelState, into a new model to send to the view so the user sees their invalid input with validation warnings? Oddly enough if I pass in a new instance of my ViewModel retrieved from the database (with the original valid data) to the View() this is ignored and the (invalid) data in the ViewData is displayed!
Thanks
I had a similar problem and decided to use the following pattern:
public ActionResult PersonalRecord(Guid id)
{
if (TempData["Model"] == null)
{
var personalRecord = _context.PersonalRecords.Single(p => p.UserId == id);
var model = personalRecord.ToPersonalRecordModel();
return View(model);
}
else
{
ViewData = (ViewDataDictionary) TempData["ViewData"];
return View(TempData["Model"]);
}
}
[HttpPost]
public ActionResult PersonalRecord(PersonalRecordModel model)
{
try
{
if (ModelState.IsValid)
{
var personalRecord = _context.PersonalRecords.Single(u => u.UserId == model.UserId);
personalRecord.Email = model.Email;
personalRecord.DOB = model.DOB;
personalRecord.PrimaryPhone = model.PrimaryPhone;
_context.Update(personalRecord);
_context.SaveChanges();
return RedirectToAction("PersonalRecord");
}
}
catch (DbEntityValidationException ex)
{
var errors = ex.EntityValidationErrors.First();
foreach (var propertyError in errors.ValidationErrors)
{
ModelState.AddModelError(propertyError.PropertyName, propertyError.ErrorMessage);
}
}
TempData["Model"] = model;
TempData["ViewData"] = ViewData;
return RedirectToAction("PersonalRecord", new { id = model.UserId });
}
Hope this helps.
I noticed that the Model is included in ViewData so you don't need to pass it in addition to the ViewData, what I don't understand is how you get at it to then return it to the view.
public ViewResult Edit(int id)
{
// Check if we have ViewData in the session from a previous attempt which failed validation
if (TempData["ViewData"] != null)
{
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
VehicleVariantEditViewModel viewModel = new VehicleVariantControllerViewModelBuilder()
.BuildForEdit(id);
return View(viewModel);
}
The above works but obviously it's making an unnecessary call to the database to build a new Model (which gets automagically overwritten with the invalid values from the Model in the passed ViewData)
Confusing.

Displaying modelstate error

I am having the following code, However the errors causght not being displayed. What is wrong ?
public ActionResult DeleteRateGroup(int id)
{
try
{
RateGroup.Load(id).Delete();
RateGroupListModel list = new RateGroupListModel();
return GetIndexView(list);
}
catch (Exception e)
{
RateGroupListModel model = new RateGroupListModel();
if (e.InnerException != null)
{
if (e.InnerException.Message.Contains("REFERENCE constraint"))
ModelState.AddModelError("Error", "The user has related information and cannot be deleted.");
}
else
{
ModelState.AddModelError("Error", e.Message);
}
return RedirectToAction("RateGroup", model);
}
}
#model MvcUI.Models.RateGroupListModel
#{
View.Title = "RateGroup";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Rate Group</h2>
#Html.ValidationSummary()
#using (Html.BeginForm())
private ActionResult GetIndexView(RateGroupListModel model)
{
return View("RateGroup", model);
}
public ActionResult RateGroup(RateGroupListModel model)
{
return GetIndexView(model);
}
It looks like you're setting the ModelState error, then redirecting to another action. I'm pretty sure the ModelState gets lost when you do that.
Typically, you'd just render the RateGroup view directly from the DeleteRateGroup action, without the redirect, passing in your model if needed, like this:
return View("RateGroup", model);
If you want the ModelState to come along to the second action with you, take a look at MvcContrib's ModelStateToTempDataAttribute. Here's the attribute's description, from the MvcContrib source code's comments:
When a RedirectToRouteResult is returned from an action, anything in the ViewData.ModelState dictionary will be copied into TempData. When a ViewResultBase is returned from an action, any ModelState entries that were previously copied to TempData will be copied back to the ModelState dictionary.

Resources