Skip to content

The RequestReduce Api

mwrock edited this page Jun 6, 2012 · 12 revisions

RequestReduce exposes a small Api to allow you to setup filtering rules at application startup for blocking certain resources from the reduction processing. This API also allows you to plug in a ErrorLogger to capture any errors reported by RequestReduce and to plug in your own Minifier to be used instead of the standard Microsoft Ajax Minifier.

Logging RequestReduce Errors

Especially if you already have a error loging mechanism, it is advisable that you log any errors encountered by the RequestReduce reduction processing. This will aid in bug reporting and generalk troubleshooting. To do this, you simply need to provide an Action delegate that will take charge of logging the passed Exception. Here is an example of how to use this if you use Elmah for error logging:

RequestReduce.Api.Registry.CaptureError(x => ErrorLog.GetDefault(null).Log(new Error(x)));

Injecting your own Minifier

RequestReduce attempts to follow a decoupled architecture which allows developers to swap out certain parts with their own behavior. To override RequestReduce's use of the Micosoft Ajax minifier library, you simply create a class that derrives from IMinifier. There is not much to IMinifier:

    public interface IMinifier
    {
        string Minify<T>(string unMinifiedContent) where T : IResourceType;
    }

Here Is RequestReduce's implementation:

    public class Minifier : IMinifier
    {
        protected readonly Microsoft.Ajax.Utilities.Minifier minifier = new Microsoft.Ajax.Utilities.Minifier();
        protected readonly CodeSettings settings = new CodeSettings {EvalTreatment = EvalTreatment.MakeAllSafe};

        public virtual string Minify<T>(string unMinifiedContent) where T : IResourceType
        {
            if (typeof(T) == typeof(CssResource))
                return minifier.MinifyStyleSheet(unMinifiedContent);
            if (typeof(T) == typeof(JavaScriptResource))
                return minifier.MinifyJavaScript(unMinifiedContent, settings);

            throw new ArgumentException("Cannot Minify Resources of unknown type", "unMinifiedContent");
        }
    }

You can derrive from this minifier if you want to continue to use the Ajax Minifier and perhaps change the settings that RequestReduce uses. It's also not difficult to imagine how you would change this implementation to use something like the YUI Compressor for.Net. Lets say you had a YuiMinifier class that you want RequestReduce to use instead of its own minifier. It might look something like this:

    public class YuiMinifier : IMinifier
    {
        public string Minify<T>(string unMinifiedContent) where T : IResourceType
        {
            if (typeof(T) == typeof(CssResource))
                return CssCompressor.Compress(unMinifiedContent);
            if (typeof(T) == typeof(JavaScriptResource))
                return JavaScriptCompressor.Compress(unMinifiedContent);

            throw new ArgumentException("Cannot Minify Resources of unknown type", "unMinifiedContent");
        }
    }

You would just need to add the following code to your startup code:

RequestReduce.Api.Registry.RegisterMinifier<YuiMinifier>();

That's it. Now your minification code will be used.

##Transforming the URLs generated by RequestReduce

There are several points in the reduction pipeline where RequestReduce will generate a URL for CSS, Javascript or image resources. Typically you want RequestReduce to build these urls in a manner transparent to you. However there may be specific scenarios where you want to influence or transform the generated URL. A good example might be when you are using a CDN to cache your static content from a data center close to your user. In such cases you would use the contentHost setting to inject the CDN host name. You may also be serving SSL content which your CDN charges more for and would like to go through your non CDN'd host for SSL content.

To do this, RequestReduce exposes the following delegate that can be assigned to its API Registry's UrlTransformer property:

public delegate string ContextUrlTransformFunc(HttpContextBase context, string originalabsoluteUrl, string urlWithContentHost);

Here is an example of using this delegate to transform the absolute urls generated by RequestReduce:

RequestReduce.Api.Registry.UrlTransformer = 
    (context, x, y) => y.Replace(
        "https://cdn.mysite.com", 
        "https://mysite.com");

UPDATE: The AbsoluteUrlTransformer method has been marked obsolete and it is now suggested that you use the UrlTransformer method. The key difference between these two methods is that AbsoluteUrlTransformer is only applied to URLs inside CSS.

Filtering Resources from the Reduction Processing

If you would like to have certain CSS or javascript resources filtered from the reduction processing or if you would like to exclude certain images or even entire requests from all response transformations, then use the AddFilter method:

public static void AddFilter(IFilter filter)

RequestReduce provides four concrete types deriving from IFilter:

public class CssFilter : Filter<CssJsFilterContext>
public class JavascriptFilter : Filter<CssJsFilterContext>
public class SpriteFilter : Filter<SpriteFilterContext>
public class PageFilter : Filter<PageFilterContext>

All of these derrive from:

public class Filter<T> : IFilter where T : class, IFilterContext

The IFilter implementations really don't do anything on their own other than pass a IFilterContext which exposes various properties of the request. Each of the IFilter implementations provide a single constructor which takes a Predicate where T is an IFilterContext that RequestReduce populates and provides to the predicate delegate.

There are three different implementations of IFilterContext available:

    public class CssJsFilterContext : IFilterContext
    {
        public HttpRequestBase HttpRequest { private set; get; }
        public string FilteredUrl { private set; get; } //The Css or JS url to be processed
        public string FilteredTag { private set; get; } //The entire HTML tag being processed
    }

    public class PageFilterContext : IFilterContext
    {
        public HttpRequestBase HttpRequest { private set; get; }
    }

    public class SpriteFilterContext : IFilterContext
    {
        //BackgroundImageClass has several properties that include all the CSS background attributes
        public BackgroundImageClass BackgroundImage { private set; get; } 
    }

Depending on the type of filter (Page, css, javascript or sprite) for each item that RequestReduce processes, it will provide the appropriate IFilterContext to your predicate. If you return true, RequestReduce will eliminate that resource from being processed. Here is an example of how to have RequestReduce ignore any image over 100px X 100px.

RequestReduce.Api.Registry.AddFilter(new SpriteFilter(x => x.Width > 100 && x.Height > 100));

Make sure to add these filters inside your App start or some other pre application, one time execution block.

##Forcing the RequestReduce Response Filter to attach to the Response before PreSendRequestHeaders or ReleaseRequestState By default RequestReduce waits until the last moment possible before attaching itself to the Http Response. In certain rare circumstances, you may need it to attach itself earlier. This may be necessary if you uhave other HttpModules that alter the response in a way that is not friendly to RequestReduce. If RequestReduce shares the ASP.Net pipeline with another Response Filter that does not properly chain multiple filters or if it modifies the response in such a way thatyou want to make sure thatRequestReduce gets to the response last, then you will want to make sure that RequestReduce gets attached to the response before this filter since the filter chain follows a "first attached, last to process" pattern.

To accomplish this, RequestReduce provides the InstallResponsefilter method. When you call this method, RequestReduce will attach itself to the response as a filter unless it has already been attached or if you have filtered the current page from processing. Here is an example of this call:

RequestReduce.Api.Registry.InstallResponseFilter();

##Running RequestReduce without the RequestReduce Module If you manage a site and do not want all requests running through the request reduce module. For example, you may run a multi tenant application and you want individual tenants to "opt-in" to RequestReduce instead of going through RequestReduce by default.

To do this, you can remove the RequestReduce module references from your web.config and add the following code in your application:

var handler = RequestReduce.Api.Registry.HandlerFactory.ResolveHandler(Request.Url);
if(handler != null)
{
    HttpContext.Current.RemapHandler(handler);
    return;
}
RequestReduce.Api.Registry.InstallResponseFilter();

This code must execute before the MapRequestHandler event in the ASP.Net pipeline.

This will check to see if the incoming request is a RequestReduce endpoint containing either RequestReduce processed content or one of the RequestReduce admin requests (dashboard or flush). If it is a RequestReduce endpoint, the appropriate RequestReduce handler will take over the request processing. If not the RequestReduce Response Filter will be manually attached to the Response sent from your application. It is recommended that you only attach the filter to html or xhtml content.

Clone this wiki locally