'NET5.0 Blazor WASM CORS client exception

I have a Blazor WASM app and a Web Api to get called by Blzor via HttpClient. Both programs run on the same machine (and also in production environment which should not be to exotic for a small business application!).

Calling the Web Api from the Blazor client result in a client CORS exception

Access to fetch at 'http://localhost:4040/' from origin 'https://localhost:5001' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

which is the expected behavior for this case.

In an former api project I developed in PHP, that had the same client behavior, I can bypass the CORS exception by simply setting the response header e.g.

$response = new Response;
$response->setState(false, $route->command);
...
header("Access-Control-Allow-Origin: *");
echo $response;

Now I want this in my .net 5.0 Web Api. I found different docs in the internet to cope with that like in

https://docs.microsoft.com/de-de/aspnet/core/security/cors?view=aspnetcore-5.0 https://www.c-sharpcorner.com/article/enabling-cors-in-asp-net-core-api-application/ https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.cors.infrastructure.corspolicybuilder.withorigins?view=aspnetcore-5.0

and implemented it in my api

    public class Startup {
        //---------------------------------------------------------------------

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        //---------------------------------------------------------------------

        public IConfiguration Configuration { get; }

        //---------------------------------------------------------------------

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices
                    (
                        IServiceCollection services
                    )
                    =>  services
                        .AddCors()
                        .AddSwaggerGen(c => c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1"}) )
                        .AddControllers()
                        ;
        //---------------------------------------------------------------------

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure
                    (
                        IApplicationBuilder app,
                        IWebHostEnvironment env
                    )
                    =>  app.
                        apply( _ =>
                        {
                            if (true) //(env.IsDevelopment())
                            {
                                app
                                .UseDeveloperExceptionPage()
                                .UseSwagger()
                                .UseSwaggerUI( c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1") );
                            }
                        })
                        .UseCors( cors =>
                            cors
                            .AllowAnyHeader()
                            .AllowAnyMethod()
                            .SetIsOriginAllowed( _ => true )
                            .AllowCredentials()
                        )
                        .UseHttpsRedirection()
                        .UseRouting()
                        .UseAuthorization()
                        .UseEndpoints( e => e.MapControllers() )
                        ;
        //---------------------------------------------------------------------
    }

Even tried to set the Response Header in the ApiController

    [Route("/")]
    [ApiController]
    public sealed class ServertimeController : ControllerBase
    {
        //[EnableCors("AllowOrigin")]
        [HttpGet]
        public Servertime Get() {

            Response.Headers.Add("Access-Control-Allow-Origin", "*");
            Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT");

            return servertime();
        }
    }

The Blazor WASM client looks like

    private async void onClick()
    {

        var response = await httpClient.GetFromJsonAsync<Servertime> ("http://localhost:4040");
        message = response.servertime;
        this.StateHasChanged();
    }

But it still results in the client CORS exception. To bypass this for development I use the browser extension “CORS Unblock”, but this is not an option for deployment.

What would be the correct approach to avoid Blazor client exception, what do I miss?



Solution 1:[1]

@Dmitriy Grebennikov answer is also valid but it needs a little bit improvements to make this more secure.

in ConfigureServices of Startup.cs add the following code before services.AddControllers();

services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder => 
                builder.WithOrigins("https://localhost:44338")
                       .AllowAnyMethod()
                       .AllowAnyHeader());
            }); 

Make sure that your url should not end with /.


You can pass many url to WithOrigins method. finally in the Configure method of Startup.cs add the following line before app.UseAuthorization(); and after app.UseRouting();

app.UseCors();

The code should be working now.

Solution 2:[2]

Just add this to ConfigureServices method:

services.AddCors(options =>
        {
            options.AddPolicy("DevCorsPolicy", builder =>
            {
                builder
                    .AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader();
            });
        });

And this to Configure method:

app.UseCors("DevCorsPolicy");

Solution 3:[3]

For a solution in Blazor WASM, NOT on Serverside, this code snippet

Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestMode(
    httpRequestMessage, 
    Microsoft.AspNetCore.Components.WebAssembly.Http.BrowserRequestMode.NoCors
);

will tell the local browser, which runs the local PWA, to not send CORS with the Request.

Code-Example (with call and used namespaces)

HttpRequestMessage httpRequestMessage = new HttpRequestMessage{
    RequestUri = new Uri("[Adress]"),
    Content = new StringContent(),
    Method = HttpMethod.Post };

WebAssemblyHttpRequestMessageExtensions.SetBrowserRequestMode(httpRequestMessage, BrowserRequestMode.NoCors);

HttpResponseMessage response = client.SendAsync(httpRequestMessage).Result;

--> the Version from "Dmitry Grebennikov" works. In my version, the Post isn`t async and the PWA throw an error.

Solution 4:[4]

var client = new HttpClient();

var request = new HttpRequestMessage
{
    Method = new HttpMethod("GET"),
    RequestUri = new Uri($"{site.BaseUrl}robots.txt")
};

request.Headers.Add("Accept", "text/plain");
request.SetBrowserRequestMode(BrowserRequestMode.NoCors);

var response = await client.SendAsync(request);

var txt = await response.Content.ReadAsStringAsync();

References

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Dmitry Grebennikov
Solution 3
Solution 4 gjmwolmarans