Skip to content

Commit ed76442

Browse files
authored
client 101 texting (#96)
* client 101 texting * grammarlied
1 parent 3062b09 commit ed76442

File tree

2 files changed

+47
-8
lines changed

2 files changed

+47
-8
lines changed

β€Žclient/101/index.md

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Let's say we want to call an external HTTP API. Let's also say the API doesn't p
44

55
## Spacing Things Up!
66

7+
We'll use SpaceX API for our little experiment. Let's set up our project - it will have a `SpaceX.Protocol` class library, containing our strongly-typed client and `SpaceX.Tests` to test our efforts. Here are the console commands for setting up our solution:
8+
79
```sh
810
dotnet new sln --name SpaceX
911
dotnet new classlib --name SpaceX.Protocol
@@ -14,7 +16,7 @@ cd SpaceX.Tests
1416
dotnet add reference ../SpaceX.Protocol
1517
```
1618

17-
`LaunchesShould`
19+
For a start, let's just see what we can get from the API. We'll create a new HttpClient, make a GET request for the latest launches, and print the results. Let's call our test class `LaunchesShould` and add the method below:
1820

1921
```csharp
2022
[TestMethod]
@@ -29,6 +31,8 @@ public async Task ReturnLatestAsRawString()
2931
}
3032
```
3133

34+
Running the method should print a json object similar to the one below:
35+
3236
```json
3337
{
3438
"fairings": null,
@@ -102,8 +106,12 @@ public async Task ReturnLatestAsRawString()
102106
}
103107
```
104108

109+
With that in place, let's carry on to getting a real object from our request.
110+
105111
## Making It Strongly-Typed
106112

113+
In the `SpaceX.Protocol` let's define a `Launch` model. We'll minify it for having just the launch `Name` and `Success` flag. We should also make our path statically represented. Here's the code we will get:
114+
107115
```cs
108116
namespace SpaceX;
109117

@@ -117,17 +125,31 @@ public class Uris {
117125
}
118126
```
119127

120-
from the `SpaceX.Protocol` folder
128+
Now, we'll need a method converting our `HttpResponseMessage` to the model of our choice. We'll use a package called `Nist.Responses`. Let's install it in the `SpaceX.Protocol`:
121129

122130
```sh
123131
dotnet add package Nist.Responses
124132
```
125133

134+
Now, we should be able to use the `Read` extension method on our response to get the actual model:
135+
126136
```cs
127137
var launch = await response.Read<Launch>();
128138
Console.WriteLine(launch);
129139
```
130140

141+
With that in place, our test should print something like this:
142+
143+
```log
144+
Launch { Name = Crew-5, Success = True }
145+
```
146+
147+
Perhaps the most important part is done now, but we will still need to take care of a few things.
148+
149+
## Logging It Up!
150+
151+
Besides, deserializing the response body the `Read` method ensures the response was successful and logs information about the request and response. Here's an approximation of what the logic of the `Read` method looks like:
152+
131153
```cs
132154
logger ??= NullLogger.Instance;
133155
var body = await response.Content.ReadAsStringAsync();
@@ -154,16 +176,17 @@ var result = Deserialize.Json<T>(body, serializerOptions);
154176
return result;
155177
```
156178

157-
## Logging It Up!
179+
For now, we haven't ripped the benefits of the built-in logging, because we haven't passed an `ILogger` to the method, so `NullLogger.Instance` was used instead. Let's fix it! First, we'll need a package with an actual logger and a package allowing us a simple logging configuration. Here's how we can install them in our test project:
158180

159181
```sh
160182
dotnet add package Microsoft.Extensions.DependencyInjection
161183
dotnet add package Microsoft.Extensions.Logging.Console
162184
```
163185

186+
Now, let's create the logger in our test class constructor. Here's the code:
187+
164188
```cs
165189
readonly ILogger logger;
166-
readonly Client client;
167190

168191
public LaunchesShould()
169192
{
@@ -175,18 +198,26 @@ public LaunchesShould()
175198
}
176199
```
177200

201+
Finally, let's pass the logger to the `Read` method:
202+
178203
```cs
179204
var launch = await response.Read<Launch>(logger);
180205
Console.WriteLine(launch);
181206
```
182207

208+
With that, we will get information about the requests written in our console like this:
209+
183210
```log
184211
info: SpaceX.Tests.LaunchesShould[0] GET /v4/launches/latest > OK {"fairings":null,"links":{"patch":{"small":"https://images2.imgbox.com/eb/d8/D1Yywp0w_o.png","large":"https://images2.imgbox.com/33/2e/k6VE4iYl_o.png"},"reddit":{"campaign":null,"launch":"https://www.reddit.com/r/spacex/comments/xvm76j/rspacex_crew5_launchcoast_docking_discussion_and/","media":null,"recovery":null},"flickr":{"small":[],"original":[]},"presskit":null,"webcast":"https://youtu.be/5EwW8ZkArL4","youtube_id":"5EwW8ZkArL4","article":null,"wikipedia":"https://en.wikipedia.org/wiki/SpaceX_Crew-5"},"static_fire_date_utc":null,"static_fire_date_unix":null,"net":false,"window":null,"rocket":"5e9d0d95eda69973a809d1ec","success":true,"failures":[],"details":null,"crew":["62dd7196202306255024d13c","62dd71c9202306255024d13d","62dd7210202306255024d13e","62dd7253202306255024d13f"],"ships":[],"capsules":["617c05591bad2c661a6e2909"],"payloads":["62dd73ed202306255024d145"],"launchpad":"5e9e4502f509094188566f88","flight_number":187,"name":"Crew-5","date_utc":"2022-10-05T16:00:00.000Z","date_unix":1664985600,"date_local":"2022-10-05T12:00:00-04:00","date_precision":"hour","upcoming":false,"cores":[{"core":"633d9da635a71d1d9c66797b","flight":1,"gridfins":true,"legs":true,"reused":false,"landing_attempt":true,"landing_success":true,"landing_type":"ASDS","landpad":"5e9e3033383ecbb9e534e7cc"}],"auto_update":true,"tbd":false,"launch_library_id":"f33d5ece-e825-4cd8-809f-1d4c72a2e0d3","id":"62dd70d5202306255024d139"}
185212
Launch { Name = Crew-5, Success = True }
186213
```
187214

215+
Now, when we've utilized the `Read` method to its fullest let's get to the next part of our journey - creating a strongly-typed client!
216+
188217
## Create the Client
189218

219+
That shouldn't take us too long, as we just need to assemble a class having `HttpClient` and `ILogger` at its disposal and wrap endpoints in a method. Here's how it might look:
220+
190221
```cs
191222
public class Client(HttpClient http, ILogger<Client> logger) {
192223
public async Task<Launch> GetLatestLaunch() => await GetAsync<Launch>(Uris.LatestLaunch);
@@ -195,10 +226,14 @@ public class Client(HttpClient http, ILogger<Client> logger) {
195226
}
196227
```
197228

229+
There's not much left - just to use the client. Let's simplify its registration by utilizing a `Microsoft.Extensions.Http` package:
230+
198231
```sh
199232
dotnet add package Microsoft.Extensions.Http
200233
```
201234

235+
And here's how our test class can register our client:
236+
202237
```cs
203238
readonly ILogger logger;
204239
readonly Client client;
@@ -215,15 +250,17 @@ public LaunchesShould()
215250
}
216251
```
217252

253+
Now, getting the latest launch will be just a single strongly typed call to the client:
254+
218255
```cs
219256
var launch = await client.GetLatestLaunch();
220257
Console.WriteLine(launch);
221258
```
222259

223-
## Wrapping Up!
224-
225-
Having a strongly typed SpaceX API client is nice, but what's more important is that the setup can be used as a base for any other setup. Utilizing NIST packages we were able to do it in no time.
260+
There's not much to improve there, so let's wrap this up!
226261

227-
You can check out the [nist repository](https://github.com/astorDev/nist) for more http-related tooling. And you can also ... clap to this article πŸ‘‰πŸ‘ˆ
262+
## Wrapping Up!
228263

264+
Having a strongly typed SpaceX API client is nice, but what's more important is that the setup can be used as a base for more complicated scenarios. For example, the `Read` method also supports the custom `JsonSerializerSettings` and you can use `PostAsJsonAsync` from `System.Net.Http.Json` to submit information to an API, `Nist.Queries` package allows you to convert objects to query strings. All of this doesn't require you to change anything in the fundamental setup introduced in this article.
229265

266+
As you may see, nist packages help us to make a strongly typed http client in no time. You can check out the [nist repository](https://github.com/astorDev/nist), there are many more HTTP-related tools. And you can also ... clap to this article πŸ‘‰πŸ‘ˆ

β€Žclient/playground/SpaceX.Protocol/Launches.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Net.Http.Json;
12
using Microsoft.Extensions.Logging;
23
using Nist.Responses;
34

@@ -16,4 +17,5 @@ public class Client(HttpClient http, ILogger<Client> logger) {
1617
public async Task<Launch> GetLatestLaunch() => await GetAsync<Launch>(Uris.LatestLaunch);
1718

1819
public Task<T> GetAsync<T>(string uri) => http.GetAsync(uri).Read<T>(logger);
20+
public Task<T> PostAsync<T>(string uri, object body) => http.PostAsJsonAsync(uri, body).Read<T>(logger);
1921
}

0 commit comments

Comments
Β (0)