Polly and Blazor, Part 1 - Simple Wait and Retry

Full source code available here.

Want to learn more about Polly? Check out my Pluralsight course on it.

A few weeks ago I gave a talk on Polly at Dotnetconf (you can check it out here), at the end, I got many questions about using Polly with Blazor, I had never tried it, but assumed it would work.

In this blog, part 1 of a few on Polly and Blazor, I’ll show you how to get a simple retry mechanism up and working in a Blazor Server App. For now, I am not concerned with dependency injections, policy registries, or anything other fancy things in this post, that will come later.

The setup

I have a .Net 5 Blazor Server app created with

dotnet new blazorserver

I stripped out the Balzor pages I didn’t need and changed the code of the Index.razor file to make a HTTP call to a controller that generates errors 75% of the time. I removed links to the other Blazor pages too.

Around the HTTP call, I use the Polly Wait and Retry Policy to retry up three times, with a delay between each request. If you haven’t used Polly before, I have written a LOT about it, and have a Pluralsight course on it.

I made a few updates to the UI to show what the HttpClient and Polly are doing.

The Wait and Retry policy will retry after 2, 4, and 6 seconds. It prints out the to the console and updates the UI with the errors it gets from the remote system and details about the retry.

Because this is my first Blazor app, it took a little longer and may have some less than perfect approaches, but you should be able to take the retry idea and apply it to your application.

Before I show the code for retrying, let me show how I added a familiar Web API controller.

In Configure(..) method of Startup.cs I added endpoints.MapControllers(); to the below block so it looks like this -

1app.UseEndpoints(endpoints =>
2{
3    endpoints.MapControllers(); // added this
4    endpoints.MapBlazorHub();
5    endpoints.MapFallbackToPage("/_Host");
6});

Then I created a Controllers directory and added a new file FaultyController.cs.

Here is its code -

 1using Microsoft.AspNetCore.Mvc;
 2
 3namespace WebApiDataDog.Controllers
 4{
 5    [ApiController]
 6    [Route("[controller]")]
 7    public class FaultyController : ControllerBase
 8    {
 9        private static int counter = 0;
10
11        public FaultyController()
12        {
13        }
14
15        [HttpGet]
16        public ActionResult Get()
17        {
18            if (++counter % 4 == 0)
19            {
20                return Ok(counter);
21            }
22            else if(counter % 4 == 1)
23            {
24                return StatusCode(501);
25            }
26            else if(counter % 4 == 2)
27            {
28                return StatusCode(502);
29            }
30            else
31            {
32                return StatusCode(503);
33            }
34        }
35    }
36}

And here is my Razor page that uses the Polly Wait and Retry policy.

 1@page "/"
 2@using Polly
 3
 4<h1>Polly Test</h1>
 5<p>This demo uses a HttpClient to call a controller that is setup to fail 75% of the time (the first three requests will fail, fourth will succeed). The Polly Retry policy will kick in, and retry up to three times.</p>
 6
 7<p>Status: @status</p>
 8<p>Polly Summary : @pollyMessage</p>
 9
10<p>Response Code : @responseCode</p>
11<p>Response Body : @number</p>
12
13<button class="btn btn-primary" @onclick="CallRemoteServiceAsync">Call remote service</button>
14
15@code {
16   HttpClient httpClient = new HttpClient() {
17        BaseAddress =  new Uri("http://localhost:5000")
18    };
19
20    private string status;
21    private string pollyMessage;
22    private string responseCode;
23    private string number;
24
25    private async Task CallRemoteServiceAsync()
26    {
27        IAsyncPolicy<HttpResponseMessage> _httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(
28            r => !r.IsSuccessStatusCode)
29            .WaitAndRetryAsync(3,
30                retryAttempt => TimeSpan.FromSeconds(retryAttempt * 2), onRetry: (response, retryDelay, retryCount, context) => {
31                    pollyMessage = $"Recieved: {response.Result.StatusCode}, retryCount: {retryCount}, delaying: {retryDelay.Seconds} seconds\n";
32                    Console.WriteLine(pollyMessage);
33                    InvokeAsync(StateHasChanged);
34        });
35
36        status = "Calling remote service...";
37        string requestEndpoint = "faulty/";
38
39        HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));
40        
41        responseCode = httpResponse.StatusCode.ToString();
42        number = await httpResponse.Content.ReadAsStringAsync();
43        status = "Finished";
44        pollyMessage = "";
45    }
46}

As I said above, I’m going to write a few more posts on Polly and Blazor to show some other scenarios, like using the Polly Context to pass data around and dependency injection (hopefully).

Full source code available here.

comments powered by Disqus

Related