0

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.

Flood Gravemind
  • 3,773
  • 12
  • 47
  • 79

1 Answers1

1

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.

Community
  • 1
  • 1
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • but I am not using any model in my view :(. What will it reference? – Flood Gravemind Aug 28 '13 at 20:08
  • Well then dude, it's probably about time to start using one. In your case I can see 3 input fields: some file input, a text input and a textarea. That automatically means a view model with 3 properties. The first will be of type `HttpPostedFileBase`, and the 2 others of type string. Then you could even decorate the first one with data annotations such as `[Required]` to ensure that the user has actually selected a file or something. I don't know your requirements, it's hard to say but you get the point. And then in your view it's just a matter of using the strongly typed helpers. – Darin Dimitrov Aug 28 '13 at 20:08