Adding a REST API to expose data

Legacy apps often end up as stores of data that can't be accessed outside the app. The data would be valuable to other applications or to business partners if it was accessible. NerdDinner is a good example—it was designed and built before the age of Single Page Apps, where the UI is logic is separated from the business logic, which is exposed through a REST API. NerdDinner keeps its data to itself; you can't see a list of dinners unless you go through the NerdDinner UI.

It's easy to unlock legacy data with a simple REST API running in a Docker container. It doesn't need to be a complex delivery: you can start by identifying a single dataset in the legacy app, which is useful for other business units or external consumers. Then, simply extract the loading logic for that dataset into a separate feature and deploy it as a read-only API in a container. You can iteratively add more features to the API when you have the demand, you don't need to implement the whole service catalogue for your first release.

The main dataset in NerdDinner is the list of dinners, and I've built an ASP.NET Core REST API to expose all the dinners in a read-only GET request. The code is in the NerdDinner.DinnerApi project for this chapter, and it's a very simple implementation. Because I've already split the core entity definitions out from the main NerdDinner project, I can expose the existing contract from the API and use whatever data access technology I like inside the project.

I've chosen to use Dapper, which is a fast and intuitive object-relational mapper built for .NET Standard, so it works with .NET Framework and .NET Core apps. Dapper uses convention-based mapping; you provide a SQL statement and a target class type and it executes the database query and maps the results to objects. The code to load the dinner data from the existing table and map it to the shared Dinner object is quite straightforward:

protected override string GetAllSqlQuery => "SELECT *, Location.Lat as Latitude... FROM Dinners";

public override IEnumerable<Dinner> GetAll()
{
_logger.LogDebug("GetAll - executing SQL query: '{0}'", GetAllSqlQuery);
using (IDbConnection dbConnection = Connection)
{
dbConnection.Open();
return dbConnection.Query<Dinner, Coordinates, Dinner>(
GetAllSqlQuery,
(
dinner,coordinates) => {
dinner.Coordinates = coordinates;
return dinner;
},
splitOn: "LocationId");
}
}

The GetAll method is called in the API controller class, and the rest of the code is the usual ASP.NET Core setup.

Dapper is usually much easier to work with than this example, but it lets you do some manual mapping when you need to, which is what I've done here. NerdDinner uses an SQL Server location data type to store where dinners are taking place. This maps to a .NET DbGeography type, but that type doesn't exist in .NET Standard. If you look through the code in Chapter 5, you'll see a few places where I map between DbGeography and my custom Coordinates types, which is what you'll need to do if you have a similar issue.

I've changed the original NerdDinner web app to use this new API when it fetches the list of dinners in the DinnersController class. I'm using a feature flag through the configuration setting DinnerApi:Enabled so that the app can either use the API as the data source, or query from the database directly. This lets me do a staged roll-out of the feature:

if (bool.Parse(Config.Current["DinnerApi:Enabled"]))
{
var client = new RestClient(Config.Current["DinnerApi:Url"]);
var request = new RestRequest("dinners");
var response = client.Execute<List<Dinner>>(request);
var dinners = response.Data.Where(d => d.EventDate >= DateTime.Now).OrderBy(d => d.EventDate);
return View(dinners.ToPagedList(pageIndex, PageSize));
}
else
{
var dinners = db.Dinners.Where(d => d.EventDate >= DateTime.Now).OrderBy(d => d.EventDate);
return View(dinners.ToPagedList(pageIndex, PageSize));
}

The new API gets packaged into the Docker image named dockeronwindows/ch05-nerd-dinner-api. The Dockerfile for this is very simple; it just starts from the official ASP.NET Core base image called microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-1809 and copies in the compiled API code.

I could run the API in a Docker container as an internal component, used by the NerdDinner web container but not publicly accessible, or I could publish a port on the API container and make it available outside the Docker network. It would be unusual to have a custom port for a public REST API, where consumers expect to access it on port 80 for HTTP and port 443 for HTTPS. I can add one more component to my solution that lets me use the standard set of ports for all my services and route incoming requests to different containers—that is called a reverse proxy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.247.125