codeflood logo

Creating and running custom pipelines in Sitecore

A lot of what happens when you request a page in Sitecore is handled by pipelines. Actually, Sitecore makes extensive use of pipelines all the way from handling page requests, to uploading files and saving items through the UI.

So what is a pipeline? A pipeline is a discrete set of steps, executed in order. It is an architectural design you can employ for certain cases in your applications. If I have a series of tasks which need to be performed to accomplish a task, then a pipeline may be the way to go. A pipeline will execute the steps in order, so I could use a pipeline for my ordered tasks as well as my unordered tasks (as the order doesn't matter we don't care that it was executed in order).

So what can we use this for in the real world? Let's take validation as an example. Let's say we have a purchase order item. Before it can move through to the "ship it!" step in the workflow the order must pass validation. That validation usually contains many discrete steps such as billable amount is greater than 0, date is in the past, GST or other taxes have been calculated properly, etc. Each of these discrete steps can be handled in isolation by a separate method or class.

Sitecore uses a pipeline to generate and populate the Sitecore.Context class. This class contains information about the current user, the current site, the current item being requested, etc. Each step in the httpRequestBegin pipeline populates just a single item in the context. The Sitecore pipelines also allow passing of an object through each step. This object can be manipulated by any of the steps and passed through to the next step until the end is reached and the object can be used by whatever executed the pipeline in the first place.

I get many benefits from using a pipeline architecture, the most relevant of which are:

  • Testability: Each step in the pipeline is usually very small and performs only a single task. This makes it much easier to test each smaller piece and pinpoint bugs rather than testing a big composite piece which might perform many steps in a single method and require more debugging to work through the bugs.
  • Recomposition: Pipelines are generally configurable. The pipeline architecture employed by Sitecore allows us to define the pipeline in web.config. The pipeline definition includes what each step is, and the order of those steps. We could also reuse those steps in another pipeline.

So let's create our own custom pipeline using the Sitecore pipeline architecture. This example pipeline is going to check that an item is valid and passes a set of validation rules implemented in code. (This is just a demo. Please refer to Alexey Rusakov's awesome validation rules blog posts for how to properly do validation in Sitecore 6). All pipelines in Sitecore require the method being invoked to accept a subclass of Sitecore.Pipelines.PipelineArgs. So we will start by defining our own subclass of the PipelineArgs class that will hold relevant information for our pipeline and allow passing of that data from step to step.

using Sitecore.Data.Items;
using Sitecore.Pipelines;

namespace CustomPipeline
{
  public class CustomPipelineArgs : PipelineArgs
  {
    private bool m_valid = false;
    private string m_message = string.Empty;
    private Item m_item = null;

    public bool Valid
    {
      get { return m_valid; }
      set { m_valid = value; }
    }

    public string Message
    {
      get { return m_message; }
      set { m_message = value; }
    }

    public Item Item
    {
      get { return m_item; }
      set { m_item = value; }
    }

    public CustomPipelineArgs(Item item)
    {
      m_item = item;
    }
  }
}

Each step in the pipeline is called a "processor". We need to make sure that each processor can accept the args we defined above. The easiest way to do this is to either use an base class or an interface. I'll go with an interface here because a base class would not be providing any functionality.

namespace CustomPipeline
{
  interface ICustomPipelineProcessor
  {
    void Process(CustomPipelineArgs args);
  }
}

This interface locks each processor into defining the "Process" method which will be called from the pipeline. Next we define each processor class and fill in the Process method to perform it's specific function. In this example I'll provide 2 processors.

namespace CustomPipeline
{
  public class DateSet : ICustomPipelineProcessor
  {
    public void Process(CustomPipelineArgs args)
    {
      if (args.Item["date"] == string.Empty)
      {
        args.Valid = false;
        args.Message = "Date has not been set";
      }
    }
  }

  public class TextSet : ICustomPipelineProcessor
  {
    public void Process(CustomPipelineArgs args)
    {
      if (args.Item["text"] == string.Empty)
      {
        args.Valid = false;
        args.Message = "Text has not been set";
      }
    }
  }
}

The DateSet class checks to make sure the date field of the item is not empty and the TextSet class checks to make sure the text field isn't empty.

Now that we have all the pieces of our pipeline defined we need to create the pipeline in web.config. Open up web.config and find the "pipelines" element. Add a new element before the closing tag containing the XML below.

<CustomPipeline>
  <processor type="CustomPipeline.DateSet, CustomPipeline" />
  <processor type="CustomPipeline.TextSet, CustomPipeline"/>
</CustomPipeline>

We don't need to define the Process method in the above processor elements as that is the name of the default method the pipeline processor will try to call. We can of course override that method by providing the method to call in the processor tag.

Now all we have to do is invoke the pipeline from somewhere in our code to run the validation over a given item. We need to create the CustomPipelineArgs object, populate it, then call into Sitecore to run our pipeline and pass in the arguments.

CustomPipelineArgs pipelineArgs = new CustomPipelineArgs(item);
CorePipeline.Run("CustomPipeline", pipelineArgs);
if (!pipelineArgs.Valid && !string.IsNullOrEmpty(pipelineArgs.Message))
{
  // Execute code here to deal with failed validation
}

So with just a few lines of code and configuration we can start to reap the benefits of a pipeline architecture for performing our applications processes, leveraging the Sitecore pipeline processor.

Comments

Much needed post. Maybe you could tackle UI (Client) pipelines next? :-)

Excellent stuff this, thanks :D
Just a thought; Aren't pipeline processors supposed to check the .Aborted flag, or is this all part of what the CoreProcessor handles?
Or maybe asked differently - if the first step in the pipeline flags abort, will the remaining steps execute?

Alistair Deneys

Thanks Alexey, I'll put the UI pipelines on the list :)

Alistair Deneys

Hi Mark, Yeah, the CorePipeline will check the aborted flag, which can be set by calling the AbortPipeline method on the PipelineArgs. If in the first step I called AbortPipeline, then the remaining steps wouldn't be run. And really, this example probably wasn't the best. I just wanted to show how easy it was to create and run custom pipelines. Thanks for the comments!

[...] more about pipelines at Coffee=>Coder=>Code or at the Getting To Know Sitecore with Adam Conn or at the John West Sitecore [...]

[...] more about pipelines at Coffee=>Coder=>Code or at the Getting To Know Sitecore with Adam Conn or at the John West Sitecore [...]

Why even bother with your interface? Sitecore should really have its own interface for which each process should implement. If every processor needs to have a Process() method then Sitecore should expose an interface for that and require developers to implement it. I don't understand why you took the step to make your own interface, it's not a contract you need to abide by from Sitecore.

Alistair Deneys

Hi Mark, It was more for me and my common dev practices, to make sure my processors all had the Process method and accepted the correct type of arguments.

Aby

Thank you so much for sharing the knowledge.

[...] to use a custom Sitecore pipeline for my SmartTreeList type. Here&#8217;s a great synopsis of how to create a custom pipeline. This allows me to apply different logic for determining the &#8220;Start Folder&#8221; depending [...]

nice article ... .thanks for sharing....

[...] Creating and Running custom pipelines (by Alistair Deneys) [...]

[&#8230;] the Sitecore Digital Marketing System. For information about creating and invoking pipelines, see Creating and running custom pipelines in Sitecore by Alistair [&#8230;]

vikas Rathore

nice article..Thanks

[&#8230;] the Sitecore Digital Marketing System. For information about creating and invoking pipelines, see Creating and running custom pipelines in Sitecore by Alistair [&#8230;]

Just the thing I was looking for... Thanks for sharing.

Anjo

Nice and informative article... I have a doubt that the pipelines are supposed to get invoked implicitly right? Why we are invoking our custom pipeline explicitly?..

Alistair Deneys

Hi Anjo, Pipelines are an architecture which Sitecore uses. Sitecore will call the pipelines which it requires and that it knows about. This article shows how one can create their own custom pipelines using the APIs and tools Sitecore has built in. Sitecore will not invoke your custom pipeline unless you call the appropriate code, hence for your custom pipelines you must call those explicitly from your own code, using the appropriate Sitecore API. Generally you should not be calling Sitecore defined pipelines explicitly yourself. Sitecore pipelines may be invoked as a result of a different API call and it's quite rare that you'd expect to see custom code calling a Sitecore defined pipeline.

Great example. I am trying to restrict the types of files uploaded using a WFFM form. I can restrict files by size and extensions, but really to do it right I need to restrict the files by content too, that is prevent someone from uploading an exe disguised as a jpg, txt, docx, etc. This particular implementation is in v6.6 (I know v8 is out and we're working on that plan).
I am using custom validations, the Mike Reynolds outlined at: http://sitecorejunkie.com/2014/04/06/restrict-certain-files-from-being-attached-to-web-forms-for-marketers-forms-in-sitecore/
Now I would like to restrict based on content. Since I'm doing this inside WFFM I'm not sure how to implement a pipeline in this scenario.
Can it be called from the custom validation in Web Forms for Marketers (system -&gt; modules -&gt; Web Forms for Marketers -&gt; Settings -&gt; Validation -&gt; Custom. ?
Thanks in advance!!! Larry

Alistair Deneys

Hi Larry, Luckily there is a pipeline which WFFM provides which you should be able to tap into to implement this logic. Have a look at the formUploadFile pipeline in the Sitecore.Forms.config file. This pipeline is used to save the uploaded file to the media library. As you have the file content in the pipeline args, it should be pretty straight forward to add a processor to the start of the pipeline to perform your checks.

Yu

Thanks for the post. I have a question. My code has default behavior built-in but allows people to override the behavior with their own pipeline. So basically I have an empty pipeline in the a config file included into web.config:
When the pipeline is empty, I would like to continue to execute my default behavior, But if user customize the config file to add their own processor, like
Then I would invoke the pipeline instead of executing the built-in code. Would this be possible? Is there an API to load the configured pipeline to find if there is any processor in it without actually executing the pipleline?
Conversely, I can have the "" commented out in the config file and ask user to uncomment it if they want customized behavior. In which case, is their an API to find out if a pipeline with certain name is configured or not, without running it?
Thanks a lot,
Yu

Alistair Deneys

Hi Yu, There's a few ways to handle this scenario. The first would be to have your custom behavior inside the pipeline. Consider your behavior the default, and developers may customise it if they wish. Your behavior could be implemented as one or several processors. The more granular the responsibilities of each processor the easier it will be for developers to customize the behavior with minimal custom code on their part.
But to answer your original question, yes, you can use Factory.GetPipeline to retrieve the pipeline and use the Processors property of the pipeline to check if there are any processors in the pipeline.

[&#8230;] I began coding, I thought about how I wanted to approach this challenge. I decided I would build a custom Sitecore pipeline to handle this code. Why? Well, quite frankly, it gives you flexibility on customization, and also [&#8230;]

[&#8230;] decided to implement a custom Sitecore pipeline to clean up the URL generated by the GetItemUrl() of the LinkProvider class, and will call this [&#8230;]

Leave a comment

All fields are required.