Skip to content

API Launcher

Dennis C. Mitchell edited this page Mar 29, 2019 · 13 revisions

One way to introduce greater modularity in a software solution is by creating several REST service API applications that work together to provide overall application functionality. Microservice architectures take this kind of approach.

ASP.NET is a great platform for building microservices and API-driven architectures. That said, developers can experience some challenges with this kind of approach, especially during testing. Consider a solution in which the main application needs to communicate with five different API applications, all of which (including the main application) need to communicate with an Identity Server's APIs. Visual Studio provides a mechanism for starting multiple projects, but this mechanism doesn't work with unit and integration tests. The developer could start the integration tests in one instance of Visual Studio and all of the child APIs in a separate instance of Visual Studio; however, it would be much more convenient to use just one instance of Visual Studio for testing all of applications.

ApiLauncher

Api Launcher Graphic

ApiLauncher is a feature of the EDennis.AspNetCore.Base library. ApiLauncher allows the developer to setup automatic launching of child APIs when a parent application is launched. ApiLauncher automatically assigns random, available ports to all child APIs. ApiLauncher recognizes when more than one parent application needs the same child API (e.g., Identity Server) and only launches this shared API just once. ApiLauncher launches all of the child APIs in such as way that the developer can debug from parent applications into child APIs. When spot-testing applications (not through a test runner), a single console window controls all of the applications, including parent and child APIs (and grandchild APIs ... etc.).

Internals

Behind the scenes, ApiLauncher uses WebHostBuilder to configure and build a Kestrel server instance for each child API. This server instance is launched asynchronously on the main application's ThreadPool. Importantly, the IWebHost instance must call both WaitForShutdownAsync() and RunAsync() (in that order) to properly launch and allow the main execution thread to continue.

ApiLauncher launches child APIs that have been configured appropriately in the parent application's appsettings.Development.json. For more information on proper configuration, see the configuration section below.

Except where the port number has been assigned in the configuration file, each child API is assigned a random available port number. The coordination of port numbers is accomplished by passing a singleton containing project-to-port mappings from the parent application to the child APIs. This singleton (of type ProjectPorts), and its associated IServiceCollection extension method, AddLauncher, uses a combination of BlockingCollection, ConcurrentDictionary, and EventWaitHandle to ensure that project port assignments are handled in a thread-safe manner. Importantly, when a port has been assigned to a child API, the child API's BaseAddress is updated in configuration.

ApiLauncher is a generic class because it requires the developer to associate a given instance of ApiLauncher with a specific child API's Startup class. The Startup class is as the entry point for the WebHostBuilder.

Because ApiLauncher launches APIs asynchronously, the parent application could continue execution without waiting for the child APIs to be launched. To partially address this issue, the library includes an AwaitApis() method, which waits for the BaseAddress to be updated for all child APIs. (to be continued)

Clone this wiki locally