Flood Gravemind
Flood Gravemind

Reputation: 3801

Validating a pure HTML form in mvc

Is there a way to validate a pure HTML form in a view with no model in mvc?

My view looks like

@using (Html.BeginForm("Upload", "Picture", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.AntiForgeryToken()
        <a class="uploadlink" style="cursor:pointer" onclick="document.getElementById('file').click();">Open</a>
        <input type="file" name="file" id="file" style="opacity:0" onchange="document.getElementById('title').value = this.value.substring(this.value.lastIndexOf('\\') +1 );"/>
        <br />
        <input type="text" name="title" id="title" />
        <br/>
        <textarea name="desc" id="desc"></textarea>
        <br/>
        <input type="submit" value="Save" />
    }

My controller looks like

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Upload(string title, string desc, HttpPostedFileBase file)
        {

            if (file == null)
            {
                return Content("<span id='result'>Please select a file first</span>");
            }
            if (string.IsNullOrEmpty(title))
            {
                return Content("<span id='result'>Please enter a name</span>");
            }
            if (file.ContentLength > 0)
            {
                var fileName = System.IO.Path.GetFileName(file.FileName);
                string c = file.FileName.Substring(file.FileName.LastIndexOf("."));
                title = title.Replace(c, "");
                byte[] uploadedFile = new byte[file.InputStream.Length];
                file.InputStream.Read(uploadedFile, 0, uploadedFile.Length);
                try
                {
                    using (MemoryStream ms = new MemoryStream(uploadedFile))
                    Image.FromStream(ms);
                }
                catch (ArgumentException)
                {
                    return Content("<span id='result'>The file you are trying to upload is not a valid image file.</span>");
                }

Instead of return Content I was wondering if there is any way to add ModelState.AddError or something similar.

Upvotes: 0

Views: 584

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039160

Yeah, you could add ModelState.AddModelError but in your view you should use some of the HTML helpers such as Html.ValidationSummary or Html.ValidationMessage if you want this message to appear.

For example in your view:

<input type="file" name="file" id="file" style="opacity:0" onchange="document.getElementById('title').value = this.value.substring(this.value.lastIndexOf('\\') +1 );"/>
@Html.ValidationMessage("file")

and in your controller action:

ModelState.AddModelError("file", "some error message");

Obviously it's much better to use view models and the strongly typed equivalents of those helpers as well as using helpers to generate your markup instead of hardcoding it as you did in your case.

You know, things like:

public class MyViewModel
{
    [Required(ErrorMessage = "Please select a file first")]
    public HttpPostedFileBase File { get; set; }

    [Required(ErrorMessage = "Please enter a name")]
    public string Title { get; set; }

    public string Description { get; set; }
}

and in your view things like:

@model MyViewModel
...

@using (Html.BeginForm("Upload", "Picture", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <a class="uploadlink" style="cursor:pointer" onclick="document.getElementById('file').click();">Open</a>

    @Html.TextBoxFor(x => x.File, new { 
        type = "file", 
        style = "opacity:0", 
        onchange="document.getElementById('title').value = this.value.substring(this.value.lastIndexOf('\\') +1 );" 
    })
    @Html.ValidationMessageFor(x => x.File)
    <br />

    @Html.EditorFor(x => x.Title)
    @Html.ValidationMessageFor(x => x.Title)
    <br/>

    @Html.TextAreaFor(x => x.Description)
    <br/>

    <input type="submit" value="Save" />
}

and in your controller action:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(MyViewModel model)
{
    if (!ModelState.IsValid)     
    {
        return View(model);
    }

    try
    {
        Image.FromStream(model.File.InputStream);
    }
    catch (ArgumentException)
    {
        ModelState.AddModelError("File", "The file you are trying to upload is not a valid image file.");
    }

    // At this stage you know that the model is valid =>
    // you could do something with those model.Title and model.Description properties

    return RedirectToAction("Success");
}

By the way you might take a look at the following answer of mine to get rid of even more plumbing/validation code from your controller action which doesn't belong there.

Upvotes: 1

Related Questions