您的位置:首页 > 编程语言 > ASP

Asp.net MVC Bundle 的使用与扩展

2014-07-04 16:28 417 查看
一、Asp.net 自带Bundle的使用:

1. 在Globale中注册与配置

BundleConfig.RegisterBundles(BundleTable.Bundles);
public class BundleConfig
{
// For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725 public static void RegisterBundles(BundleCollection bundles)
{
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));

#if !DEBUG
BundleTable.EnableOptimizations = true;
#endif
}
}


2. 页面上使用

@Scripts.Render("~/bundles/jquery")


 

二、扩展使用-动态Bundle

要求达到以下目标:1. 支持动态页面上的Bundle,而不必每次在Global中添加Bundle。2.支持Javascript混淆

1. 扩展方法

public static class Extension
{
public static IHtmlString Script(this HtmlHelper helper, params string[] urls)
{
var bundleDirectory = "~/bundles/" + MakeBundleName("js", urls);
var bundle = BundleTable.Bundles.GetBundleFor(bundleDirectory);
if (bundle == null)
{
var transform = new JavascriptObfuscator();
bundle = new ScriptBundle(bundleDirectory).Include(urls);
bundle.Transforms.Add(transform);
BundleTable.Bundles.Add(bundle);
}
return Scripts.Render(bundleDirectory);
}

public static IHtmlString Style(this HtmlHelper helper, params string[] urls)
{
var bundleDirectory = "~/bundles/" + MakeBundleName("css", urls);
var bundle=BundleTable.Bundles.GetBundleFor(bundleDirectory);
if (bundle == null)
{
bundle = new StyleBundle(bundleDirectory).Include(urls);
BundleTable.Bundles.Add(bundle);
}
return Styles.Render(bundleDirectory);
}

private static string MakeBundleName(string type, params string[] urls)
{
var array =
urls.SelectMany(url => url.Split('/'))
.SelectMany(url => url.Split('.'))
.Distinct()
.Except(new[] {"~", type});

return string.Join("-", array);
}
}


JavascriptObfuscator 类的实现

public class JavascriptObfuscator : IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
var p = new ECMAScriptPacker(ECMAScriptPacker.PackerEncoding.Normal, true, false);

response.Content = p.Pack(response.Content);
}
}


2. 页面上使用

@Html.Style("~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.theme.css", "~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.menu.css")
@Html.Script("~/Scripts/JQueryUI2/ui/jquery.ui.core.js", "~/Scripts/JQueryUI2/ui/jquery.ui.position.js", "~/Scripts/JQueryUI2/ui/jquery.ui.widget.js", "~/Scripts/JQueryUI2/ui/jquery.ui.menu.js")


附录:javascript混淆器代码

/// <summary>
/// Packs a javascript file into a smaller area, removing unnecessary characters from the output.
/// </summary>
public class ECMAScriptPacker : IHttpHandler
{
/// <summary>
/// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info.
/// </summary>
public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 };

private PackerEncoding encoding = PackerEncoding.Normal;
private bool fastDecode = true;
private bool specialChars = false;
private bool enabled = true;

string IGNORE = "$1";

/// <summary>
/// The encoding level for this instance
/// </summary>
public PackerEncoding Encoding
{
get { return encoding; }
set { encoding = value; }
}

/// <summary>
/// Adds a subroutine to the output to speed up decoding
/// </summary>
public bool FastDecode
{
get { return fastDecode; }
set { fastDecode = value; }
}

/// <summary>
/// Replaces special characters
/// </summary>
public bool SpecialChars
{
get { return specialChars; }
set { specialChars = value; }
}

/// <summary>
/// Packer enabled
/// </summary>
public bool Enabled
{
get { return enabled; }
set { enabled = value; }
}

public ECMAScriptPacker()
{
Encoding = PackerEncoding.Normal;
FastDecode = true;
SpecialChars = false;
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="encoding">The encoding level for this instance</param>
/// <param name="fastDecode">Adds a subroutine to the output to speed up decoding</param>
/// <param name="specialChars">Replaces special characters</param>
public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars)
{
Encoding = encoding;
FastDecode = fastDecode;
SpecialChars = specialChars;
}

/// <summary>
/// Packs the script
/// </summary>
/// <param name="script">the script to pack</param>
/// <returns>the packed script</returns>
public string Pack(string script)
{
if (enabled)
{
script += "\n";
script = basicCompression(script);
if (SpecialChars)
script = encodeSpecialChars(script);
if (Encoding != PackerEncoding.None)
script = encodeKeywords(script);
}
return script;
}

//zero encoding - just removal of whitespace and comments
private string basicCompression(string script)
{
ParseMaster parser = new ParseMaster();
// make safe
parser.EscapeChar = '\\';
// protect strings
parser.Add("'[^'\\n\\r]*'", IGNORE);
parser.Add("\"[^\"\\n\\r]*\"", IGNORE);
// remove comments
parser.Add("\\/\\/[^\\n\\r]*[\\n\\r]");
parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/");
// protect regular expressions
parser.Add("\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)", "$2");
parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?", IGNORE);
// remove: ;;; doSomething();
if (specialChars)
parser.Add(";;[^\\n\\r]+[\\n\\r]");
// remove redundant semi-colons
parser.Add(";+\\s*([};])", "$2");
// remove white-space
parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3");
parser.Add("([+\\-])\\s+([+\\-])", "$2 $3");
parser.Add("\\s+");
// done
return parser.Exec(script);
}

WordList encodingLookup;
private string encodeSpecialChars(string script)
{
ParseMaster parser = new ParseMaster();
// replace: $name -> n, $$name -> na
parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)",
new ParseMaster.MatchGroupEvaluator(encodeLocalVars));

// replace: _name -> _0, double-underscore (__name) is ignored
Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*");

// build the word list
encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate));

parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup));

script = parser.Exec(script);
return script;
}

private string encodeKeywords(string script)
{
// escape high-ascii values already in the script (i.e. in strings)
if (Encoding == PackerEncoding.HighAscii) script = escape95(script);
// create the parser
ParseMaster parser = new ParseMaster();
EncodeMethod encode = getEncoder(Encoding);

// for high-ascii, don't encode single character low-ascii
Regex regex = new Regex(
(Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+"
);
// build the word list
encodingLookup = analyze(script, regex, encode);

// encode
parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+",
new ParseMaster.MatchGroupEvaluator(encodeWithLookup));

// if encoded, wrap the script in a decoding function
return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup);
}

private string bootStrap(string packed, WordList keywords)
{
// packed: the packed script
packed = "'" + escape(packed) + "'";

// ascii: base for encoding
int ascii = Math.Min(keywords.Sorted.Count, (int)Encoding);
if (ascii == 0)
ascii = 1;

// count: number of words contained in the script
int count = keywords.Sorted.Count;

// keywords: list of words contained in the script
foreach (object key in keywords.Protected.Keys)
{
keywords.Sorted[(int)key] = "";
}
// convert from a string to an array
StringBuilder sbKeywords = new StringBuilder("'");
foreach (string word in keywords.Sorted)
sbKeywords.Append(word + "|");
sbKeywords.Remove(sbKeywords.Length - 1, 1);
string keywordsout = sbKeywords.ToString() + "'.split('|')";

string encode;
string inline = "c";

switch (Encoding)
{
case PackerEncoding.Mid:
encode = "function(c){return c.toString(36)}";
inline += ".toString(a)";
break;
case PackerEncoding.Normal:
encode = "function(c){return(c<a?\"\":e(parseInt(c/a)))+" +
"((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}";
inline += ".toString(a)";
break;
case PackerEncoding.HighAscii:
encode = "function(c){return(c<a?\"\":e(c/a))+" +
"String.fromCharCode(c%a+161)}";
inline += ".toString(a)";
break;
default:
encode = "function(c){return c}";
break;
}

// decode: code snippet to speed up decoding
string decode = "";
if (fastDecode)
{
decode = "if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;}";
if (Encoding == PackerEncoding.HighAscii)
decode = decode.Replace("\\\\w", "[\\xa1-\\xff]");
else if (Encoding == PackerEncoding.Numeric)
decode = decode.Replace("e(c)", inline);
if (count == 0)
decode = decode.Replace("c=1", "c=0");
}

// boot function
string unpack = "function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}";
Regex r;
if (fastDecode)
{
//insert the decoder
r = new Regex("\\{");
unpack = r.Replace(unpack, "{" + decode + ";", 1);
}

if (Encoding == PackerEncoding.HighAscii)
{
// get rid of the word-boundries for regexp matches
r = new Regex("'\\\\\\\\b'\\s*\\+|\\+\\s*'\\\\\\\\b'");
unpack = r.Replace(unpack, "");
}
if (Encoding == PackerEncoding.HighAscii || ascii > (int)PackerEncoding.Normal || fastDecode)
{
// insert the encode function
r = new Regex("\\{");
unpack = r.Replace(unpack, "{e=" + encode + ";", 1);
}
else
{
r = new Regex("e\\(c\\)");
unpack = r.Replace(unpack, inline);
}
// no need to pack the boot function since i've already done it
string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout;
if (fastDecode)
{
//insert placeholders for the decoder
_params += ",0,{}";
}
// the whole thing
return "eval(" + unpack + "(" + _params + "))\n";
}

private string escape(string input)
{
Regex r = new Regex("([\\\\'])");
return r.Replace(input, "\\$1");
}

private EncodeMethod getEncoder(PackerEncoding encoding)
{
switch (encoding)
{
case PackerEncoding.Mid:
return new EncodeMethod(encode36);
case PackerEncoding.Normal:
return new EncodeMethod(encode62);
case PackerEncoding.HighAscii:
return new EncodeMethod(encode95);
default:
return new EncodeMethod(encode10);
}
}

private string encode10(int code)
{
return code.ToString();
}

//lookups seemed like the easiest way to do this since
// I don't know of an equivalent to .toString(36)
private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz";

private string encode36(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(36, i)) % 36;
encoded = lookup36[digit] + encoded;
code -= digit * (int)Math.Pow(36, i++);
} while (code > 0);
return encoded;
}

private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

private string encode62(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(62, i)) % 62;
encoded = lookup62[digit] + encoded;
code -= digit * (int)Math.Pow(62, i++);
} while (code > 0);
return encoded;
}

private static string lookup95 = "¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ";

private string encode95(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(95, i)) % 95;
encoded = lookup95[digit] + encoded;
code -= digit * (int)Math.Pow(95, i++);
} while (code > 0);
return encoded;
}

private string escape95(string input)
{
Regex r = new Regex("[\xa1-\xff]");
return r.Replace(input, new MatchEvaluator(escape95Eval));
}

private string escape95Eval(Match match)
{
return "\\x" + ((int)match.Value[0]).ToString("x"); //return hexadecimal value
}

private string encodeLocalVars(Match match, int offset)
{
int length = match.Groups[offset + 2].Length;
int start = length - Math.Max(length - match.Groups[offset + 3].Length, 0);
return match.Groups[offset + 1].Value.Substring(start, length) +
match.Groups[offset + 4].Value;
}

private string encodeWithLookup(Match match, int offset)
{
return (string)encodingLookup.Encoded[match.Groups[offset].Value];
}

private delegate string EncodeMethod(int code);

private string encodePrivate(int code)
{
return "_" + code;
}

private WordList analyze(string input, Regex regex, EncodeMethod encodeMethod)
{
// analyse
// retreive all words in the script
MatchCollection all = regex.Matches(input);
WordList rtrn;
rtrn.Sorted = new StringCollection(); // list of words sorted by frequency
rtrn.Protected = new HybridDictionary(); // dictionary of word->encoding
rtrn.Encoded = new HybridDictionary(); // instances of "protected" words
if (all.Count > 0)
{
StringCollection unsorted = new StringCollection(); // same list, not sorted
HybridDictionary Protected = new HybridDictionary(); // "protected" words (dictionary of word->"word")
HybridDictionary values = new HybridDictionary(); // dictionary of charCode->encoding (eg. 256->ff)
HybridDictionary count = new HybridDictionary(); // word->count
int i = all.Count, j = 0;
string word;
// count the occurrences - used for sorting later
do
{
word = "$" + all[--i].Value;
if (count[word] == null)
{
count[word] = 0;
unsorted.Add(word);
// make a dictionary of all of the protected words in this script
//  these are words that might be mistaken for encoding
Protected["$" + (values[j] = encodeMethod(j))] = j++;
}
// increment the word counter
count[word] = (int)count[word] + 1;
} while (i > 0);
/* prepare to sort the word list, first we must protect
words that are also used as codes. we assign them a code
equivalent to the word itself.
e.g. if "do" falls within our encoding range
then we store keywords["do"] = "do";
this avoids problems when decoding */
i = unsorted.Count;
string[] sortedarr = new string[unsorted.Count];
do
{
word = unsorted[--i];
if (Protected[word] != null)
{
sortedarr[(int)Protected[word]] = word.Substring(1);
rtrn.Protected[(int)Protected[word]] = true;
count[word] = 0;
}
} while (i > 0);
string[] unsortedarr = new string[unsorted.Count];
unsorted.CopyTo(unsortedarr, 0);
// sort the words by frequency
Array.Sort(unsortedarr, (IComparer)new CountComparer(count));
j = 0;
/*because there are "protected" words in the list
we must add the sorted words around them */
do
{
if (sortedarr[i] == null)
sortedarr[i] = unsortedarr[j++].Substring(1);
rtrn.Encoded[sortedarr[i]] = values[i];
} while (++i < unsortedarr.Length);
rtrn.Sorted.AddRange(sortedarr);
}
return rtrn;
}

private struct WordList
{
public StringCollection Sorted;
public HybridDictionary Encoded;
public HybridDictionary Protected;
}

private class CountComparer : IComparer
{
HybridDictionary count;

public CountComparer(HybridDictionary count)
{
this.count = count;
}

#region IComparer Members

public int Compare(object x, object y)
{
return (int)count[y] - (int)count[x];
}

#endregion
}
#region IHttpHandler Members

public void ProcessRequest(HttpContext context)
{
// try and read settings from config file
if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)
{
NameValueCollection cfg =
(NameValueCollection)
System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");
if (cfg["Encoding"] != null)
{
switch (cfg["Encoding"].ToLower())
{
case "none":
Encoding = PackerEncoding.None;
break;
case "numeric":
Encoding = PackerEncoding.Numeric;
break;
case "mid":
Encoding = PackerEncoding.Mid;
break;
case "normal":
Encoding = PackerEncoding.Normal;
break;
case "highascii":
case "high":
Encoding = PackerEncoding.HighAscii;
break;
}
}
if (cfg["FastDecode"] != null)
{
if (cfg["FastDecode"].ToLower() == "true")
FastDecode = true;
else
FastDecode = false;
}
if (cfg["SpecialChars"] != null)
{
if (cfg["SpecialChars"].ToLower() == "true")
SpecialChars = true;
else
SpecialChars = false;
}
if (cfg["Enabled"] != null)
{
if (cfg["Enabled"].ToLower() == "true")
Enabled = true;
else
Enabled = false;
}
}
// try and read settings from URL
if (context.Request.QueryString["Encoding"] != null)
{
switch (context.Request.QueryString["Encoding"].ToLower())
{
case "none":
Encoding = PackerEncoding.None;
break;
case "numeric":
Encoding = PackerEncoding.Numeric;
break;
case "mid":
Encoding = PackerEncoding.Mid;
break;
case "normal":
Encoding = PackerEncoding.Normal;
break;
case "highascii":
case "high":
Encoding = PackerEncoding.HighAscii;
break;
}
}
if (context.Request.QueryString["FastDecode"] != null)
{
if (context.Request.QueryString["FastDecode"].ToLower() == "true")
FastDecode = true;
else
FastDecode = false;
}
if (context.Request.QueryString["SpecialChars"] != null)
{
if (context.Request.QueryString["SpecialChars"].ToLower() == "true")
SpecialChars = true;
else
SpecialChars = false;
}
if (context.Request.QueryString["Enabled"] != null)
{
if (context.Request.QueryString["Enabled"].ToLower() == "true")
Enabled = true;
else
Enabled = false;
}
//handle the request
TextReader r = new StreamReader(context.Request.PhysicalPath);
string jscontent = r.ReadToEnd();
r.Close();
context.Response.ContentType = "text/javascript";
context.Response.Output.Write(Pack(jscontent));
}

public bool IsReusable
{
get
{
if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)
{
NameValueCollection cfg =
(NameValueCollection)
System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");
if (cfg["IsReusable"] != null)
if (cfg["IsReusable"].ToLower() == "true")
return true;
}
return false;
}
}

#endregion
}


 

总结:

1. 通过继承IBundleTransform接口能够对Asp.net MVC中自带Bundle类进行扩展。

2. 可以动态添加Bundle,而不必在Globale中一次性全都添加。

3. 除了Asp.net MVC自带的Bundle外,还有其他的Bundle插件,例如Bundle Transformer: YUI 1.8.0

 

 

本文摘自:http://blog.csdn.net/leewhoee/article/details/19107013#t3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐