Delete Uploaded file in MVC5? - asp.net

I'm working with an MVC5 project, I have created a simple system that the user can upload a file "CV" for each Employee.
Now all thing work for me fine except "DELETING File".
I need to add action method for deleting uploaded file and the ability to replace it with another file.
in the model class I have created two property HttpPostedFileBase CV to save the uploaded file
and String cvName, to save the file name and use it to create a link to that file.
In the controller that what I have done:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteCV(string cvName)
{
//Session["DeleteSuccess"] = "No";
var CVName = "";
CVName = cvName;
string fullPath = Request.MapPath("~/Content/CVs/" + CVName);
if (System.IO.File.Exists(fullPath))
{
System.IO.File.Delete(fullPath);
//Session["DeleteSuccess"] = "Yes";
}
return RedirectToAction("Index");
}
and this is the view:
<div class="form-group">
#Html.LabelFor(model => model.CV, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#{
if (File.Exists(Server.MapPath("~/Content/CVs/"
+ Html.DisplayFor(model => model.cvName))))
{
#Html.DisplayFor(model => model.cvName) #Html.HiddenFor(model => model.cvName)
<a href="#Url.Action("DeleteCV", new { #Model.cvName })">
<img src="#Url.Content("~/Content/Images/Delete.jpg")" width="20" height="20" class="img-rounded" />
</a>
}
else
{
<input type="file" name="file" accept="pdf" />
}
}
</div>
</div>
I can't delete the file, each time this message appears
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /DeleteCV

You're sending a GET to a POST
Change the [HttpPost] to [HttpGet]
Or use JQuery and send a DELETE verb like I mentioned in the comments

You're using an , so your link will become /Controller/DeleteCV?cvName=SomeName, which will be executed as GET. You don't want that for many reasons, and frankly, the rest of the code is a mess too. Don't do business logic (like checking for a file) in your view, and you might want to add a few checks around that File.Delete().
Do the file check in your controller, saving the result in a model variable, and create a separate form to POST to your Delete method:
if (#Model.FileExists)
{
#using(Html.BeginForm("Cv", "DeleteCV", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.cvName)
<input type="submit" value="Delete" />
}
}
else
{
#using(Html.BeginForm("Cv", "UploadCV", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input type="file" name="file" accept="pdf" />
<input type="submit" value="Upload" />
}
}

Related

ASP.NET Editable Grid: Update register

I've been struggling with this for a while now. I'm constructing this view:
But when I hit the 'Update' button after do some changes the web refreshes to show the original values.
About the view: I get this view using an IEnumerable and looping thru each item in the model inside a form. Then, inside the form, there is a table that contains only 1 row. I do this in order to wrap all the items of the record in one form. This is part of code:
#foreach (var item in Model)
{
<form asp-action="Test" asp-route-id="#item.Id">
<table class="table">
<tbody>
<tr>
<td>
<input type="hidden" asp-for="#item.Id" />
<div class="form-group">
<div class="col-md-10">
<input asp-for="#item.MchName" readonly class="form-control" />
<span asp-validation-for="#item.MchName" class="text-danger"></span>
</div>
</div>
</td>
//more fields
<td>
<input type="submit" value="Update" class="btn btn-default" />
</td>
</tr>
</tbody>
</table>
</form>}
I declare an asp-action and a asp-route-id:
<form asp-action="Test" asp-route-id="#item.Id">
Question: Is this good enough? Is there something missing?
This is the Get Method:
public async Task<IActionResult> Test()
{
PopulateMachineTypeDropDownListStore();
return View(await _context.Machines.AsNoTracking().ToListAsync());
}
Question: I'm not passing any argument to the controller, yet the view will list the items following the given structure using an IEnumerable. Should I pass anything to the Get Method or is it fine as it is?
This is the Post Method:
#model IEnumerable<Application.Models.Machine>
[HttpPost, ActionName("Test")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> TestPost(int? id)
{
if (id == null)
{
return NotFound();
}
var machinetoUpdate = await _context.Machines
.SingleOrDefaultAsync(s => s.Id == id);
if (await TryUpdateModelAsync(
machinetoUpdate,
"",
s => s.MchName, s => s.StoreID, s => s.PUnit, s => s.Status))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction("Test");
}
PopulateMachineTypeDropDownListStore();
return View(await _context.Machines.AsNoTracking().ToListAsync());
}
Question: I don't know if because the entity I retrieve the id from (and that I use to update the model thru TryUpdateModelAsync()) is also being used to compare to the model that thru the view this might not been working properly.
Thanks in advance for any help.

ASP.NET MVC 5 Form Validation and Error Handling

Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)

Can't send parameters through POST on asp.net vNext app. (beta 3) (MV6, mono, EF7, OSX)

I hope someone can help me with this...
I'm working on osx on a new small project and "getting to know" the new vNext with MV6 and Entity Framework 7, with mono, kestrel, and using sublime text for the coding...
I have a very simple form:
<div class="login-form-outer">
#using (Html.BeginForm("Login2", "Account", null, FormMethod.Post, new { #class = "form-horizontal", role = "form", #autocomplete="off" }))
{
#Html.AntiForgeryToken()
<h4>Use a Local Account to log in.</h4>
<hr>
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
} </div>
</div>
The form submits, the action "Login2" gets called, but the parameter never reaches the action...
This is the action BTW.
[HttpPost]
[AllowAnonymous]
public IActionResult Login2([FromBody] string Name)
{
Console.WriteLine(Name);
Console.WriteLine("Login2");
return RedirectToAction("Index", "Home");
}
The only thing the console shows is "Login2", and the redirection also works.
The thing is...
I have a virtual machine running a SQL Server database... I installed visual studio 2015 there, and executed the exact same project (shared folder) using WEB, KESTREL, and IIS, and they all worked! The parameter gets printed in the console...
So I don't really know where the problem is... is it mono? is it kestrel on OSX?... please help!
I usually work with Rails and Laravel, I'm a noob here...
Thanks in advance.
I don't really know with mvc6 but with mvc5 a simple string parameter was sent in the query string.
Did you tried to remove the
[FromBody]
EDIT:
In MVC5 a simple string parameter can be sent through a form post, a query string or a route value.

Ajax Request that Post back a new page instead of loading partial

I have this HTML code in my view
#using (Ajax.BeginForm("AddJoke", "Home", new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "MyfriendsJokes" , InsertionMode= InsertionMode.InsertAfter}))
{
<div style="display:block">
<textarea placeholder="Post New Joke" id="newJoke" name="joke" rows="3" cols="50" style="float:left;position"></textarea>
<button type="submit" id="postnewjoke" style="float:left"> Post </button>
#Html.TextBoxFor(model => model.fileContent, new { type = "file", id = "fileuploaded", name = "fileuploaded" })
<div style="display:inline-block">
<input type="checkbox" name="geo" id="geo" style="width: 100%; float: left; display: block">
<input name="longitude" style="display:none"/>
<input name="latitude" style="display:none" />
<input name="user" style="display:none" value="#Model.user.Id"/>
<span>Include Location</span>
</div>
<span id="jokeError" style="color:red;font-size:14px;"></span>
</div>
}
<article id="MyfriendsJokes">
#Html.Partial("_NewJoke")
</article>
and this code in my controller
[HttpPost]
public PartialViewResult AddJoke(string joke, string user, HomePage page,HttpPostedFileBase fileuploaded, string longitude, string latitude)
{
Joke newJ = new Joke();
newJ.Key = Guid.NewGuid();
newJ.body = joke;
newJ.facebookID = user;
newJ.rank = 0;
newJ.time = DateTime.Now;
newJ.longitude = longitude;
newJ.latitude = latitude;
db.Jokes.Add(newJ);
HomePage page1 = new HomePage();
page1.user = Session["user"] as MyAppUser;
//db.SaveChanges();
return PartialView("_NewJoke", page1);
}
but instead of adding elements to the targeted div, it reload the page with a new whole page with just the elements of the partial view which is this
#using Jokes.Models
#using Microsoft.AspNet.Mvc.Facebook.Models
#model HomePage
<div style="display:block">
#Model.user.Name
</div>
can someone help and say what's wrong here to append elements to div instead of loading a new whole page?
Make sure that the jquery.unobtrusive-ajax.js script is referenced in your page. This is what AJAXifies all the output generated by the Ajax.* helpers. Without this script you only get a standard <form> element generated by the Ajax.BeginForm with a bunch of data-* attributes. The jquery.unobtrusive-ajax.js script analyzes those data-* attributes and subscribes to the submit event of the form, canceling the default action of making a full postback and sending an AJAX request to the server based on the data-* attributes.
It's important to mention that this script must be included AFTER jquery.js because it depends on it.
Also you seem to have some file input in your form and your controller action is taking an HttpPostedFileBase parameter. You should realize that you cannot upload files using an AJAX request and once you include this script your file uploads will simply stop working. In order to be able to upload files using AJAX you could either use some plugin such as jquery.form and Blueimp file upload or you could directly use the new XMLHttpRequest object that's built into modern browsers. The advantage of the plugins is that they do feature detection and will fallback to other techniques depending on the capabilities of the client browsers.

Return Different Views From MVC Controller

I've a MVC application, whose SharedLayout view(Master Page) gives user capability to search. They could search their order by Order No or By Bill no. So there are two option buttons the Shared View along with the textbox. Code is somewhat like this
#using (Html.BeginForm("Track", "Tracking", FormMethod.Post))
{
<div style="text-align: center">
<textarea cols="20" id="txtNo" name="txtOrderNo" rows="2" ></textarea>
</div>
<div style="text-align: center">
<input type="radio" name="optOrderNo" checked="checked" value="tracking" />Order No <input type="radio" name="optRefNo" value="tracking" />Ref No
</div>
<div style="text-align: center">
<input type="submit" value="Track" />
</div>
}
So it'll go to TrackingController and Track Method in it and return the view. It works fine for a single search as a View is associated with a controller's methods. It works fine but how could i conditionally return the other view based on the radio button selection.
What i come up with is this
[HttpPost]
public ActionResult Track(FormCollection form)
{
string refNo = null;
if (form["optRefNo"] == null)
{
string OrderNo = form["txtOrderNo"];
var manager = new TrackingManager();
var a = manager.ConsignmentTracking(OrderNo);
var model = new TrackingModel();
if (OrderNo != null)
model.SetModelForConsNo(a, consNo);
return View(model);
}
refNo = form["txtConsNo"];
return TrackByRef(refNo);
}
public ActionResult TrackByRef(string refNo)
{
//what ever i want to do with reference no
return View();
}
Kindly guide.
Thanks
View has an overload where the first parameter is a string. This is the name (or path) to the view you want to use, rather than the default (which is a view that matches the action's name).
public ActionResult TrackByRef(string refNo)
{
//what ever i want to do with reference no
return View("Track");
// or, if you want to supply a model to Track:
// return View("Track", myModel);
}

Resources