Creating the client
To access the API, we need to create a client. There are many ways of doing this, but we will do it in the simplest way possible by writing the code ourselves.
The client will implement the same IBlogApi
interface. This way, we have the same code regardless of which implementation we are using, and direct JSON access with BlogApiJsonDirectAccess
or BlogApiWebClient
, which we are going to create next:
- Right-click on the Dependencies node under
BlazorWebApp.Client
and select Manage NuGet Packages. - Search for
Microsoft.AspNetCore.Components.WebAssembly.Authentication
and click Install. - Also, search for
Microsoft.Extensions.Http
and click Install. - In the
BlazorWebApp.Client
project, in the root of the project, add a new class and name itBlogApiWebClient.cs
. - Open the newly created file and add the following namespaces:
using Data.Models; using Data.Models.Interfaces; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using System.Net.Http.Json; using System.Text.Json;
- Add
IBlogApi
to the class and make it public like this:namespace BlazorWebApp.Client; public class BlogApiWebClient : IBlogApi { }
- Some API calls will be public (do not require authentication), but
HttpClient
will be configured to require a token.The handling of tokens is handled automatically by Blazor, so we only need one client, and, in this case, we call it
Api
.To be able to call the API, we need to inject
HttpClient
. Add the following code to the class:private readonly IHttpClientFactory _factory; public BlogApiWebClient(IHttpClientFactory factory) { _factory = factory; }
- Now, it’s time to implement calls to the API. Let’s begin with the Get calls for blog posts. Add the following code:
public async Task<BlogPost?> GetBlogPostAsync(string id) { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<BlogPost>($"api/BlogPosts/{id}"); } public async Task<int> GetBlogPostCountAsync() { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<int>("/api/BlogPostCount"); } public async Task<List<BlogPost>?> GetBlogPostsAsync(int numberofposts, int startindex) { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<List<BlogPost>>($"/api/BlogPosts?numberofposts={numberofposts}&startindex={startindex}"); }
We use the
HttpClient
we injected and then callGetFromJsonAsync
, which will automatically download the JSON and convert it into the class we supply to the generic method.Now, it gets a little trickier: we need to handle authentication. Luckily, this is built into
HttpClient
so we only need to handleAccessTokenNotAvailableException
. If a token is missing, it will automatically try and renew it, but if there is a problem (for example, the user is not logged in), we can redirect to the login page.We will come back to tokens and how authentication works in Chapter 8, Authentication and Authorization.
- Next, we add the API calls that need authentication, such as saving or deleting a blog post.
Add the following code under the code we just added:
public async Task<BlogPost?> SaveBlogPostAsync(BlogPost item) { try { var httpclient = _factory.CreateClient("Api"); var response = await httpclient.PutAsJsonAsync<BlogPost> ("api/BlogPosts", item); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<BlogPost>(json); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } return null; } public async Task DeleteBlogPostAsync(string id) { try { var httpclient = _factory.CreateClient("Api"); await httpclient.DeleteAsync($"api/BlogPosts/{id}"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } }
If the call throws
AccessTokenNotAvailableException
, that meansHttpClient
couldn’t get or renew a token automatically, and the user needs to log in.This state should probably never happen because we will ensure that when the user navigates to that page, they will need to be logged in, but it’s better to be safe than sorry.
- Now, we need to do the same for
Categories
. Add the following code to theBlogApiWebClient
class:public async Task<List<Category>?> GetCategoriesAsync() { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<List<Category>>($"api/Categories"); } public async Task<Category?> GetCategoryAsync(string id) { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<Category>($"api/Categories/{id}"); } public async Task DeleteCategoryAsync(string id) { try { var httpclient = _factory.CreateClient("Api"); await httpclient.DeleteAsync($"api/Categories/{id}"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } } public async Task<Category?> SaveCategoryAsync(Category item) { try { var httpclient = _factory.CreateClient("Api"); var response = await httpclient.PutAsJsonAsync<Category>("api/Categories", item); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<Category>(json); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } return null; }
- Next up, we will do the same for
Tags
. Add the following code just under the code we just added:public async Task<Tag?> GetTagAsync(string id) { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<Tag>($"api/Tags/{id}"); } public async Task<List<Tag>?> GetTagsAsync() { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<List<Tag>>($"api/Tags"); } public async Task DeleteTagAsync(string id) { try { var httpclient = _factory.CreateClient("Api"); await httpclient.DeleteAsync($"api/Tags/{id}"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } } public async Task<Tag?> SaveTagAsync(Tag item) { try { var httpclient = _factory.CreateClient("Api"); var response = await httpclient.PutAsJsonAsync<Tag>("api/Tags", item); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<Tag>(json); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } return null; }
- Let’s not forget about our comments! Add the following code just under the code we just added:
public async Task<List<Comment>> GetCommentsAsync(string blogpostid) { var httpclient = _factory.CreateClient("Api"); return await httpclient.GetFromJsonAsync<List<Comment>>($"api/Comments/{blogpostid}"); } public async Task DeleteCommentAsync(string id) { try { var httpclient = _factory.CreateClient("Api"); await httpclient.DeleteAsync($"api/Comments/{id}"); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } } public async Task<Comment?> SaveCommentAsync(Comment item) { try { var httpclient = _factory.CreateClient("Api"); var response = await httpclient.PutAsJsonAsync<Comment>("api/Comments", item); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<Comment>(json); } catch (AccessTokenNotAvailableException exception) { exception.Redirect(); } return null; }
Great job! Our API client is now done!