6
votes

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?

3
In the hosted project template both the app and service run from the same port, not just the same machine. In your case you're using two different ports which is no different than using two different machines as far as CORS is concerned. - Panagiotis Kanavos
Do you really want to host the app and API on separate ports? In that case you'll have to configure CORS on the Web API to allow cross-origin calls. This is explained in the docs here - Panagiotis Kanavos
The correct way to handle this is to add the origins, not disable ports. It's very easy to do so, use policy.WithOrigins("http://localhost:5000", "https://localhost:5001") in your CORS configuration. As for In an former api project I developed in PHP, hackers read SO too and now know about one more vulnerability they can try against a specific target - Panagiotis Kanavos

3 Answers

5
votes

@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.

3
votes

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");
0
votes
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