Hands-On Exercise: Calling a Custom gRPC Method from Blazor Server
Hands-On Exercise: Calling a Custom gRPC Method from Blazor Server
This exercise walks you through a common scenario: building a user dashboard where a Blazor Server application needs to fetch user details from a gRPC service by sending a user ID. This guide will cover creating the gRPC service, implementing its logic, consuming it from the Blazor app, and displaying the result on the UI.
Step 1: Define the gRPC Contract with .proto File
First, we'll define the service and message types in our gRPC contract.
1.1 Create `hello.proto` File
Create a file named `hello.proto` in your gRPC server project at `GrpcDemo.Server/Protos/hello.proto`.
syntax = "proto3";
option csharp_namespace = "GrpcDemo";
package user;
service UserService {
rpc GetUserDetails (UserRequest) returns (UserResponse);
}
message UserRequest {
int32 user_id = 1;
}
message UserResponse {
int32 user_id = 1;
string name = 2;
int32 age = 3;
string city = 4;
}
**Explanation:**
- `syntax = "proto3"`: Specifies the version of Protocol Buffers.
- `option csharp_namespace`: Instructs the Protobuf compiler to generate C# classes in the `GrpcDemo` namespace.
- `package user`: Organizes the generated code under a logical package, which will become a namespace prefix in C#.
- `UserService`: Defines our RPC service with a single method, `GetUserDetails`, which takes a `UserRequest` and returns a `UserResponse`.
1.2 Add `.proto` Reference to gRPC Server Project
Edit your `GrpcDemo.Server.csproj` file to tell the build process to compile the `.proto` file and generate the necessary server-side C# code.
<ItemGroup>
<Protobuf Include="Protos\hello.proto" GrpcServices="Server" />
</ItemGroup>
Step 2: Implement the gRPC Service on the Server
Now we'll write the code that will actually handle the gRPC call.
2.1 Create `UserService.cs` in `GrpcDemo.Server/Services`
This class will inherit from the generated base class and contain the logic for fetching user details.
using Grpc.Core;
using user;
public class UserService : user.UserService.UserServiceBase
{
private readonly Dictionary<int, (string Name, int Age, string City)> _users = new()
{
{ 1, ("Alice", 28, "Delhi") },
{ 2, ("Bob", 32, "Mumbai") },
{ 3, ("Charlie", 25, "Bangalore") }
};
public override Task<UserResponse> GetUserDetails(UserRequest request, ServerCallContext context)
{
if (_users.TryGetValue(request.UserId, out var data))
{
return Task.FromResult(new UserResponse
{
UserId = request.UserId,
Name = data.Name,
Age = data.Age,
City = data.City
});
}
throw new RpcException(new Status(StatusCode.NotFound, "User not found"));
}
}
2.2 Register Service in `Program.cs`
Finally, register the `UserService` so the server knows how to handle incoming gRPC requests for this service.
app.MapGrpcService<UserService>();
Make sure to place this after `app.UseRouting()` but before `app.Run()`.
Step 3: Setup the Blazor Server Project
Now we'll configure the client-side project to be able to talk to our server.
3.1 Copy `hello.proto` to Blazor Project
As we discussed in the last post, both the client and server need the same `.proto` file. Copy `hello.proto` to `GrpcDemo.Blazor/Protos/hello.proto`.
3.2 Reference the .proto in Blazor Project File
Edit your `GrpcDemo.Blazor.csproj` to generate client-side stubs.
<ItemGroup>
<Protobuf Include="Protos\hello.proto" GrpcServices="Client" />
</ItemGroup>
This time, the `GrpcServices` attribute is set to `Client`.
3.3 Add Required NuGet Packages
Ensure you have the necessary NuGet packages installed in your Blazor project.
dotnet add package Grpc.Net.Client
dotnet add package Google.Protobuf
dotnet add package Grpc.Tools
Step 4: Register gRPC Client in Blazor
In `GrpcDemo.Blazor/Program.cs`, we need to register the gRPC client with the dependency injection container.
builder.Services.AddGrpcClient<user.UserService.UserServiceClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
});
This makes the `UserServiceClient` available for injection into our Razor components.
Step 5: Create Razor Page to Call gRPC
Finally, we'll create a simple UI to interact with our gRPC service.
5.1 Add New Razor Component: `Pages/UserLookup.razor`
This page will contain a form to input a user ID and display the results.
@page "/user-lookup"
@inject user.UserService.UserServiceClient UserClient
<h3>User Lookup</h3>
<input @bind="userId" type="number" placeholder="Enter User ID" />
<button @onclick="FetchUser">Fetch User</button>
@if (error != null)
{
<p style="color:red">@error</p>
}
else if (user != null)
{
<div>
<p><b>Name:</b> @user.Name</p>
<p><b>Age:</b> @user.Age</p>
<p><b>City:</b> @user.City</p>
</div>
}
@code {
int userId;
user.UserResponse? user;
string? error;
async Task FetchUser()
{
try
{
var response = await UserClient.GetUserDetailsAsync(new user.UserRequest { UserId = userId });
user = response;
error = null;
}
catch (Grpc.Core.RpcException ex)
{
error = ex.Status.Detail;
user = null;
}
}
}
The code block shows how to inject the gRPC client, create a method to make the call, and use a `try-catch` block to handle potential errors like a user not being found.
5.2 Add Link in `NavMenu.razor`
Update your `Shared/NavMenu.razor` file to add a navigation link to our new page.
<li class="nav-item px-3">
<NavLink class="nav-link" href="user-lookup" Match="NavLinkMatch.All">
<span class="oi oi-person" aria-hidden="true"></span> User Lookup
</NavLink>
</li>
Final Project Structure Recap
For clarity, here is how the final project structure should look.
GrpcDemo/
├── GrpcDemo.Server/
│ ├── Protos/hello.proto
│ ├── Services/UserService.cs
│ └── Program.cs
├── GrpcDemo.Blazor/
│ ├── Protos/hello.proto
│ ├── Pages/UserLookup.razor
│ └── Program.cs
Test It Out
1. Run both the `GrpcDemo.Server` and `GrpcDemo.Blazor` projects. 2. Open your browser and navigate to the Blazor app. 3. Use the "User Lookup" link to go to the new page. 4. Try entering user IDs `1`, `2`, or `3` to see the user details. 5. Try entering a non-existent ID like `99` to see the "User not found" error.
This exercise demonstrates the full flow of defining, implementing, and consuming a custom gRPC method in a real-world scenario.
Comments
Post a Comment