BlazorGrpcWebCodeFirst Save

Sample implementation of gRPC-Web code-first approach in Blazor WebAssembly ASP.NET Core hosted project.

Project README

Blazor WebAssembly with gRPC-Web code-first approach

Do you like WCF-like approach and need to cover communication in between ASP.NET Core service and Blazor WebAssembly client? Use code-first with gRPC-Web! You can try the it right now by following a few simple steps (commit):

1. Blazor.Server - Prepare the ASP.NET Core host

Add NuGet packages:

Register CodeFirstGrpc() and GrpcWeb() services in Startup.cs ConfigureServices() method:

services.AddCodeFirstGrpc(config => { config.ResponseCompressionLevel = System.IO.Compression.CompressionLevel.Optimal; });

Add GrpcWeb middleware in between UseRouting() and UseEndpoints():

app.UseGrpcWeb(new GrpcWebOptions() { DefaultEnabled = true });

2. Blazor.Shared - Define the service contract (code-first)

Add System.ServiceModel.Primitives NuGet package.

Define the interface of your service:

[ServiceContract]
public interface IMyService
{
	Task DoSomething(MyServiceRequest request);

}

[DataContract]
public class MyServiceResult
{
	[DataMember(Order = 1)]
	public string NewText { get; set; }

	[DataMember(Order = 2)]
	public int NewValue { get; set; }
}

[DataContract]
public class MyServiceRequest
{
	[DataMember(Order = 1)]
	public string Text { get; set; }

	[DataMember(Order = 2)]
	public int Value { get; set; }
}

3. Blazor.Server - Implement and publish the service

Implement your service:

public class MyService : IMyService
{
	public Task DoSomething(MyServiceRequest request)
	{
		return Task.FromResult(new MyServiceResult()
		{
			NewText = request.Text + " from server",
			NewValue = request.Value + 1
		});
	}
}

Publish the service in Startup.cs:

app.UseEndpoints(endpoints =>
{
	endpoints.MapGrpcService<MyService>();
	// ...
}

4. Blazor.Client (Blazor Web Assembly) - consume the service

Add NuGet packages:

4A. Direct consumption of the service

Consume the service in your razor file:

var handler = new Grpc.Net.Client.Web.GrpcWebHandler(Grpc.Net.Client.Web.GrpcWebMode.GrpcWeb, new HttpClientHandler());
using (var channel = Grpc.Net.Client.GrpcChannel.ForAddress("https://localhost:44383/", new Grpc.Net.Client.GrpcChannelOptions() { HttpClient = new HttpClient(handler) }))
{
	var testFacade = channel.CreateGrpcService<IMyService>();
	this.result = await testFacade.DoSomething(request);
}

4B. Consumption via dependency injection

Register a GrpcChannel in your Program.cs (or Startup.cs:ConfigureServices())

builder.Services.AddSingleton(services =>
{
	// Get the service address from appsettings.json
	var config = services.GetRequiredService<IConfiguration>();
	var backendUrl = config["BackendUrl"];

	// If no address is set then fallback to the current webpage URL
	if (string.IsNullOrEmpty(backendUrl))
	{
		var navigationManager = services.GetRequiredService<NavigationManager>();
		backendUrl = navigationManager.BaseUri;
	}

	// Create a channel with a GrpcWebHandler that is addressed to the backend server.
	//
	// GrpcWebText is used because server streaming requires it. If server streaming is not used in your app
	// then GrpcWeb is recommended because it produces smaller messages.
	var httpHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler());

	return GrpcChannel.ForAddress(
		backendUrl,
		new GrpcChannelOptions
		{
			HttpHandler = httpHandler,
			//CompressionProviders = ...,
			//Credentials = ...,
			//DisposeHttpClient = ...,
			//HttpClient = ...,
			//LoggerFactory = ...,
			//MaxReceiveMessageSize = ...,
			//MaxSendMessageSize = ...,
			//ThrowOperationCanceledOnCancellation = ...,
		});
});

Register the individual services (you might want to extract the "logic" to an extension method for better readability).

builder.Services.AddTransient<IMyService>(services =>
{
	var grpcChannel = services.GetRequiredService<GrpcChannel>();
	return grpcChannel.CreateGrpcService<IMyService>();
});

And now you can consume the services whereever needed (e.g. from .razor file):

@inject	IMyService MyService
@code
{
	async Task Submit()
	{
		this.result = await MyService.DoSomething(request);
	}
}

Advanced scenarios

For more advanced usage with error-handling, authentication + authorization and more, see our Havit.Blazor project template:

References, Credits

Known Issues

Open Source Agenda is not affiliated with "BlazorGrpcWebCodeFirst" Project. README Source: hakenr/BlazorGrpcWebCodeFirst
Stars
40
Open Issues
0
Last Commit
9 months ago

Open Source Agenda Badge

Open Source Agenda Rating