DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Securing Your Software Supply Chain with JFrog and Azure
Register Today

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Getting Started With the YugabyteDB Managed REST API
  • 10 Traits That Separate the Best Devs From the Crowd
  • A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Getting Started With the YugabyteDB Managed REST API
  • 10 Traits That Separate the Best Devs From the Crowd
  • A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage

Blazor and Dragons! How to Consume gRPC-web From Blazor

Today we're going to talk about how to consume a gRPC service from a Blazor client. And we're going to do it with Dungeons and Dragons!

David Guida user avatar by
David Guida
CORE ·
Jun. 26, 20 · Tutorial
Like (4)
Save
Tweet
Share
7.29K Views

Join the DZone community and get the full member experience.

Join For Free

Hi All! Today we’re going to talk about how to consume a gRPC service from a Blazor client. And we’re going to do it with Dungeons & Dragons!

People who know me, know that deep down, I’m a big nerd. I started playing D&D when I was a freshman in college and kept going for years. Eventually, I began writing my own campaigns, holding sessions as Dungeon Master, and even participating in competitions. And winning.

I play Baldur’s Gate 2 at least once a year. Each time with a different class/race. Oh boy, that game is massive!

The last game I bought on Steam? Icewind Dale. Of course, I played it in the past but never had the pleasure of “owning” it.

So what does all of this with Blazor and gRPC? Well, a few days ago I was looking for a fun way to study them both. And I thought: is there anything out in the interwebz that I can leverage? 

And the answer, of course, was yes: the D&D 5e REST API! It’s free and doesn’t require any auth so it’s a perfect way to feed some data in our app.

My goal? Well as I said, I just wanted to play with Blazor and gRPC. And have some fun in the meantime.

So I wrote a simple Blazor webassembly gRPC client to display an archive of D&D classes. You can click on a row and get redirected to a detail page. Easy peasy.

The data is coming from a separate application, exposing a gRPC service. This server is basically just a simple proxy over the D&D REST API. I deliberately decided to not add any caching or other fancy things, just to focus on the transport.

Now, due to browser limitations, we can’t really use gRPC here, but we can rely on gRPC-web instead. Let me quote James Newton-King:

It is impossible to implement the gRPC HTTP/2 spec in the browser because there is no browser API with enough fine-grained control over HTTP requests. gRPC-Web solves this problem by being compatible with HTTP/1.1 and HTTP/2.

For those interested, the full article is here.

Yes, before you say it, I’m not good at naming things.


As usual, the code is available on GitHub, help yourself.

Let’s explore the client today. The first step is to add our .proto file, defining the layout of our messages:

ProtoBuf
 




x
27


 
1
syntax = "proto3";
2
import "google/protobuf/empty.proto";
3
option csharp_namespace = "BlazorAndDragons.Client";
4
package classes;
5
service Classes {
6
    rpc GetAll (google.protobuf.Empty) returns (GetAllResponse);
7
    rpc GetDetails(GetDetailsRequest) returns (GetDetailsResponse);
8
}
9
message GetAllResponse {
10
    message ClassArchiveItem{
11
        string id = 1;
12
        string name = 2;
13
    } 
14
    repeated ClassArchiveItem data = 1;
15
}
16
message GetDetailsRequest{
17
    string id = 1;
18
}
19
message GetDetailsResponse{
20
    string id=1;
21
    string name=2;
22
    int32 hitDie=3;
23
    repeated Proficiency proficiencies=4;
24
    message Proficiency{
25
        string name=1;
26
    }
27
}



Not much to say here. We have two methods, one returning all the available classes (and taking no parameter: thank you google.protobuf.Empty ). The other one instead returns the class details given the id.

Notice how I’ve leveraged the repeated keyword to define arrays of complex objects.

Now, an extremely important step is to make sure our .proto definition is properly referenced in our .csproj file:

XML
 




xxxxxxxxxx
1


 
1
<ItemGroup>
2
    <Protobuf Include="Protos\classes.proto" GrpcServices="Client" />
3
</ItemGroup>



That GrpcServices=”Client” will basically tell Visual Studio to generate the client proxy classes for us. Quite handy indeed.

Now we have to plug the client in our DI container in our Program.cs:

C#
 




xxxxxxxxxx
1
12


 
1
var builder = WebAssemblyHostBuilder.CreateDefault(args);           
2
builder.Services.AddSingleton(sp =>
3
{
4
    var config = sp.GetRequiredService<IConfiguration>();
5
    var serverUrl = config["ServerUrl"];
6
    var channel = GrpcChannel.ForAddress(serverUrl, new GrpcChannelOptions
7
                {
8
                    HttpHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler())
9
                });
10
    var client = new Classes.ClassesClient(channel);
11
    return client;
12
 });



As you can see, I’m using GrpcWebMode.GrpcWeb here. I’m not doing any streaming, just unary calls so no need to use GrpcWebMode.GrpcWebText. This gives me the benefit of smaller messages:

GrpcWebMode.GrpcWeb


GrpcWebMode.GrpcWebText


For those interested, here’s a nice article explaining the difference between unary and streamed calls.

Now the last step: let’s call our service! That’s actually the easy part:

C#
 




xxxxxxxxxx
1
34


 
1
@page "/"
2
@using Google.Protobuf.WellKnownTypes
3
@inject Classes.ClassesClient Client
4
<h1>Classes</h1>
5
@if (_classes == null)
6
{
7
    <p><em>Loading...</em></p>
8
}
9
else
10
{
11
    <table class="table">
12
        <thead>
13
            <tr>
14
                <th>Name</th>
15
            </tr>
16
        </thead>
17
        <tbody>
18
            @foreach (var item in _classes)
19
            {
20
                <tr>
21
                    <td><a href="/class/@item.Id">@item.Name</a></td>
22
                </tr>
23
            }
24
        </tbody>
25
    </table>
26
}
27
@code {
28
    private GetAllResponse.Types.ClassArchiveItem[] _classes;
29
    protected override async Task OnInitializedAsync()
30
    {
31
        var results = await this.Client.GetAllAsync(new Empty());
32
        this._classes = results?.Data?.ToArray();
33
    }
34
}



As you can see, the Client is injected and invoked during page initialization. Stop. No strings attached. Told you t’was easy.

In another article we’ll take a look at our server instead. Thanks for reading!

Blazor

Opinions expressed by DZone contributors are their own.

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Getting Started With the YugabyteDB Managed REST API
  • 10 Traits That Separate the Best Devs From the Crowd
  • A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: