Sharing ASP.NET Core 2.0 controllers with authorization and configuration
With ASP.NET Core 2.0 it’s easy to publish APIs as libraries and include them in your other projects. This is helpful because it allows you to build end-to-end functionality (e.g. content management) that you can share among different website projects.
This post will walk through a short example of how to build an ASP.NET Core 2.0 controller library which uses ASP.NET Core 2.0 configuration and authorization features. The code for this post is available on GitHub here.
Contents
- The example solution structure
- Using controllers from a class library
- Providing services to library controllers
- Configuring options for the controller library
- Adding authorization to library controllers using policies
The example solution structure
To demonstrate how to do this we will create a solution with two projects:
- An ASP.NET Core 2.0 web project (
ControllerLibrarySample.Web
) - A .NET Standard 2.0 class library project (
ControllerLibrarySample.Library
)
For simplicity, the controller class library and web project are in the same solution. In reality, you might publish the library as a nuget package in order to share it among your projects.
Using controllers from a class library
Creating the controller
First, we need to create a controller that we want to share in the class library. I’m going to use a simple version of the ValuesController
that Visual Studio generated in the website project called OtherValuesController
:
Notice we are using MVC. For this to build, the library project will need to reference the Microsoft.AspNetCore.Mvc
package via nuget.
Using the controller
For the web project to see and use controllers which are in our class library, we only need to reference the class library project:
In previous versions of ASP.NET you had to write some plumbing code for MVC to see controllers in referenced assemblies. In ASP.NET Core 2.0, it’s automatic. For more information on how this works, you can refer to the documentation on Application Parts.
Now, if we run the Web project and visit /api/othervalues
we can see that our library controller is working and available:
Providing services to library controllers
We can use controllers from our class library in our web project, but what about services? It’s quite likely that our controllers will use constructor injection to retrieve services from the DI container. We could register the services for our library inside of Startup.cs
, but that’s not very reusable since any consumer of the library would have to know about all of the dependencies which our controllers use.
Instead, the class library should encapsulate knowledge of which services are required. To do this, we can create an extension method on IServiceCollection
in the class library:
And change our ConfigureServices
method:
Now our library controllers can request services in their constructors and MVC can provide them from its DI container:
Configuring options for the controller library
What if our controllers require some configuration options? ASP.NET Core 2.0 has a lot of nice ways to provide configuration values, but our library shouldn’t force consumers to use a particular method.
To abstract our library from the configuration method we need to build two pieces. First, our class library needs to define the options it provides and call services.Configure<>()
to register these with ASP.NET Core 2.0. Secondly, we need to provide a way for the consumer to pass configuration values to the controller library.
Creating options used by the library controllers
We can define what options our library exposes by creating a POCO in the library project. We’ll add an exciting new option, Option1
, with the default value of "default"
:
Now, we can use our options in our controller by injecting an IOptions<ExampleOptions>
in the constructor:
Passing options to the controller library
Next we need to build a way for the consumer to provide values for the configuration options of our library.
To do this, we can add a parameter to our configuration extension method which allows the consumer to provide a setup callback which is passed an instance of our options POCO. The consumer can modify this instance to configure the values of ExampleOptions
:
Now, our consumer can provide the configuration values inside of Startup.cs
:
In this example we bind the library options directly to the configuration section “SampleLibrary” inside of appsettings.json
:
Now we have a nice and flexible method of configuring our controller library which lets consumers provide option values using any configuration method they care to use.
Adding authorization to library controllers using policies
Sometimes you may need to restrict access to certain actions or controllers based on some authorization rules. With ASP.NET Core 2.0, we can use policies to abstract the required authorization level from how the authorization is actually performed.
For example, let’s add a restricted method to the OtherValuesController
:
Now when we try to run our web project, we get the following exception:
Inside our Startup.cs
we can now configure our policy based on some requirement. We could require a specific claim or role, or even some other custom business logic. This could look something like the following (incomplete example, full auth configuration is out of scope of this post):
This is beneficial to us since consumers of our library can now define how they enforce restrictions to particular API methods we have defined, without the library having to know how that particular site performs authorization. This makes the library much more reusable.
Conclusion
Hopefully this example has demonstrated how ASP.NET Core 2.0 lets us build nicely abstracted controller libraries which use the configuration and authorization features of the framework.
If you know of something I missed or want to ask questions/provide feedback, please open an issue on the repository which accompanies this example.