In this lab we're going connect everything together.
We're going to use a design pattern called Backends For Frontends, where the client calls our own server (home so to speak) and the server calls the Rest Service, using a reverse proxy.
Our client will issue http requests to our server and it will handle the results to update the model. Blazor already takes care of updating the UI.
Our Server will forward the calls to the REST service and return the results to the client. Since there is no point in reinventing the wheel, we're going to use YARP, a library that will forward the calls for us so that we don't have to write any service for it.
Let's start by our Frontend project.
We're going to replace our old Memory Repository with a new one that uses HttpClient.
- In the
Solution ExplorerunderInfrastructure\Repositoriesfolder of thePhotoSharingApplication.Frontend.Clientproject, add a newRestfolder - In the
Restfolder, add aPhotosRepositoryclass - Let the
PhotosRepositoryclass implement theIPhotosRepositoryinterface
using PhotoSharingApplication.Shared.Entities;
using PhotoSharingApplication.Shared.Interfaces;
namespace PhotoSharingApplication.Frontend.Client.Infrastructure.Repositories.Rest;
public class PhotosRepository : IPhotosRepository {
public Task<Photo?> CreateAsync(Photo photo) {
throw new NotImplementedException();
}
public Task<Photo?> FindAsync(int id) {
throw new NotImplementedException();
}
public Task<List<Photo>> GetPhotosAsync(int amount = 10) {
throw new NotImplementedException();
}
public Task<Photo?> RemoveAsync(int id) {
throw new NotImplementedException();
}
public Task<Photo?> UpdateAsync(Photo photo) {
throw new NotImplementedException();
}
}Let's require a dependency on an HttpClient object
private readonly HttpClient http;
public PhotosRepository(HttpClient http) => this.http = http;Now let's implement the different actions
- The
FindAsyncbecomes
public async Task<Photo?> FindAsync(int id) => await http.GetFromJsonAsync<Photo>($"/photos/{id}");which requires
using System.Net.Http.Json;- The GetPhotosAsync becomes
public async Task<List<Photo>> GetPhotosAsync(int amount = 10) => await http.GetFromJsonAsync<List<Photo>>($"/photos");- The
CreateAsyncbecomes
public async Task<Photo?> CreateAsync(Photo photo) {
HttpResponseMessage response = await http.PostAsJsonAsync("/photos", photo);
return await response.Content.ReadFromJsonAsync<Photo>();
}- The
UpdateAsyncbecomes
public async Task<Photo?> UpdateAsync(Photo photo) {
HttpResponseMessage response = await http.PutAsJsonAsync($"/photos/{photo.Id}", photo);
return await response.Content.ReadFromJsonAsync<Photo>();
}- The
RemoveAsyncbecomes
public async Task<Photo?> RemoveAsync(int id) {
HttpResponseMessage response = await http.DeleteAsync($"/photos/{id}");
return await response.Content.ReadFromJsonAsync<Photo>();
}Now we need to inject the Repository and configure the HttpClient.
-
Open the
Program.csfile of thePhotoSharingApplication.Frontend.Clientproject -
Replace
builder.Services.AddScoped<IPhotosRepository, PhotoSharingApplication.Frontend.Client.Infrastructure.Repositories.Memory.PhotosRepository>();with
builder.Services.AddScoped<IPhotosRepository, PhotoSharingApplication.Frontend.Client.Infrastructure.Repositories.Rest.PhotosRepository>();The Client calls the Server and the Server now needs to forward the call to the REST service. We need to:
-
Add the YARP package, as described in the Getting Started
-
Add the services and middleware to the pipeline
-
Configure YARP to forward the calls to the correct backend address
-
In the
PhotoSharingApplication.Frontend.Serverproject, add aYarp.ReverseProxynuGet package -
Add the YARP Middleware by opening
Program.csand adding the following lines:
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));Then replace
app.MapControllers();with
app.MapReverseProxy();- Add the Configuration to the
appsettings.jsonfile:
"ReverseProxy": {
"Routes": {
"photosrestroute": {
"ClusterId": "photosrestcluster",
"Match": {
"Path": "/photos/{*any}"
}
}
},
"Clusters": {
"photosrestcluster": {
"Destinations": {
"photosrestdestination": {
"Address": "https://localhost:5003/"
}
}
}
}
}In order to start both projects at the same time, we need to configure the Solution in Visual Studio
- In the
Solution Explorer, right click on the Solution, selectSet Startup Projects - Click on
Multiple Startup Projects - Set
PhotoSharingApplication.Frontend.ClientonStart - Set
PhotoSharingApplication.WebServices.Rest.PhotosonStart - Click
Ok - Start the two projects by pressing
F5
You will notice an error in the browser console:
Failed to fetch
This happens because our Rest service does not allow Cross Origin Requests (CORS). Let's proceed to modify our Rest project.
- Open
Program.csof thePhotoSharingApplication.WebServices.Rest.Photosproject and add the following code right before the building of the app
builder.Services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
//add the previous statement before this line:
var app = builder.Build();- Also in Program.cs, add the following code BEFORE app.UseAuthorization
app.UseCors("AllowAll");Save and verify that the client can send and receive data to and from the server.
The lab is complete, we successfully connected our frontend with the backend.
By the way, this could be a good moment to get rid of all the files, folders and projects that we don't need, just to clean up the whole solution.
In the next 3 labs we're going to implement the Comments functionality.
- Lab07 will take care of the FrontEnd:
The
DetailsPage will not only show the Photo details, it will also show the related comments and will allow the user to create, update and delete comments. For now we're going to use a Memory repository, with no real Backend side - Lab08 will focus on the Backend: The server will expose its functionalities through a gRpc Service
- Lab09 will connect the frontend to the backend
Go to Labs/Lab07, open the readme.md and follow the instructions to continue.