Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Notifications You must be signed in to change notification settings

hakenr/BlazorGrpcWebCodeFirst

Repository files navigation

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

About

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

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

AltStyle によって変換されたページ (->オリジナル) /