概述. 在美国第44任总统就职日这个历史性的日子,让我们来分析一下新的白宫网站(whitehouse.gov)代码结构吧。网站基于ASP.NET构建。

[singlepic=18466]

whitehouse.gov网站使用IIS 6.0. 网站的HTTP头信息中包含键值对”Server: Microsoft-IIS/6.0″。网站并没有使用微软公司最新版本服务器软件,IIS/7。

whitehouse.gov网站使用ASP.NET 2.0. HTTP头中标识自己的程序版本为”X-Aspnet-Version: 2.0.50727″。这个信息可以去掉,这样可以为每次服务器响应节省30字节带宽。

whitehouse.gov网站使用JQuery 1.2.6. JQuery JavaScript库,使用了其最小集版本,位于/includes/文件夹下。很多开发者使用Google服务器托管的JQuery以便提升性能和减小下载脚本的延迟时间。这样做可以提升站点性能。

<script type="text/javascript" src="/includes/eop/jquery-1.2.6.min.js"></script>

whitehouse.gov网站使用GZIP压缩. 所有网站文本都采用了GZIP压缩,显著地提升了性能。

未压缩大小:  48218 bytes
压缩后大小:   8370 bytes
节省带宽:           ~80%

whitehouse.gov网站使用Vary: Accept-Encoding. 在HTTP头中添加Vary: Accept-Encoding是一种强制代理不向不能解码GZIP的客户端发送GZIP内容的手段。

whitehouse.gov网站使用Cache-Control: privatemax-age. 它使用了”max-age=85895“,大约1 天。这样,html页面应该只会在你的计算机中缓存1天。

whitehouse.gov网站使用Web Trends Live追踪技术. WebTrends声称是“领先的网页分析和客户为中心智能市场营销解决方案(leading provider of web analytics and consumer-centric marketing intelligence solutions)。”

whitehouse.gov网站使用meta keywords标记. 这样做很奇怪,因为Internet上的访问者很少有不知道这里是干什么的。meta标记如下:

<meta name="keywords" content="President, Barack Obama, White House,
United States of America, 44th President, White House history, President Obama,
Barck, Barek, Barak, Barrack, Barrak, Obma, Barack" />

这样并不会对提升whitehouse.gov网站的Google PageRank有多少贡献。我认为搜索Barack Obama的用户无论如何都会被导引到这个网站的。

whitehouse.gov网站使用ViewState. 这是一个隐藏表单,允许网站在浏览器中存储服务器端数据。ASP.NET自动解析发送到浏览器的ViewState信息。浏览器不应该解析这个信息。

whitehouse.gov网站使用WebResource.axd. 这是ASP.NET生成的一个脚本文件。通常它们不能被有效地缓存,并且我发现它们的确降低了性能。

whitehouse.gov网站包含了很多空白字符. 如果你的浏览器启用了GZIP,这并不会带来性能影响,但是如果去掉这些空白字符,网站代码可以减小20%以上。

whitehouse.gov网站包含注释标签. 网站使用了很多HTML注释分割页面代码区域。如果能将这些注释写在服务器端代码中,可以在编译页面时不将注释编译在最终页面代码中,从而提升效率。

<!-- Start -->
<!-- End -->

whitehouse.gov网站包含很长很长的ASP.NET ID. 页面中的很多元素包含非常长的ID,是浪费带宽的主要因素之一。这些长ID可以很容易地在服务器端替换成短ID。

<a id="ctl09_rptNavigation_ctl00_rptNavigationItems_ctl01_hlSubNav"...

whitehouse.gov网站使用的GIF多于PNG. 网站使用的GIF图片多于PNG图片。PNG格式的图片可以更加优化,从而节省带宽和消耗。网站中使用了两个GIF动画。

whitehouse.gov网站使用了5个层叠式样式表(stylesheets)文件和12个JavaScripts脚本文件. 如果能把这两类文件合并成两个文件,网站可以更快而且更轻量级。当然这是针对访问首页的访客而言。奇怪的是,用于修饰管理页面的层叠式样式表也被加载进了普通用户的访问中:

/* admin styles */
/* cms */
.adminNavigation {width:996px; position:relative; z-index:100;}

whitehouse.gov网站使用了高度压缩的JPG. 如果你距离屏幕较远,或者视力不是那么好,这些图片看起来还行。开发者对JPG图片采用了高度压缩。这里显示的图片被放大并且轻微地二次压缩过。

[singlepic=18465]

whitehouse.gov网站使用了image sprites技术. 这项技术可以大幅提升站点性能,因为它将若干个小图片合并成了一张图片。这是一项先进的技术。采用了image sprites技术的图片是”nav-sprite.png“.

whitehouse.gov网站使用了Packer. Dean Edward的Packer是一个用于压缩JavaScript文件的工具。JavaScript脚本将在被下载时自动解压缩,这是一项很差的优化手段, 因为往往JavaScript经过GZIP压缩之后,比经过YUI压缩器(YUI Compressor)压缩之后更小。[参见 jquery-plugins.js]

eval(function(p,a,c,k,e,d)...

使用Packer的决定不像是一个深入了解GZIP或者文本压缩技术的人做出的。压缩之前的文件确实变小了,然而这导致了最终需要被下载的文件变大了。[http://dean.edwards.name/packer/]

whitehouse.gov大小821 KB, 在我的线路上(cable modem)使用了1.58秒完成加载。这个数字大约是新的、基于图片的网站的平均值。

加载时间:  1.58 seconds
总计大小:  821 KB

whitehouse.gov网站包含了几个隐藏链接. 在源文件里,大部分是在JavaScript中,大约嵌入了6个链接。这样,这些幸运的人就得到了来自白宫网站的外链(译者注:PageRank 9啊!9!)。

www.youngpup.net
http://sorgalla.com/jcarousel/
http://billwscott.com/carousel/
http://www.codylindley.com

whitehouse.gov网站使用了一个不透明的favicon.ico. 为了站点在加入书签后具有更好的视觉效果(译者注:对很多非IE浏览器而言,无论是否加入书签,favicon文件都会显示在标签栏),一个具有透明背景的favicon是更好的选择。这项改变对于一个知道怎么修改的人来说可以在10分钟内完成。

结论是,whitehouse.gov网站还是很吸引人的。虽然它并不算是一个非常有效率的站点,并且过多地注意了视觉效果。一个网站优化专家可以在几天之内将它的加载速度提升到现在的两倍。

最后,记住本届政府和奥巴马总统并不是写这些代码的人。

翻译自:http://dotnetperls.com/Content/whitehouse-gov-Site.aspx

A little jazz, help you to move on.

Let’s Start From Here
王若琳
giving up, why should i
we’ve come to far to forget
we’re beautiful, we just got lost
somewhere along the way
so much was missing when you went away
let’s start from here, lose the past
change our minds, we don’t need a finish line
let’s take this chance don’t think too deep
and all those promises we couldn’t seem to keep
i don’t care where we go
let’s start from here
standing here face to face
a finger on your lips
don’t say a word don’t make a sound
silence surrounds us now
even when you were gone i felt you everywhere
let’s start from here, lose the past
change our minds, we don’t need a finish line
let’s take this chance don’t think too deep
and all those promises we couldn’t seem to keep
i don’t care where we go
let’s start from here
let’s start from here
i’ve never been the one to open up
but you’ve always been the voive within
the only warmth from my cold heart
let’s start from here, lose the past
change our minds, we don’t need a finish line
let’s take this chance don’t think too deep
and all those promises
let’s start from here, lose the past
change our minds
we don’t need a finish line
let’s take this chance don’t think too deep
and all those promises we couldn’t seem to keep
i don’t care where we go
let’s start from here
let’s start from here
let’s start from here
let’s start from here

“/”应用程序中的服务器错误。
验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保<machineKey>配置指定了相同的validationKey和验证算法。不能在群集中使用AutoGenerate。

概括地说,就是POST执行时引起的“验证视图状态MAC失败”错误。
原因很简单:当你想使用传统POST提交信息到另一个aspx页面时出现这个错误。如果提交的action是本页面则不会出错。
解决方案:也很简单,去掉form中的runat=”server”即可。

这几天有点小活,要求是ASP.NET的,虽然我不怎么看好ASP.NET,但是人家要求了我也就照做吧。
最复杂的部分是一个TreeView。出于兼容性和简单性考虑没有用ASP.NET的TreeView控件,而是参考一篇文章自行写了一个出来。

[singlepic=18460]

技术指标如下:

  • 实现:Div+CSS+JavaScript+ASP.NET(C#)
  • 功能:可以实现无限级的TreeNode
  • 标准:在IE 7和Firefox 3.1下显示效果相同,兼容XHTML 1.0 Strict最严格的网页标准

效果如左图所示。最后一个函数可能会因为超宽看不到全部,具体请参照文章附件中的源代码。
附件下载:http://download.nocoo.us/Download/Archive/TreeViewCode.rar

TreeNode.cs

[code=’c#’]
///

/// TreeView结点类
///

public class TreeNode
{
private string name;
private string href;
private List subNodes = new List();

///

/// 结点名
///

public string Name { get { return name; } }
///

/// 结点链接
///

public string Href { get { return href; } }
///

/// 下级结点个数
///

public int Count { get { return this.subNodes.Count; } }
///

/// 获取或者设置下级结点
///

/// 序号 /// 下级结点
public TreeNode this[int index]
{
get { return subNodes[index]; }
set { subNodes[index] = value; }
}

///

/// 构造函数
///

/// 结点名 public TreeNode(string name)
{
this.name = name;
this.href = null;
}

///

/// 构造函数
///

/// 结点名 /// /// 结点链接 public TreeNode(string name, string href)
{
this.name = name;
this.href = href;
}

///

/// 添加下级结点
///

/// 新结点 public void Add(TreeNode node)
{
subNodes.Add(node);
}
}
[/code]

TreeView.cs

[code=’c#’]
///

/// TreeView
///

public class TreeView
{
private List nodes = new List();

///

/// 填充测试用数据
///

public void FillTestData()
{
TreeNode node1 = new TreeNode(“中国”, “#”);
TreeNode node11 = new TreeNode(“华北地区”, “#”);
TreeNode node111 = new TreeNode(“河南省”, “#”);
TreeNode node112 = new TreeNode(“河北省”, “#”);
TreeNode node113 = new TreeNode(“山东省”, “#”);
TreeNode node1131 = new TreeNode(“青岛市”, “#”);
TreeNode node1132 = new TreeNode(“济南市”, “#”);
TreeNode node11321 = new TreeNode(“市中区”, “#”);
TreeNode node11322 = new TreeNode(“历下区”, “#”);
TreeNode node11323 = new TreeNode(“槐荫区”, “#”);
TreeNode node11324 = new TreeNode(“天桥区”, “#”);
TreeNode node11325 = new TreeNode(“长清区”, “#”);
TreeNode node1133 = new TreeNode(“菏泽市”, “#”);
TreeNode node1134 = new TreeNode(“济宁市”, “#”);
TreeNode node1135 = new TreeNode(“德州市”, “#”);
TreeNode node12 = new TreeNode(“东北地区”, “#”);
TreeNode node13 = new TreeNode(“西北地区”, “#”);
TreeNode node14 = new TreeNode(“华东地区”, “#”);
TreeNode node15 = new TreeNode(“西南地区”, “#”);
TreeNode node16 = new TreeNode(“华南地区”, “#”);
TreeNode node17 = new TreeNode(“华中地区”, “#”);
TreeNode node18 = new TreeNode(“港澳台地区”, “#”);

node1132.Add(node11321);
node1132.Add(node11322);
node1132.Add(node11323);
node1132.Add(node11324);
node1132.Add(node11325);

node113.Add(node1131);
node113.Add(node1132);
node113.Add(node1133);
node113.Add(node1134);
node113.Add(node1135);

node11.Add(node111);
node11.Add(node112);
node11.Add(node113);

node1.Add(node11);
node1.Add(node12);
node1.Add(node13);
node1.Add(node14);
node1.Add(node15);
node1.Add(node16);
node1.Add(node17);
node1.Add(node18);

nodes.Add(node1);
nodes.Add(new TreeNode(“俄罗斯”, “”));
nodes.Add(new TreeNode(“美国”));
nodes.Add(new TreeNode(“韩国”));
nodes.Add(new TreeNode(“澳大利亚”));
nodes.Add(new TreeNode(“印度”, “”));
nodes.Add(new TreeNode(“加拿大”, “”));
}

///

/// 获取TreeView的html代码
///

///
public string GetHtmlString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(“

    “);
    for (int i = 0; i < nodes.Count; i++) { sb.AppendLine(GetNodeHtml(nodes[i], 1, (i + 1).ToString())); } sb.AppendLine("

“);
return sb.ToString();
}

private string GetNodeHtml(TreeNode thisNode, int depth, string id)
{
StringBuilder sb = new StringBuilder();
bool hasSub = (thisNode.Count > 0) ? true : false;
sb.AppendLine(string.Format(“

  • “, depth, id));
    sb.AppendLine(string.Format(

    {3}

    “,
    hasSub ? “closedir” : “nodir”,
    depth,
    id,
    (thisNode.Href == null || thisNode.Href.Equals(“”)) ? thisNode.Name : string.Format(“{1}“, thisNode.Href, thisNode.Name)
    ));
    if (hasSub)
    {
    sb.AppendLine(string.Format(“

    “, depth, id));
    sb.AppendLine(“

      “);
      for (int i = 0; i < thisNode.Count; i++) { sb.AppendLine(GetNodeHtml(thisNode[i], depth + 1, string.Format("{0}_{1}", id, i + 1))); } sb.AppendLine("

    “);
    sb.AppendLine(“

    “);
    }
    sb.AppendLine(“

  • “);
    return sb.ToString();
    }
    }
    [/code]

    ASP.NET页面调用:

    [code=’c#’]
    <% CMC.TreeView list = new CMC.TreeView(); list.FillTestData(); Response.Write(list.GetHtmlString()); %>
    [/code]

    ASP.NET页面JavaScript:

    [code=’js’]
    defaultNodeState();

    function defaultNodeState()
    {
    var nodeState = getCookie(“nodeState”);
    if(nodeState == null)
    {
    nodeState = “,|,|,”;
    setCookie(“nodeState”,nodeState);
    }
    var layer = nodeState.split(‘|’);
    for(var i=0;i

    我一直觉得孔乙己会做一个很好的HR。
    “读过书,……我便考你一考。茴香豆的茴字,怎样写的?” ——孔乙己

    在初学一门编程语言的时候,写一个“Hello world!”程序是最常见的入门方法。通过写一个成功的“Hello world!”,可以实践这门语言最基本的语法特性,还可以带给自己成就感,真是一举两得。C/C++语言本身有很多特性,如果能够将这些技术分解出来变成一个个的“Hello world!”,并且将这些技术点到为止,貌似也算是一件善事。这里,列举了10个“Hello world!”程序,大家雅俗共赏一下。

    1. 最经典的“Hello world!”

    “Hello world!”最经典的写法当然是直接用 printf 输出“Hello world!”这几个字符了。无论用C还是 C++,写起来都非常的简洁明了。这里把最常见的几个全部列在下面。

    [code=’c++’]
    #include
    #include

    int main()
    {
    printf(“Hello world!”); // 教科书的写法
    puts(“Hello world!”); // 我最喜欢的
    puts(“Hello” ” ” “world!”); // 拼接字符串
    std::cout << "Hello world!" << std::endl; // C++风格的教科书写法 return 0; } [/code] 特别需要注意的是,在C/C++里,如果两个字符串之间除空白符以外没有任何东西,编译器会自动认为这两个字符串是连在一起的字符串。这样,如果一个字符串过长,可以用这种方法换行来写,既不浪费性能,又美观。

    2. 用宏写的“Hello world!”

    在C/C++里,宏是一个神奇的东西。特别是在C语言中,宏可以帮我们做一些“又脏又累”的活,包括拼接代码片断、隐藏繁琐的实现细节等等。其中特别有趣的是“#”的用法,它可以“提取”参数的名字,把它变成字符串。
    [code=’c++’]
    #include

    #define Say(sth) puts(#sth)

    int main()
    {
    return Say(Hello world!);
    }
    [/code]

    请注意,这个Hello world可是完全没有出现引号哦!

    3. 断章取义的“Hello world!”

    字符串是一种常量这当然毫无疑问,但是它的类型是什么,这就需要考虑一下了。使用C++的typeid就可以这个问题的答案,而且只要是符合C或C++标准的编译器就应该是一样的结果。比如字符串“Hello world!”,它的类型就是 char const [13]。
    知道了这个,就可以写出以下的“Hello world!”:
    [code=’c++’]
    #include

    int main()
    {
    return puts(&”Do not say: Hello world!”[12]);
    }
    [/code]

    4. 退出时运行的“Hello world!”

    大家都知道 main 函数退出意味着程序结束,可是这并不完全正确,我们完全可以在 main 函数退出以后做很多事呢——比如说,输出“Hello world!”。这个功能依赖于C标准库中提供的函数 atexit(),调用这个函数并注册自己的回调函数就行。需要注意,这个函数可以调用多次,最后注册的函数最先执行。
    [code=’c++’]
    #include
    #include

    void say()
    {
    printf(“world!”);
    }

    void sth()
    {
    printf(“Hello “);
    }

    int main()
    {
    return atexit(say), atexit(sth);
    }
    [/code]

    5. 读取自己的“Hello world!”

    C/C++的编译器提供了一些有用的内置宏,最常用的就是 __FILE__ 和 __LINE__ 了。其中,__FILE__ 代表当前的源文件的文件名,嗯,对了,如果我们让这个程序读取自己的源文件,不就可以做一个很有意思的“Hello world!”了么?
    [code=’c++’]
    // Hello world!

    #include
    #include
    #include

    int main()
    {
    std::ifstream ifs(__FILE__);
    std::string say, some, word;

    ifs >> say >> some >> word;
    std::cout << some << " " << word; return 0; } [/code]

    6. 话分两头的“Hello world!”

    有了C++的类,我们就可以光明正大的在 main 函数执行之前和之后做感兴趣的事情了。我们可以声明一个全局的类的实例,这样,在 main 函数执行之前会调用这个类的构造函数,结束之后则会调用析构函数。
    [code=’c++’]
    #include

    class say
    {
    public:
    say()
    {
    std::cout << "Hell"; } ~say() { std::cout << "world!"; } }hello; int main() { std::cout << "o "; return 0; } [/code]

    7. 传入模板的“Hello world!”

    C++的模板功能极为强大,可以说是C++里面最艰深、最经典、最时尚的部分。一个“Hello world!”当然无法使用很多很高级的模板技巧,我也不想只使用模板特化这样无阻挂齿的小技巧,嗯,那就来演示一个比较罕见的用法吧。
    [code=’c++’]
    #include

    template
    class say
    {
    public:
    void operator () ()
    {
    std::cout << words; } }; extern char hello[] = "Hello world!"; int main() { return say()(), 0;
    }
    [/code]

    请注意,这个 extern 是十分必要的,只有加上了 extern,这个指针才是一个编译器间可以确定的值,也才可以参与模板运算。还有,hello 必须为数组类型,而不能为 char*,这个道理和加 extern 是一样的。
    此外,这里还演示了 functor 的用法,嗯,关于它的优点就不在这里多说了,反正是与原生指针相比有很多好处就是了。

    8. 调用私有函数的“Hello world!”

    我们知道,C++类的私有函数是不能被外界访问的,比如说 main 函数里面,它绝对不能访问类的私有函数,除非把它设为类的友元函数。不过我们还是可以用一些比较奇怪的方法访问类的私有函数——当然,这个私有函数必须满足一个条件:它是虚函数。
    这里就涉及到一个问题,指向虚函数的虚表放在哪里?对于 VS.Net 2003 而言,虚表是类的第一个成员,虚函数指针按照函数声明的顺序放在虚表里面。当然,这个说法并不严谨,更细节的东西还是去看看那本“成人高钙奶粉”吧,它会给出最权威的解答。
    这里是一个很有意思的例子:
    [code=’c++’]
    #include
    #include

    class secret
    {
    private:
    virtual void say()
    {
    std::cout << "Hello world!"; } }; int main() { secret word; (reinterpret_cast(**(intptr_t**)(&word)))();

    return 0;
    }
    [/code]

    9. 最暴力的“Hello world!”

    最暴力的调用函数的方法是:直接修改函数的返回地址,让这个地址指向我们想要调用的函数。这也就是缓冲区溢出漏洞的应用方法了,不过里面还涉及到很多问题,在这里就不一一列举,想要了解的话,还是去 Google 吧。这里只演示一个可以在 VS.Net 2003 下可以用的“Hello world!”。
    [code=’c++’]
    #include
    #include
    #include

    void say()
    {
    puts(“Hello world!”);
    exit(0);
    }

    int main()
    {
    volatile intptr_t a = 0;
    volatile intptr_t * p = &a;

    *(p + 2) = (intptr_t)say;
    *(p + 3) = (intptr_t)say;

    return 0;
    }
    [/code]

    10. 外星人说的“Hello world!”

    好了,这个“Hello world!”是最匪夷所思的一个了!不过它并没有涉及任何复杂的C/C++语言特性,只是看起来有点酷。你能看懂外星人在说什么不?
    [code=’c++’]
    #include

    void alien_say(char * p)
    {
    while (putchar(*(p += *(p + 1) – *p)));
    }

    int main()
    {
    return alien_say(“BETHO! Altec oh liryom(a loadjudas!) dowd.”), 0;
    }
    [/code]

    [code=’c#’]
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection;

    namespace ConsoleApplication1
    {
    public class ReflectionTest
    {
    public const int test1 = 1;
    public readonly static int test2 = 1;
    public static int test3 = 1;
    public readonly int test4 = 1;
    }

    class Program
    {
    static void Main(string[] args)
    {
    ReflectionTest t = new ReflectionTest();
    FieldInfo f;

    // const int test1
    // const不应该被更改,猜想应当抛出异常
    // 运行中SetValue(t, 2);确实抛出System.FieldAccessException异常
    Console.WriteLine(“test1=” + ReflectionTest.test1);
    f = typeof(ReflectionTest).GetField(“test1”);
    f.SetValue(t, 2);
    Console.WriteLine(“test1=” + ReflectionTest.test1);

    // readonly static int test2
    // 因为readonly不进行编译时检查,编译通过,但是执行时应当出错
    // 但是实际中没有任何提示,也并未更改,错误被吃掉了!
    Console.WriteLine(“test2=” + ReflectionTest.test2);
    f = typeof(ReflectionTest).GetField(“test2”);
    f.SetValue(t, 2);
    Console.WriteLine(“test2=” + ReflectionTest.test2);

    // static int test3
    // static变量当然可以通过反射更改
    Console.WriteLine(“test3=” + ReflectionTest.test3);
    f = typeof(ReflectionTest).GetField(“test3”);
    f.SetValue(t, 2);
    Console.WriteLine(“test3=” + ReflectionTest.test3);

    // readonly int test4
    // 实例中的readonly变量可以通过反射更改其值
    // 因此,readonly变量的赋值方法总共有三种:
    // 1. 在声明时对readonly变量赋值;
    // 2. 在构造函数中对readonly变量赋值;
    // 3. 使用反射在实例生命期中对readonly变量赋值。
    Console.WriteLine(“test4=” + t.test4);
    f = typeof(ReflectionTest).GetField(“test4”);
    f.SetValue(t, 2);
    Console.WriteLine(“test4=” + t.test4);
    }
    }
    }
    [/code]

    运行结果:
    [code=’sh’]
    test1=1
    未处理的异常: System.FieldAccessException: 无法设置常量字段。
    在 System.Reflection.MdFieldInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureIn
    fo culture)
    在 System.Reflection.FieldInfo.SetValue(Object obj, Object value)
    在 ConsoleApplication1.Program.Main(String[] args) 位置 C:\Documents and Settings\Zheng Li\桌面\ConsoleApplication1\C
    onsoleApplication1\Program.cs:行号 28
    [/code]

    注释掉第28行的结果:
    [code=’sh’]
    test1=1
    test1=1
    test2=1
    test2=1
    test3=1
    test3=2
    test4=1
    test4=2
    [/code]

    用惯C#,比如我想让一个int数等于无穷大,只需要简单写:
    [code=’c#’]
    int a = int.MaxValue;
    [/code]
    转到C++上,疑惑了…
    首先C++不会有属性这种东西,而且不同平台上的某个类型的最大最小值是不一样的。
    解决方案是使用limits头文件的方法。
    [code=’cpp’]
    #include #include
    using namespace std;

    int _tmain(int argc, _TCHAR* argv[])
    {
    cout<<"short:"<

    文章导航