Search code examples
c#corsblazorgrpcgrpc-web

GRPC-Web + Blazor CORS Issue


I'm trying to create a Blazor WASM application that will call a GRPC gateway using grpc-web.

The description of the Gateway Service is:

syntax = "proto3";
import "Services/AdService.proto";

package BonnieAndClydesdale.Core;

service GatewayService {
  rpc GetAds (AdRequest) returns (AdReply);
}

I've followed this guide to set-up grpc-web on server and client.

I have this in the program.cs of my server:

builder.Services.AddGrpc();
builder.Services.AddGrpcReflection();
builder.Services.AddGrpcClient<AdService.AdServiceClient>(o => o.Address = new("https://localhost:7223")); // Downstream GRPC service

WebApplication app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<GatewayService>().EnableGrpcWeb();
app.UseGrpcWeb();

And this in the program.cs of my blazor app:

builder.Services.AddSingleton(services => 
                              {
                                  HttpClient httpClient = new(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
                                  string baseUri = "https://localhost:7080"; // TODO - Add to AppSettings 
                                  GrpcChannel channel = GrpcChannel.ForAddress(baseUri, new() { HttpClient = httpClient });
                                  return new GatewayService.GatewayServiceClient(channel); 
                              });

However, when I load this page:

public partial class ForSale: ComponentBase
{
    [Inject]
    private GatewayService.GatewayServiceClient Client { get; init; } = null!;//TODO replace with service
    private readonly List<AdDetails> _ads;

    public ForSale()
    {
        _ads = new();
    }

    protected override async Task OnInitializedAsync()
    {
        AdReply? rep = await Client.GetAdsAsync(new());
        if (rep.AdDetails is not null)
        {
            _ads.AddRange(rep.AdDetails);
        }
    }
    
}

I'm getting a CORS error:

Access to fetch at 'https://localhost:7080/BonnieAndClydesdale.Core.GatewayService/GetAds' from origin 'https://localhost:5001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.

Any ideas on how to approach fixing this?


EDIT

I've seen a lot of answers to similar questions that suggest using services.AddCors(...) and app.UseCors() but these methods don't seem to exist on Blazor WASM apps.


Solution

  • The issue arose from a misunderstanding as to where the CORS policy needed to be set. It needed to be set in the gateway server rather than on the Blazor WASM web-app. This makes sense since CORS is implemented on the server but the confusion arose because most tutorials seem to assume that we're using Blazor Server.

    For the sake of completeness, I added this to the startup.cs of my gateway (not the Blazor App).

    const string corsPolicy = "_corsPolicy";
    builder.Services.AddCors(options =>
                             {
                                 options.AddPolicy(name: corsPolicy,
                                                   policy  =>
                                                   {
                                                       policy.WithOrigins("https://localhost:5001",
                                                                          "http://localhost:5000")
                                                             .AllowAnyHeader()
                                                             .AllowAnyMethod();
                                                   });
                             });
    WebApplication app = builder.Build();
    app.UseCors(corsPolicy);