skip to Main Content

I’m trying to limit the size of files that can be uploaded to my page. I found I can accomplish this using RequestFormLimitsAttribute.

[RequestFormLimits(MultipartBodyLengthLimit = MaxUploadFileLength)]
public class BulkTruckUploadModel : PageModel
{
    // ...
}

This definitely prevents larger files from being uploaded. But it causes Microsoft Edge to just throw up a generic error page.

enter image description here

Is there any way to capture this error and display a more meaningful error message?

Here is my form that submits the file.

<form method="post" enctype="multipart/form-data">

    <div class="row">
        <div class="col-md-12">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-6">
            <div class="mb-4">
                <label asp-for="BulkUpload.File" class="control-label"></label>
                <input type="file" asp-for="BulkUpload.File" class="form-control" />
                <span asp-validation-for="BulkUpload.File" class="text-danger"></span>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-12">
            <div class="form-check form-switch mb-4">
                <input asp-for="BulkUpload.FirstRowHasHeaders" class="form-check-input">
                <label asp-for="BulkUpload.FirstRowHasHeaders" class="form-check-label"></label>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-12">
            <div class="mb-3">
                <input type="submit" value="Upload" class="btn btn-primary" />
                <a class="btn btn-secondary" asp-page="Trucks">Cancel</a>
            </div>
        </div>
    </div>
</form>

And here’s my handler that gets the file.

public async Task<IActionResult> OnPostAsync()
{
    if (ModelState.IsValid)
    {
        try
        {
            // Read the file from BulkUpload.File.OpenReadStream()
        }
        catch (Exception ex)
        {
            // Handle exceptions
        }
    }
    return Page();
}

Update: The goal here is to prevent denial-of-service attacks by someone trying to upload enormous files. I could also just remove the size limitation and then check the file length property from code. But it isn’t clear to me if this approach prevents very large files from impacting the server. (Does this prevent uploading big files, or just check the length after they’ve been uploaded?)

2

Answers


  1. When using RequestFormLimitsAttribute in ASP.NET Core to limit the size of file uploads, you can encounter less-than-ideal error pages if a user tries to upload a file that exceeds the specified limit. To handle this more gracefully, you can catch the specific exception that is thrown when the limit is exceeded and then provide a custom error message to the user.

    • Use exception handling middleware to catch the BadHttpRequestException which is thrown when the size limit is exceeded.
    • Provide a custom error response or redirect the user to a custom error page.

    The Configure method of your Startup.cs would be:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Use exception handling middleware to catch file size exceptions
        app.UseExceptionHandler(errorApp =>
        {
            errorApp.Run(async context =>
            {
                var exceptionHandlerPathFeature =
                    context.Features.Get<IExceptionHandlerPathFeature>();
    
                var exception = exceptionHandlerPathFeature.Error;
    
                // Check if the exception is due to a large file upload
                if (exception is BadHttpRequestException badHttpRequestException &&
                    badHttpRequestException.StatusCode == StatusCodes.Status413RequestEntityTooLarge)
                {
                    // Set the response status code and content type
                    context.Response.StatusCode = StatusCodes.Status413RequestEntityTooLarge;
                    context.Response.ContentType = "text/html";
    
                    // Provide your custom error message
                    await context.Response.WriteAsync("<html><body><h1>File too large</h1>" +
                        "<p>The file you are trying to upload exceeds the maximum allowed size.</p></body></html>");
                }
            });
        });
    
        // Other middleware registrations
    }
    

    Phil‘s comment about using an exception filter to handle InvalidDataException is a viable approach when dealing with traditional form submissions in ASP.NET Core. An exception filter can intercept specific exceptions that are thrown during the processing of a request and allow you to redirect the user to a custom error page or perform other custom error handling logic.

    public class InvalidDataExceptionFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext context)
        {
            if (context.Exception is InvalidDataException)
            {
                // Log the exception details if necessary
    
                // Redirect to a custom error page or modify the response directly
                context.Result = new RedirectToActionResult("Error", "Home", new { message = "The file you tried to upload is too large." });
                context.ExceptionHandled = true;
            }
        }
    }
    

    And then you would register this filter in your Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews(options =>
        {
            options.Filters.Add(new InvalidDataExceptionFilter());
        });
        // Other service configurations
    }
    

    That filter will catch InvalidDataException exceptions thrown by any action in your application, allowing you to handle them in a centralized way.

    The type of exception to be caught (InvalidDataException in this case) should be the one actually thrown by the framework when the file size limit is exceeded. You might need to adjust the exception type if a different one is thrown in your particular case.


    To address your update regarding the prevention of denial-of-service attacks by checking file sizes after upload: the approach of using RequestFormLimitsAttribute or similar settings in appsettings.json is designed to prevent the server from processing giant files in the first place, which is beneficial for protecting against such attacks. Checking the file length after it has been uploaded would not offer the same level of protection, as the large file would still have been transmitted to the server.


    Client-side validation provides immediate feedback to the user, which is helpful for user experience, but it should not be the only line of defense. Server-side validation is important for security.

    Client-side (Browser)   ↔   Server-side (ASP.NET Core)
       │                                 │
       │ 1. Check file size              │
       │    before upload                │
       │    (using JavaScript)           │
       │                                 │
       │ ←───────────────────────────────│
       │                                 │
       │ 2. If file size is within       │
       │    acceptable limits, upload    │
       │                                 │
       │───────────────────────────────→ │
       │                                 │
       │                                 │ 3. Check file size again (optional)
       │                                 │    and process file
       │                                 │
       │                                 │
       └───────────────────────────────→ │
                                         │ 4. If an error occurs (e.g., file too big),
                                         │    return a custom error page
    

    For server-side validation in ASP.NET Core, see above.
    For the client-side, you can implement a JavaScript check before the file is uploaded as described in the Stack Overflow comments. Here is a function you might include in your HTML page:

    function checkFileSize(maxFileSize) {
        var input = document.getElementById('fileinput');
        if (input.files && input.files[0]) {
            var file = input.files[0];
            if (file.size > maxFileSize) {
                alert("File size should not exceed " + maxFileSize + " bytes.");
                // Prevent form submission or clear the file input
                return false;
            }
        }
        return true;
    }
    

    You would then call this function on your form’s submit event or file input’s change event, passing the maximum file size you want to allow.

    Login or Signup to reply.
    1. Instead of displaying any meaning full display of error message, I would recommend using JavaScript File Uploader, in which you can identify the file size and prevent upload right there in the browser. Which I guess other answer has also recommended.
    2. For optimum performance, I would recommend maximum file upload size to 4 MB, and basically breaking file inside browser into multiple parts and upload each part as a separate request. Thus reducing the server buffer. And at the end, write a commit action to combine all files on server (Both S3 and Azure Blobs provide multipart uploads). Where in you upload file in parts and at the end run a commit API to save file.
    3. And for DDOS protection, I would recommend using library such as "RateLimiter". ASP.NET core provides new inbuilt rate limiter, there is also an open source github repo, https://github.com/stefanprodan/AspNetCoreRateLimit , which you can use it. Basically it is not very difficult to do it, however, you can easily guess how much parallel connections you want to allow.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search