A
Anonieko Ramos
Answer. Use IHttpHandler.
thanks Ro ry for coming up with this code.
It processes css file to add variables. neat idea
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
namespace CssHandler {
public class CssHandler : IHttpHandler {
// We're going to use a constant for the name of our Cache object
variable.
//
// I think that using constants for this purpose is better than
using string
// literals.
//
// For example, which of the following would be easier to maintain:
//
// Cache["Some really long and error prone string"] = someValue;
// - or -
// Cache[SOME_CONSTANT] = someValue;
//
// I think I've made my point.
private const string CSS_CACHE_BODY = "Css.FileBody";
// We don't really need this constructor. It's here for decorative
purposes only -
// Kind of like the plastic grass you get with sushi.
public CssHandler() {}
// IHttpHandler method - Ignore this one. These aren't the droids
you're looking for.
public bool IsReusable { get { return false; } }
// IHttpHandler method - This is the entry point for our handler.
public void ProcessRequest( System.Web.HttpContext context ) {
try {
// Grab the filename that was requested by the browser. Since this
handler is
// for CSS documents, the filename is probably going to be
something like
// "styles.css" - It could, of course, be any other CSS document
in the system.
// "styles.css" was just an example, OK? Get over it.
string file = context.Request.PhysicalPath;
// Does the file even exist? If it doesn't, then let's just forget
about the whole
// thing. We've been had. You can add your own error processing
here, such as
// returning a 404 error to the browser. I'd do it, but I'm half
dead from sleep
// deprivation in Connecticut, and it isn't really the focus of
this sample code.
if (!File.Exists(file))
return;
// We're going to store the process CSS file in the following
variable named "body."
//
// Notice that when we try to pull the processed CSS from the
cache, we're appending
// the file name of the stylesheet we're currently processing to
the "CSS_CACHE_BODY"
// variable. This is so that our handler can deal with multiple
stylesheets. If we didn't
// do this, then each stylesheet to be processed would overwrite
the previous one in
// the cache. So many bad things would come of this that I prefer
not to think about it.
// Hence using the file name as a qualifier to create a
unique(ish) key.
string body = string.Empty;
if (context.Cache[CSS_CACHE_BODY + file] != null)
body = context.Cache[CSS_CACHE_BODY + file].ToString();
//string body = context.Cache[CSS_CACHE_BODY + file] != null ?
// context.Cache[CSS_CACHE_BODY].ToString() : string.Empty;
// If "body" is equal to string.Empty, then it means we either
haven't processed
// the stylesheet yet, or that it's been blown out of the cache.
if (body == string.Empty) {
// Read the contents of the file into a variable named "body."
StreamReader reader = new StreamReader(file);
body = reader.ReadToEnd();
reader.Close();
// Strip the comments from the CSS document. If we don't, then
they'll get in the
// way later on when we perform the actual processing.
body = StripComments(body);
// Now that we've stripped the comments, we can go ahead and
process the CSS.
body = ProcessStyleSheet(body);
// Almost time to stuff the contents of the "body" variable into
the Cache.
//
// First, we're going to create a CacheDependency object that
we'll associate
// with our Cache item (the processed CSS body). If the CSS file
is changed at
// any time, the cached object will be vaporized, and null will
be returned in
// the "if" test you saw a few lines ago, causing this code to
run again.
//
// If you'd like a detailed explanation of the CacheDependency
object, then I
// can recommend the world's greatest CacheDependency tutorial
(which I
// happened to write): http://neopoleon.com/blog/posts/1976.aspx
//
// Note that I'm just kidding about all this "world's greatest"
crap. You have
// to find ways to make yourself feel good about sitting at home
on a Friday
// night (Saturday morning now) while putting together sample
code. It's fun
// and all, but something tells me that I should be out getting
drunk and trying
// to get it on with a sofa.
CacheDependency cd = new CacheDependency(file);
// We've performed all the processing necessary, so let's store
this stylesheet
// in the cache.
//
// Note that when using a CacheDependency, you use the "Insert"
method of the
// Cache object. I understand that some of you might not like
this, but I don't
// like mushrooms or pork, and I have to co-exist with them - we
all have to
// do our part to be tolerant of the world around us.
context.Cache.Insert(CSS_CACHE_BODY + file, body, cd);
}
// Whether we've had to process a file or not, we have to dump its
contents to the
// Response stream. That's what we're doing here.
//
// This is the last step in the process. Once it's complete, our
handler's job
// is finished.
context.Response.Write(body);
} catch (Exception ex) {
context.Response.Write(ex.Message);
}
}
// This is the method that we're using (as its name implies) to
strip comments from
// the body of the CSS document.
//
// There's some regular expressions stuff in here. A discussion of
regular expressions
// is beyond the scope of this tutorial, so just pretend like you
never saw this.
private string StripComments( string body ) {
body = Regex.Replace(body, @"/\*.+?\*/", "",
RegexOptions.Singleline);
return body;
}
// This where the real work is getting done. One of our modern
"@define" stylesheets
// comes in this end, and goes out the other in a format that a
browser will understand.
private string ProcessStyleSheet( string body ) {
// I made the decision to use regular expressions to extract the
@define block
// from the rest of the CSS document.
//
// If you don't understand regular expressions, then I can
wholeheartedly
// recommend Dan Appleman's guide to .NET regular expressions. It's
short,
// available online, and to the point.
Regex regex = new Regex(@"@define\s*{(?<defines>[^}]*)}",
RegexOptions.Singleline);
Match match = regex.Match(body);
// Did our regular expression find a @define block? If not, then
let's just return
// the stylesheet unmodified. No reason to hop through the rest of
the code if
// there's nothing to do, eh?
if (!match.Success)
return body;
// The regular expression ought to have isolated the variable
name/value pairs
// in the @define block. We're going to separate these key/value
pairs out into
// a string array so that we can manipulate them individually.
string[] lines = match.Groups["defines"].Value.Split(new char[]
{';'});
// We're going to be performing some string manipulation on the CSS
document
// body. Like good little coders, we're NOT going to do this with a
string -
// Rather, we're going to do it with a StringBuilder.
StringBuilder sb = new StringBuilder(body);
// Iterate through each element of the "lines" array. Each string
in the "lines"
// array should consist of one key/value pair.
foreach (string line in lines) {
// Did we get a blank line? If so, then let's move onto the next.
if (string.Empty == line.Trim())
continue;
// The CSS assignment operator is the colon (':') - We'll split
the line on
// this character, resulting in an array with two elements. The
first
// element will be the variable name, while the second will be the
variable
// value.
string[] tokens = line.Split(new char[] {':'});
string variableName = tokens[0].Trim();
string variableValue = tokens[1].Trim();
// Now that we've extracted the variable name/value from the line,
we can
// replace all of the instances of the variable name in the rest
of the
// CSS document with the variable's value. This is where all the
"magic"
// happens.
sb.Replace("@" + variableName, variableValue);
}
// All done. Return our processed stylesheet to the caller. Not so
bad, eh?
return sb.ToString();
}
}
}
------------
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
........
<!-- Pssssst - this is the stuff we have to add to the config file
to map CSS files to
the custom handler -->
<httpHandlers>
<add verb="GET" path="*.css" type="CssHandler.CssHandler,
CssHandler" />
</httpHandlers>
</system.web>
</configuration>
thanks Ro ry for coming up with this code.
It processes css file to add variables. neat idea
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
namespace CssHandler {
public class CssHandler : IHttpHandler {
// We're going to use a constant for the name of our Cache object
variable.
//
// I think that using constants for this purpose is better than
using string
// literals.
//
// For example, which of the following would be easier to maintain:
//
// Cache["Some really long and error prone string"] = someValue;
// - or -
// Cache[SOME_CONSTANT] = someValue;
//
// I think I've made my point.
private const string CSS_CACHE_BODY = "Css.FileBody";
// We don't really need this constructor. It's here for decorative
purposes only -
// Kind of like the plastic grass you get with sushi.
public CssHandler() {}
// IHttpHandler method - Ignore this one. These aren't the droids
you're looking for.
public bool IsReusable { get { return false; } }
// IHttpHandler method - This is the entry point for our handler.
public void ProcessRequest( System.Web.HttpContext context ) {
try {
// Grab the filename that was requested by the browser. Since this
handler is
// for CSS documents, the filename is probably going to be
something like
// "styles.css" - It could, of course, be any other CSS document
in the system.
// "styles.css" was just an example, OK? Get over it.
string file = context.Request.PhysicalPath;
// Does the file even exist? If it doesn't, then let's just forget
about the whole
// thing. We've been had. You can add your own error processing
here, such as
// returning a 404 error to the browser. I'd do it, but I'm half
dead from sleep
// deprivation in Connecticut, and it isn't really the focus of
this sample code.
if (!File.Exists(file))
return;
// We're going to store the process CSS file in the following
variable named "body."
//
// Notice that when we try to pull the processed CSS from the
cache, we're appending
// the file name of the stylesheet we're currently processing to
the "CSS_CACHE_BODY"
// variable. This is so that our handler can deal with multiple
stylesheets. If we didn't
// do this, then each stylesheet to be processed would overwrite
the previous one in
// the cache. So many bad things would come of this that I prefer
not to think about it.
// Hence using the file name as a qualifier to create a
unique(ish) key.
string body = string.Empty;
if (context.Cache[CSS_CACHE_BODY + file] != null)
body = context.Cache[CSS_CACHE_BODY + file].ToString();
//string body = context.Cache[CSS_CACHE_BODY + file] != null ?
// context.Cache[CSS_CACHE_BODY].ToString() : string.Empty;
// If "body" is equal to string.Empty, then it means we either
haven't processed
// the stylesheet yet, or that it's been blown out of the cache.
if (body == string.Empty) {
// Read the contents of the file into a variable named "body."
StreamReader reader = new StreamReader(file);
body = reader.ReadToEnd();
reader.Close();
// Strip the comments from the CSS document. If we don't, then
they'll get in the
// way later on when we perform the actual processing.
body = StripComments(body);
// Now that we've stripped the comments, we can go ahead and
process the CSS.
body = ProcessStyleSheet(body);
// Almost time to stuff the contents of the "body" variable into
the Cache.
//
// First, we're going to create a CacheDependency object that
we'll associate
// with our Cache item (the processed CSS body). If the CSS file
is changed at
// any time, the cached object will be vaporized, and null will
be returned in
// the "if" test you saw a few lines ago, causing this code to
run again.
//
// If you'd like a detailed explanation of the CacheDependency
object, then I
// can recommend the world's greatest CacheDependency tutorial
(which I
// happened to write): http://neopoleon.com/blog/posts/1976.aspx
//
// Note that I'm just kidding about all this "world's greatest"
crap. You have
// to find ways to make yourself feel good about sitting at home
on a Friday
// night (Saturday morning now) while putting together sample
code. It's fun
// and all, but something tells me that I should be out getting
drunk and trying
// to get it on with a sofa.
CacheDependency cd = new CacheDependency(file);
// We've performed all the processing necessary, so let's store
this stylesheet
// in the cache.
//
// Note that when using a CacheDependency, you use the "Insert"
method of the
// Cache object. I understand that some of you might not like
this, but I don't
// like mushrooms or pork, and I have to co-exist with them - we
all have to
// do our part to be tolerant of the world around us.
context.Cache.Insert(CSS_CACHE_BODY + file, body, cd);
}
// Whether we've had to process a file or not, we have to dump its
contents to the
// Response stream. That's what we're doing here.
//
// This is the last step in the process. Once it's complete, our
handler's job
// is finished.
context.Response.Write(body);
} catch (Exception ex) {
context.Response.Write(ex.Message);
}
}
// This is the method that we're using (as its name implies) to
strip comments from
// the body of the CSS document.
//
// There's some regular expressions stuff in here. A discussion of
regular expressions
// is beyond the scope of this tutorial, so just pretend like you
never saw this.
private string StripComments( string body ) {
body = Regex.Replace(body, @"/\*.+?\*/", "",
RegexOptions.Singleline);
return body;
}
// This where the real work is getting done. One of our modern
"@define" stylesheets
// comes in this end, and goes out the other in a format that a
browser will understand.
private string ProcessStyleSheet( string body ) {
// I made the decision to use regular expressions to extract the
@define block
// from the rest of the CSS document.
//
// If you don't understand regular expressions, then I can
wholeheartedly
// recommend Dan Appleman's guide to .NET regular expressions. It's
short,
// available online, and to the point.
Regex regex = new Regex(@"@define\s*{(?<defines>[^}]*)}",
RegexOptions.Singleline);
Match match = regex.Match(body);
// Did our regular expression find a @define block? If not, then
let's just return
// the stylesheet unmodified. No reason to hop through the rest of
the code if
// there's nothing to do, eh?
if (!match.Success)
return body;
// The regular expression ought to have isolated the variable
name/value pairs
// in the @define block. We're going to separate these key/value
pairs out into
// a string array so that we can manipulate them individually.
string[] lines = match.Groups["defines"].Value.Split(new char[]
{';'});
// We're going to be performing some string manipulation on the CSS
document
// body. Like good little coders, we're NOT going to do this with a
string -
// Rather, we're going to do it with a StringBuilder.
StringBuilder sb = new StringBuilder(body);
// Iterate through each element of the "lines" array. Each string
in the "lines"
// array should consist of one key/value pair.
foreach (string line in lines) {
// Did we get a blank line? If so, then let's move onto the next.
if (string.Empty == line.Trim())
continue;
// The CSS assignment operator is the colon (':') - We'll split
the line on
// this character, resulting in an array with two elements. The
first
// element will be the variable name, while the second will be the
variable
// value.
string[] tokens = line.Split(new char[] {':'});
string variableName = tokens[0].Trim();
string variableValue = tokens[1].Trim();
// Now that we've extracted the variable name/value from the line,
we can
// replace all of the instances of the variable name in the rest
of the
// CSS document with the variable's value. This is where all the
"magic"
// happens.
sb.Replace("@" + variableName, variableValue);
}
// All done. Return our processed stylesheet to the caller. Not so
bad, eh?
return sb.ToString();
}
}
}
------------
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
........
<!-- Pssssst - this is the stuff we have to add to the config file
to map CSS files to
the custom handler -->
<httpHandlers>
<add verb="GET" path="*.css" type="CssHandler.CssHandler,
CssHandler" />
</httpHandlers>
</system.web>
</configuration>