Skip to content

Implementing a PipelineContributor

simonthorogood edited this page Dec 18, 2010 · 4 revisions

What’s a Pipeline Contributor?

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.

One important lifetime consideration

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!

Building a Pipeline Contributor

Step 1: Initialize the contributor

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.

Step 2: Write the code that will actually execute within the pipeline

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.

Step 3: Register the pipeline contributor with the ResourceSpace

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.

Implementing a friendlier 401 authorization brush-off

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.

See also

Clone this wiki locally