Communication between microservices using typed clients and Dapr — Part 1
In the world of .Net APIs, communication between microservices can be achieved using multiple techniques, either by using the httpClient directly or considering an approach using GRPC. In this two-part series, we will be first looking at how this kind of communication can be achieved using typed rest clients built with swagger and NSwag, and in the second part, we will be using Dapr to show an alternative way of achieving the same. Hopefully, at the end of this excercise, we’ll be able to understand the two techniques and the problems they are attempting to solve.
The application we will be using to demonstrate these techniques is a simple gift registry where a user can register for gifts and another valid user can sign up to buy a gift. We can create two microservices, one to manage the registry and another one to manage users. The source code is available here. Switch to the branch “Step1” (git checkout Step1) which contains the base solution.
When creating these projects using the visual studio template, it is important to ensure that the option “Enable OpenAPI support” is selected, like so,
The project structure and startup projects for the Registry API in the “Step1” branch looks like this,
The Registry API has a controller that has an endpoint to get a registry and an endpoint to let a user register for a gift,
The User API has a controller that has a single endpoint to get user details given a user ID,
Since the goal of this excercise is to learn how to call across services, the project has been simplifed with no real data stores or layers. We have a list of registry objects that is just a singleton injected into the controller.
Note: The use of a singleton is purely for convenience. In a real world application, we would be using an actual data store (SQL server, MongoDB etc.,) with clearly defined layers.
Hitting play should bring up a couple of web pages that look like this,
When we enabled Open API support during the creation of the project, swagger generation was included in it. Swagger is tooling that uses the OpenAPI specification. It reads the XML and attribute annotations within the controllers and models and creates a swagger JSON file that describes the capabilities of our API. A quick look at the startup.cs file shows the swagger related code that was added,
The first box shows the extension method that registers the swagger generator with the DI container. In the Configure method, we call the extension methods to use the swagger middleware and to generate the UI that came up when we hit play using the swagger JSON created by the generator.
The User microservice is very similar in structure to the Registry microservice and will be used to manage users. Endpoints in these APIs can be invoked using the swagger UI, like so,
Similarly, we should be able to invoke the endpoints in the User microservice.
The registry contoller exposes an endpoint for a user to buy a gift from another user’s registry,
Here, say, we want to validate the buyers user ID to make sure the user is a valid user in the system. For this, we can call the get user details endpoint already available in the user microservice.
Switch to the git branch client-step to see the source code for this step. The controllers now have a few additional annotations that add more information to the swagger JSON,
The addition of the ProducesResponseType annotations to the endpoints enriches the swagger JSON with more useful information about the endpoint.
We can now add a service reference to the User API in the Registry API which will let us call the user endpoints through a strongly typed client, like so,
To summarize the steps,
- Run the User api and copy out the swagger JSON URL (localhost:5001/swagger/v1/swagger.json).
- With the User API running, right click on the Registry API project, and under the add menu, select service reference.
- Under the Service references (OpenAPI, gRPC) section, click the add button.
- On the modal, ensure that the OpenAPI option is selected. Click next.
- Select the URL option and paste the URL copied out in step 1 and click finish. This will now add additional libraries to the project and generate the client.
- Click close to close out the modal and build the solution.
Opening the Registry API project file now shows us a new item group with the Open API reference to the user client API swagger.json file which has been copied into a folder named OpenAPIs.
NSwag is the library which creates a class using the swagger json file, and to see this file, click the show all files option in the solution explorer and expand the Obj folder,
The file and the class name is set to SwaggerClient by default. It would be ideal to have a name that tells us what API this class represents, for example, UserClient. To do this, lets make some changes to the project file for the registry API,
By explictly setting the ClassName and the OutputPath properties, we can control what gets generated. Additional options can be set to generate an interface for the client which isn’t included by default. Building the project again should now generate the client.
Now that we have a client that we can call, we can add a base URL property in the appsettings.json file and register the client with the DI container so that we can use it from the registry controller.
In the startup.cs file, add the client to DI,
Now we can inject the client into our controller and we should be able to call the endpoint using the client,
We can now use the swagger UI for the Registry API to test the put endpoint with an invalid user ID, and we should see a 404,
Now passing a valid user ID should result in a successful call,
This completes Part 1 of this series of blog posts. In the next part, we will see an alternative way of invoking services using Dapr.
For part 2, click here.