Custom From* attributes for controller action methods in ASP.NET Core
Implementing custom From* attributes for controller action methods in ASP.NET Core.
Recently I was going through some controllers and noticed a lot of code that was reading claims from ClaimsPrinciple
. If you ever worked with claims in ASP.NET Core then code like User.FindFirst(...)
might be familiar. All that the method does is that it will find the first claim that matches the type you want. You might have also noticed that, if you wanted to unit test some method in the controller that uses claims, it actually requires a quite a few extra lines of code to achieve that. You have to create ClaimsPrincipal
, ClaimsIdentity
and Claim
s and then set everything on the HttpContext
of your ControllerContext
.
I stopped for a second and said to myself: "It would be nice if I could just get the required claim as a method parameter". And then I realized that it might not be that hard to actually achieve that! Let's see what we have to do to implement a custom [FromClaim]
attribute.
Representing our source of data for binding
We will start by creating a BindingSource
for our source of data. This is needed when we want to bind our claim to the parameter of our controller action method.
We will use the BindingSource
that we have created in the following steps.
Creating the value provider
The next very important step is to create the value provider. The value provider is the class where the actual claims will be read from the ClaimsPricinpal
. We will base our new ClaimValueProvider
on the BindingSourceValueProvider
since we want to bind our claims to parameters in our controller action methods.
In our ClaimValueProvider
we will need a BindingSource
which will be the one we created for claims and a ClaimsPrincipal
from which we will read the required claim.
In the ContainsPrefix(...)
method, we will check whether the required claim is available.
In the GetValue(...)
method, we will return the first claim that matches the required claim type.
This is all we need to do in the value provider. As you can see, you can easily unit test this class, since it does only a couple of small things.
Creating the value provider factory
Now when we have implemented the ClaimValueProvider
, we will need something that will create it with correct BindingSource
and ClaimsPrincipal
. This will be done by implementing a new IValueProviderFactory
which we will call ClaimValueProviderFactory
.
We will pass our claim-specific BindingSource
and also pass the ClaimsPrincipal
from the HttpContext
that is available in the ValueProviderFactoryContext
which we get.
Once you have your provider created, you have to also add it into the available value providers in the ValueProviderFactoryContext
.
The last step is to hook up our ClaimValueProviderFactory
into available value provider factories which ASP.NET Core uses. You can do that in the AddControllers[WithViews](...)
method in ConfigureServices(...)
.
The last but not the least, our custom [FromClaim]
attribute
So far we have implemented all the logic that will extract the claim from the ClaimsPrincipal
and hooked up everything in ASP.NET Core. What is missing is our [FromClaim]
attribute itself.
Our attribute will implement two interfaces:
IBindingSourceMetadata
- this will specify which binding source will be used for bindingIModelNameProvider
- this will specify the model name, this is thekey
that we will get in the value provider as a parameter
We will also add a constructor which receives a claim type, so we have to specify the claim type when we use it in our controller action method, e.g. [FromClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name")]
.
Using our new attribute in controller action method
Now when we have everything implemented, we can easily use our new attribute in the controller action method.