Click here to Skip to main content
15,921,622 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I'm build a .NET 7 blog with MVC and Razor-Pages.

My model is:
C#
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;

namespace MannsBlog.Models
{    
    public class ContactFormModel
    {        
        [Required]
        [EmailAddress]
        public string Email { get; set; } = string.Empty;
        
        [StringLength(4096, ErrorMessage = "Your message is too long. Please shorten it to max. 4096 chars.")]
        [MinLength(5)]
        [Required]
        public string Body { get; set; } = string.Empty;
        
        [Required]
        [StringLength(100, ErrorMessage = "Name is too long. Just 100 chars allowed.")]
        public string Name { get; set; } = string.Empty;
        
        [Required]
        [StringLength(150, ErrorMessage = "Subject too long. Just 150 chars allowed.")]
        public string Subject { get; set; } = string.Empty;
                
        public IFormFile? Attachment { get; set; }
    }
}


As form i'm using:
HTML
@inject IViewLocalizer _localizer
@model MannsBlog.Models.ContactFormModel

<form id="myForm" enctype="multipart/form-data" class="form-vertical" asp-controller="Root" asp-action="Contact" method="post">
    <div class="form-group">
        <input class="e-input" name="Name" type="text" asp-for="Name" placeholder="@_localizer["your-name"]" />
        <span asp-validation-for="Name"></span>
        <input class="e-input" name="Subject" type="text" asp-for="Subject" placeholder="@_localizer["subject"]" />
        <span asp-validation-for="Subject"></span>
        <input class="e-input" name="Email" type="text" asp-for="Email" placeholder="@_localizer["email"]" />
        <span asp-validation-for="Email"></span>
        <ejs-richtexteditor id="Body" showCharCount="true" maxLength="600" value="@Model.Body" locale="@culture"></ejs-richtexteditor>
        <div id="dateError" style="padding-top: 10px"></div>
        <span asp-validation-for="Body"></span>
        <input class="samplebtn e-control e-btn" type="file" asp-for="Attachment" name="@_localizer["attachment"]" multipart />
    </div>
    <div style="text-align: center">
        <button id="validateSubmit" class="samplebtn e-control e-btn" type="submit" value="Send" data-ripple="true">@_localizer["submit"]</button>
        <button id="resetbtn" class="samplebtn e-control e-btn" type="reset" data-ripple="true">Reset</button>
    </div>
</form>


There i'm using the asp-for="Attachment" to mark the reference in the model.

My Controller has the "Contact" method:

C#
[HttpGet("contact")]
public IActionResult Contact()
{
    var contactModel = new ContactFormModel();
    return View(contactModel);
}

[HttpPost("contact")]
[ValidateAntiForgeryToken]
// [ValidateReCaptcha]
public async Task<IActionResult> Contact(ContactFormModel model)
{
    try
        {
            if (ModelState.IsValid)
            {
                if (model.Attachment.Length > 0)
                {

                if (await _mailService.SendMailAsync("ContactTemplate.txt", model.Name, model.Email, model.Subject, model.Body, model.Attachment))
                {                    
                    return View("Views/Root/EmailSent");
                }
            }
            else
            {
                if (await _mailService.SendMailAsync("ContactTemplate.txt", model.Name, model.Email, model.Subject, model.Body))
                {                    
                    return View("Views/Root/EmailSent");
                }
            }
        }
    }
    catch (Exception ex)
    {
        _logger.LogError("Failed to send email from contact page", ex);
        return BadRequest(new { Reason = "Error Occurred" });
    }
    return BadRequest(new { Reason = "Failed to send email..." });
}


I set a breakpoint on the first try in the Controller. When i'm adding a Message and add a Attachment and using the Submit-Button, i going to the Breakpoint. When i'm looking in the model, i can see, that Email, Body, Name and Subject are filled. But in Attachment i'm just seeing "null".

Maybe i missed anything?

What I have tried:

I searched inside our articles. But the most, what i found, was for the legacy ASP.NET MVC. I found some documentations about forms with Blazor (.razor files instead of my used .cshtml files with Razor Syntax).
Finally i found the doc on MS Learn File Uploads
The first given example is like mine.
Posted
Updated 3-Jan-23 2:07am
v3
Comments
Graeme_Grant 2-Jan-23 10:09am    
Why tag Blazor? It's not related.

1 solution

I did a quick Google Search: c# mvc form file example - Google Search[^]

Found this accepted solution: MVC: How to post File Upload and other form fields to one action [solved] - StackOverflow[^]

UPDATE

I have put together a sample app to check my updated solution. Here is the working solution for attaching an image that is based off this: Upload files in ASP.NET Core | Microsoft Learn[^].

NOTE: I have not implemented the saving of the image/file, I have left that for you.

1. Form Data Model:
C#
public class DataModel
{
    public string Name { get; set; }
    public string Email { get; set; }
    public IFormFile? FormFile { get; set; }
}

2. Page:
HTML
@model DataModel

@using (Html.BeginForm("FileUpload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{ 
    @Html.ValidationSummary();
    
    <fieldset>
        @Html.EditorFor(model => model.Name)
    </fieldset>    
    
    <fieldset>
        @Html.EditorFor(model => model.Email)
    </fieldset>    
    
    <fieldset>
        <input type="file" asp-for="FormFile" type="file"/>
    </fieldset>    

    <input type="submit" id="btnSubmit" value="Upload" />
}

3. Controller
C#
public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
        => _logger = logger;

    public IActionResult Index()
        => View();

    [ResponseCache(Duration = 0,
        Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
        => View(new ErrorViewModel
        {
            RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
        });

    [HttpPost]
    public async Task<IActionResult> FileUpload(DataModel model)
    {
        if (ModelState.IsValid)
        {
            if (model.FormFile == null)
            {
                ModelState.AddModelError("File", "Please Upload Your file");
            }
            else if (model.FormFile.Length > 0)
            {
                int MaxContentLength = 1024 * 1024 * 3; //3 MB
                string[] AllowedFileExtensions =
                {
                    ".jpg", ".gif", ".png", ".pdf"
                };

                if (!AllowedFileExtensions.Contains(
                        model.FormFile.FileName.Substring(
                            model.FormFile.FileName.LastIndexOf('.'))))
                {
                    ModelState.AddModelError("File",
                        "Please file of type: " +
                        string.Join(", ", AllowedFileExtensions));
                }

                else if (model.FormFile.Length > MaxContentLength)
                {
                    ModelState.AddModelError("File",
                        "Your file is too large, maximum allowed size is: " +
                        MaxContentLength + " MB");
                }
                else
                {
                    using var memoryStream = new MemoryStream();
                    await model.FormFile.CopyToAsync(memoryStream);

                    if (memoryStream.Length < MaxContentLength)
                    {
                        Debugger.Break();
                        // save to file or DB.....
                    }
                    else
                    {
                        ModelState.AddModelError("File", "The file is too large.");
                    }
                }
            }

            return Ok();
        }
        return BadRequest();    
    }
}

Set a breakpoint on the first line in the FileUpload. When the breakpoint is hit, inspect the model and you will see the reference to the file data. I have set a Debugger.Break(); where you need to handle the saving of the file stream.

Enjoy!

BONUS

In building my solution, I came across this page with client-side file validation: How to upload a file in ASP.MVC[^]. Ignore the rest, just look at the javascript, this script might be of interest to you.
 
Share this answer
 
v3
Comments
Sascha Manns 3-Jan-23 8:03am    
Thank you @Graeme_Grant. Your solution points to the legacy MVC. I'm using .NET7 with their MVC. But thank you for your try. In general i have linked to a official documentation of how to use IFormFile to upload a file. As far as i understood, i have implemented it right. I downloaded the in doc mentioned solution and will try it out and show, if its working.
Graeme_Grant 3-Jan-23 10:45am    
Yeah, I did not validate my answer. So I've fired up VS and checked my answer. You now have a working solution to work with.
Sascha Manns 3-Jan-23 11:05am    
Perfect. Thanks for your work :-)
Graeme_Grant 3-Jan-23 11:18am    
I would not call it perfect... but it does answer your question. 😊

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900