-
Notifications
You must be signed in to change notification settings - Fork 1
Implementing a PipelineContributor
In OpenRasta, if you need some code to execute on every request – much like ASP.NET’s global.asax
or any humble HttpModule
– you need a Pipeline Contributor. Pipeline Contributors have access to the whole context of the current call, can execute before or after any of the built in (or “known”) contributors and can do things like halting execution, handling redirections, or rendering something totally different. We need to do two things to have a Pipeline Contributor take part in the process – we need first to build the contributor, and then we need to register it with OpenRasta’s ResourceSpace
.
If you’ve written IHttpModule
implementations before, this will be a breeze. Just bear in mind that an @IHttpModule@can have a lifetime of per-request or singleton through the IsReusable
property. If there were such a property in OpenRasta, pipeline contributors would have theirs hard-wired to a true
value – so every pipeline contributor is a singleton. Bear that in mind and don’t go stuffing your contributors with state!
To build a Pipeline Contributor, we simply implement a single method on IPipelineContributor
. In this example, we’ll implement a contributor that checks for the presence of an API key on the query string for the current call, and halt execution if it is not there. We’ll start with a basic implementation:
public class ApiKeyAuthorizer : IPipelineContributor
{
public void Initialize(IPipeline pipelineRunner)
{
pipelineRunner.Notify(CheckApiKey).Before<KnownStages.IOperationInvocation>();
}
}
There’s a lot to understand here. The single method we need to implement is
Initialize
, to which OpenRasta will (after we register the contributor) provide an IPipeline
instance. All we then need to do is tell the pipeLineRunner
what will execute (in this case a method we haven’t written yet called CheckApiKey
) and when to execute. In this case, we’re asking the pipeLineRunner
to execute before a well-known pipeline contributor that implements the KnownStages.IOperationInvocation
interface. See the list of well-known contributors to decide where you need to execute your own contributor.
In step 1, we referenced a function that didn’t exist then – CheckApiKey
. Let’s write it now. For this example, we’ll just reject any API key that doesn’t contain the string “123”. Intellisense tells us that when we call IPipeline.ExecuteBefore
or IPipeline.ExecuteAfter
, we need a Func<ICommunicationContext, PipelineContinuation>
. Here’s what we’ll use:
private static PipelineContinuation CheckApiKey(ICommunicationContext context)
{
if(context.Request.Uri.Query.Contains("123"))
return PipelineContinuation.Continue;
return PipelineContinuation.Abort;
}
So far so good – we’re expecting something on the request’s querystring called “
apikey
” containing the string “123”. If it’s there we return PipelineContinuation.Continue
, which lets us through. If not, we return PipelineContinuation.Abort
, which simply halts execution. Here’s a full list of PipelineContinuation members and their meanings.
In order to get this to run, we need to visit our IConfigurationSource
and register the pipeline contributor. This is an easy one-liner:
ResourceSpace.Uses.PipelineContributor<ApiKeyAuthorizer>();
h3. So what did we get?
If you diligently implemented all of this in an OpenRasta solution serving all your favourite resources, you might be a bit disappointed. Yes, it stops people accessing resources they’re not allowed to see, but it’s hardly idiomatic or friendly, is it? We get an HTTP status code of 500. Personally, I’d be expecting a 401 (Unauthorized), perhaps with a nice semi-friendly (hey, they are unauthorized, we’d better not roll out the full red carpet yet) error message. Well, PipelineContinuation.Abort
– despite appearances – is not our friend here. It’s intended for real error situations from which we can’t recover – and right now that’s not where we are.
As with everything else in OpenRasta that needs to change the HTTP return code, we’ll need to serve an OperationResult. Here’s how we change CheckApiKey
:
private static PipelineContinuation CheckApiKey(ICommunicationContext context)
{
if(context.Request.Uri.Query.Contains("123"))
return PipelineContinuation.Continue;
context.OperationResult = new OperationResult.Unauthorized();
return PipelineContinuation.RenderNow;
}
This is better; it gives us the 401 we were after. Notice how the use of
PipelineContinuation.RenderNow
instead of Abort
is more what we were after. But we can do better still – the 401 still gives us a blank page. How about we return something with a decent error message in it (something like “No valid API key supplied”?). We can do that too with an approach that works anywhere (not just in pipeline contributors). Let’s continue in ErrorsAsResources.