18

Edit: Code Repo here.

Also I'm aware that I can simply add .DisableAntiforgery(); to my API endpoint as explained here to make it work, but I'd like to do this the proper way.


I'm trying to upload a file from a Blazor component in the client side to a backend Minimal API endpoint using an Http POST call but after many failed attempts, I'm posting this question here.

I first tried this in my backend API as documented here:

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        // Do my thing
    }
});

Sent the request to the backend API using Swagger UI:

I was hit with this error: 😩

System.InvalidOperationException: Endpoint HTTP: POST /upload_many contains anti-forgery metadata, but a middleware was not found that supports anti-forgery.
Configure your application startup by adding app.UseAntiforgery() in the application startup code. If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAntiforgery() must go between them. Calls to app.UseAntiforgery() must be placed after calls to app.UseAuthentication() and app.UseAuthorization().
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.ThrowMissingAntiforgeryMiddlewareException(Endpoint endpoint)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

I then tried this.

Program.cs in my backend API:

//...
builder.Services.AddAntiforgery();
//...
var app = builder.Build();
//...
app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        // Do my thing here
    }
});
//...

I was hit with this error: 😫

Microsoft.AspNetCore.Http.BadHttpRequestException: Invalid anti-forgery token found when reading parameter "IFormFileCollection myFiles" from the request body as form.
 ---> Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery cookie ".AspNetCore.Antiforgery.JitzV0NlXkw" is not present.
   at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
   at Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryMiddleware.InvokeAwaited(HttpContext context)
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.InvalidAntiforgeryToken(HttpContext httpContext, String parameterTypeName, String parameterName, Exception exception, Boolean shouldThrow)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForForm>g__TryReadFormAsync|102_0(HttpContext httpContext, String parameterTypeName, String parameterName, Boolean throwOnBadRequest)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass102_2.<<HandleRequestBodyAndCompileRequestDelegateForForm>b__2>d.MoveNext()
--- End of stack trace from previous location ---
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryMiddleware.InvokeAwaited(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

I'm beginning to think that I'm not doing this right. And I couldn't find a working example in the documentation.

5 Answers 5

15

I had this error without blazor, just minimal api.

.DisableAntiforgery() helps with error in topic, but catched another with binding IFormFile.. Then I took file from request manually and it's worked:

app.MapPost("/upload", async (HttpRequest request) =>
    {
        if (!request.HasFormContentType || request.Form.Files.Count == 0)
            return Results.BadRequest("No file uploaded");
        var file = request.Form.Files.FirstOrDefault();
        if (file == null || file.Length == 0)
            return Results.BadRequest("File is empty");

        // Happy

        return Results.Ok("TEST");
    })
    .DisableAntiforgery();
Sign up to request clarification or add additional context in comments.

Comments

8

I figured this out after going through this.

Full source code here.

As per the docs, to be able to upload files you need an Authorization header, a client certificate, or a cookie header.

To satisfy that requirement, I decided to pass Antiforgery token with the file upload POST request.

Now my API endpoints looks like this:

// Get token endpoint
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
    var tokens = forgeryService.GetAndStoreTokens(context);
    var xsrfToken = tokens.RequestToken!;
    return TypedResults.Content(xsrfToken, "text/plain");
});
//.RequireAuthorization(); // In a real world scenario, you'll only give this token to authorized users

// Post files endpoint
app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        // ...
    }

    return TypedResults.Ok("Ayo, I got your files!");
});

So I've got 2 endpoints now:

Upload the file

Step 1: Get the token.

Step 2.1: Before making the POST call to Upload endpoint, add the XSRF token you received from Step 1.

Step 2.2: Add the file you want to upload. pickle.png in my case.

Step 2.3: Hit send. At this point, you'll be able to make a successful call.

2 Comments

Thanks, Ash for posting the answer. I checked your code on GitHub, why didn't you use the app.UseAntiforgery() middleware? Just a question
@SafyanYaqoob I did that.. was getting another error that antiforgery token is not matching
1

I recently had a similar issue, turns out I just had to remove app.UseRouting() to resolve this. I did go down the same rabbit hole of trying to configure anti-forgery, without needing or wanting it.

I have it on video, feel free to watch: DOTNET 8 Blazor Upgrade!

This was for my Blazor pre-rendering setup. I can't speak for your API requirements. But it sounds like you didn't ask for the anti-forgery, yet it bugs you.

(I replied on GitHub as well)

Comments

1

You can disabling anti-forgery check on route groups

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

var group = app.MapGroup("/").DisableAntiforgery();
group.MapPost("/hello", ([FromForm] string name) =>
{
    return TypedResults.Ok(name);
});

app.Run();

Comments

0

As per the docs you can inject an AntiforgeryStateProvider and add a RequestToken to the headers collection as a RequestVerificationToken:

private async Task OnSubmit() {
  var antiforgery = Antiforgery.GetAntiforgeryToken();
  var request = new HttpRequestMessage(HttpMethod.Post, "action");
  request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
  var response = await client.SendAsync(request);
  ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.