[转载]iframe的onload事件 - 与时俱进 - 博客园

mikel阅读(1335)

[转载]iframe的onload事件 – 与时俱进 – 博客园.

很 多时候,我们会需要改变一个iframe的地址(src属性),或者使用表单(form)的target在指定的iframe进行提交后,在 iframe加载完毕(onload)时立即响应某个操作,以提高WEB应用程序的价值。本文讨论了跨浏览器的iframe onload事件的监听方法。

如果你没时间去阅读全文,可以看解决方案的内容概要:

  1. 同域的页面嵌套,最好的是让内嵌的页面调用父页面的函数,如 window.parent.callparentFunctoin()。
  2. 如 果是异域,或者子页面已存在且无法修改,那么:在Firefox/Opera/Safari中,可以直接使用iframe onload事件;而在IE中,可以通过定时器测定子页面的document.readyState,或者使用iframe onreadystatechange事件计算该事件的响应次数。

以上内容基于参考文档: Q239638 和 Q188763.

如果你对这个话题很感兴趣,请一定要继续阅读哦。。。

注[1]:为 了使问题更集中,本文所述的<iframe>均直接写在父页面中,对使用 document.createElement(“iframe”)或者其它方式建立的iframe不作讨论,因为这样会使问题在IE下变得更加复杂,但 只要使用本文的结论,无论何方式下建立的iframe,问题仍然会得到解决。

一个简单的包含iframe的父页面将是如下样子的:

<!DOCTYPE ...> <html xmlns="...">   <head>   <meta ... />   <title>Iframe</title>   <body>     <iframe name="iframe1"  id="iframe1" width="300" height="50" src="#" ></iframe>     <script type="text/javascript">//codes here</script>   </body> </html>

开始:

让我们从一种简单的情形和解决方法开始:

1.WINDOW.PARENT 对象

1.1 调用父页面对象

<!–This is an inner page in the iframe–>
<script type=”text/JavaScript”>
window.onload=function{ window.parent.iframeCall();}
</script>

在网上找到的方法中,最令人开心的一个,莫过于在能子页面中调用父页面的对象了:

window.parent.callFunciton()。

不过我想:可能这点全地球人都已经知道了。只是这个方法有一个缺点,那就是子父页面必须在同域中。

还有一点,就是前端工程师们需对子页面有修改权;或者,可以请负责此子页面的同事为我们添加一段代码:

<script type=”text/JavaScript”>
if(window.parent!=window) window.parent.iframeCall();
</script>

把它放到window.onlad中,或者直接放在</body>之前。

注[2]:在对iframe或其它窗口性质的前端编程中,同域名是最完美的先天条件。只要在同一域名中,各个窗口间的对象是共享的,我们完全可以自由发挥,在不同的窗口间来回驾驭。总之,只有想不到,没有做不到。

1.2 异域

在不同域名的页面,浏览器出于安全考虑,几乎完全封锁了页面间的对象来往,这里没有鹊桥,牛郎和织女只能远远想望。当然,用iframe嵌套页面还是可以的,毕竟还可以思念。

面对家族的封锁,罗密欧还是很想见朱丽叶,他在夜里架起梯子抓到朱丽叶的窗前与她见面;在异域的页面嵌套中,子页面总是可以直接改变父窗口的location以防止被嵌套,但父页面对这个一点办法也没有。

当然,子页面除了仅仅永恒地拥有父窗口.location的修改权外,也没有其它了。例如,在IE下,子页面只能直接修改父页面的.location为另一个源:

<script tyle=”text/javascript”>parent.location=”http://anotherPage.com/”;</script>

但无法访问其它对象,如window.name,document等,连location.href等location的子属性就无法访问。当然,在防止嵌套方面,使用top.location会更强大。

但Firefox中,似乎还可以为top.location添加一些东西,但这是在我不严谨的测试中出现过的情况,未经找到相应的权威文档哦。

2. IFRAME ONLOAD 事件

在Firefox/Opera/Safari中,直接使用frame元素的onload事件即可:
document.getElementById(“iframe1”).onload=function(){
//your codes here.
};
只可惜它在IE下经常无效,因为在IE下它最多只能被激活一次,而且无论你有多少个iframe,被激活的也只能是最后一个的。更详细的描述请看:Q239638 和 Q188763

原因
这些事件是在IFRAME内的文档对象模型中激活的,而不是父页面的。在IFRAME加载完毕的时候,这个事件就被激活了,而且ReadyState已经是“完成”状态。所以你无法通过这个事件来查检一个IFRAME是否加载完毕。

为了得到更好的表现,我们再稍稍研究一个问题:IFRAME递归。

3.IFRAME 递归

在处理IFRAME时,浏览器应该有一个基本规则,那就是防止递归,防止页面无限的自我加载,使客户端设备崩溃。事实上,文中出现的几个浏览器均做到这点,只是不同的浏览器有不同的处理方式。请分别尝试以下代码:
<iframe src=”” onload=”finish()” name=”iframe1”></iframe>
<iframe src=”#hashonly” onload=”finish()” name=”iframe2”></iframe>
<iframe src=”?search” onload=”finish()” name=”iframe3”></iframe>
<iframe src=”http://anotherPage.com” onload=”finish()” name=”iframe4”></iframe>
执行的结果是,在父页面加载时,上面的iframe onload函数在IE/Opera/Safari中均会被激活,Firefox对第二个没有反应。这主要因为他们在防止递归方面的处理是不同的。
对于#hashonly和?search这样的URL,浏览器会解释为页面本身。但hash和search的不同之处是,改变 search可以组成新的源,而改变hash不会。通常地,浏览器一遇到同源的iframe内页即会停止加载,但Safari却会加载多一次。
假如把finish()函数写成如下:
var finsh=function(){alert(”onload from :”+this.src);}
运行时分别弹出的消息弹出框的次数如下:

ifm/brw:    IE    |    Firefox    |    Opera    |    Safari iframe1:    1     |       1       |      1      |      0 iframe2:    1     |       0       |      1      |      1 iframe3:    2     |       1       |      2      |      2 iframe4:    1     |       1       |      1      |      1

再结合页面所呈现的内容,可得看出这些浏览器在处理递归问题上的一些细则:

  • Firefox 不会在iframe中加载任何东西和激活onload事件(可能是任何事件)
  • IE和Opera不会在iframe中加载页面,但会激活onload事件。
  • Safari(windows版本)会在iframe中加载页面一次且仅仅一次,并会激活onlaod事件且仅激活依附在父页面上那个iframe的onload事件。

关 于本节,如果仅把iframe用于页面嵌套,那意义不大;如果用于动态加载/呈现内页,或者用于良好用户体验的form target表单提交处理(不是Ajax),并且要求较高的浏览器兼容性时,作用才会显示出来。根据本节结果,为了提高兼容性,最好事先把iframe指 向一个空页面——blank.html,因为它在4种浏览器中的表现是一样的。如果不想事先加载页面,那就得花多点心思去判断浏览器类型了。

4.代码实现

4.1Firefox/Opera/Safari,直接使用iframe onload事件

document.getElementById(“iframe1”).onload=function(){     //your codes here. };

4.2在IE下,定时器测document.readyState或者注册iframeonreadystatechange事件

4.2.1定时器以及document.readyState

var fm1=window.frames["iframe1"]; var fmState=function(){   var state=null;   if(document.readyState){     try{       state=fm1.document.readyState;     }catch(e){state=null;}     if(state=="complete" || !state){//loading,interactive,complete       //onComplete();       return;     }     window.setTimeout(fmState,10);   } }; //在改变src或者通过form target提交表单时,执行语句: if(fmState.TimeoutInt) window.clearTimeout(fmState.timeoutInt); fmState.timeoutInt = window.setTimeout(fmState,400);

为 什么要延时400毫秒?因为javascript对DOM的操作是异步的,我们必须等待脚本对DOM落实执行后才开始下一步。400秒这个数取决与客户端 的设备和浏览器的响应速度,好的设备的响应速度能在10毫秒以内甚至更快,但100毫秒左右可能比较大众化,400毫秒应该是十分保守的了。总之,较大的 毫秒数能适合更多的用户设备状况,并能减少了客户端设备的工作量。

至于document.readyState,指的是iframe内子页的docuent.readyState,而不是父页面的。只要允许,即在同域情况下,document.readyState会返回5个状态:

uninitialized 对象未初始化.
loading 对象正在加载数据.
loaded 对象已加载完数据.
interactive 在这个状态下,用户可以参与互动,即使在对象未加载完毕也可以
complete 对象已完成初始化.

为什么使用try和 catch?因为在异域的情况下,当iframe的子页到达interactive状态时,父页面就会失去访问权,所以最多只能返回到loaded这一步,因此IE出一个未知错误——其实就是没有权限,所以try和catch,让这个错误沉默下去。

幸好这个方法只针对IE(目前我能使用到的版本:IE6/7),否则麻烦大了:Opera不等页面加载完就开始交互了,而IE会等页面加载完毕才进行交互,所以感觉用Opera打开网页的速度相对比IE快。

4.2.2.onreadystatechange 事件三步曲

var stateID={}; var fmStChange=function(){   if(ifFirstLoad) return;   stateID[this.id]=stateID[this.id] ? stateID[this.id]+1:1;   switch (stateID[this.id]){     case 1:       //state loading       //onComplete(STEP1);       break;     case 2:       //state interactive       //onComplete(STEP2);       break;     case 3:       //state complete       //onComplete(LASTSTEP);       break;   }   if(stateID[this.id]&gt;=3) stateID[this.id]=null; }; $("iframe1").onreadystatechange=fmStChange; //if you want to ignore the parent page load //add the following two line var ifFirstLoad=true; $("iframe1").onload=function(){ifFirstLoad=false;}

每当iframe加载页面,过程内会激活onreadystatechange事件三次,相应的状态分别是loading,interactive和complete,而最后一次才是complete,所以我们得计算一下,直到第三次才算是完成。

注意:这 个方案中的stateID在状态判断时非常重要,要进行必要的修正和保护,如在每次应用iframe 时,把stateID[iframe_id]复位为null,或者在第三次响应完成之前,不要对iframe进行新轮页面加载,或者在新一轮的页面加载前 消除之前的事件并复位。

[转载]ie6下的iframe,问题多多。 - izumi - 博客园

mikel阅读(1145)

[转载]ie6下的iframe,问题多多。 – izumi – 博客园.

 

ie6下的iframe,问题多多。

 

今天在ie6下显示Velocity页面中的iframe,发现除了iframe的外框以外,src中请求的url完全没有读取成功,结果是个空的iframe。

总结网上一共有以下几种解决办法:

1,

<iframe name=’ifrm_select_jhs_x’ src=”+url+” scrolling=’no’ frameborder=0 width=’256′ height=’174′></iframe>

ifrm_select_jhs_x.location.href = url;

2,

认为src不能写在<iframe的后面

http://be-evil.org/showlog-91.html

http://social.msdn.microsoft.com/forums/zh-TW/236/thread/a01ee23e-08d6-414d-ba22-9922f456af3c/

3,

认为iframe src的URL长度过长

http://yuweijun.blogspot.com/2008/11/when-iframe-src-is-too-long-in-ie6ie7.html

http://support.microsoft.com/kb/208427/zh-cn

 4,

认为iframe src的URL长度过长

http://yuweijun.blogspot.com/2008/11/when-iframe-src-is-too-long-in-ie6ie7.html

http://support.microsoft.com/kb/208427/zh-cn

 5,

猜测是:html书写不规范造成。因为这个结果页面是jsp动态生成的,可能其它代码造成了影响。

最后解决方法:先生成iframe,点击“更多信息”时改变iframe的src,之前是采用每次点击删除之前的iframe,添加新的iframe对象。

6,

今天解决了此问题 以下是核心代码
………..
Container.innerHTML=”<iframe id=sdfgfer567fgh name=sdfgfer567fgh src=”+url+” frameborder=’0′ scrolling=’no’ width=”+divwidth+” height=”+divh+” marginheight=’0′ marginwidth=’0′>”;//创造iframe 并指定ID
var ieset = navigator.userAgent;
if(ieset.indexOf(“MSIE 6.0”) > -1)//浏览器判断 如果是IE6
setTimeout(‘window.parent[\’sdfgfer567fgh\’].location.reload();’,0);//执行这一方法

……

7,

function logindiv() { if(Fid(login_div)) { document.body.removeChild(Fid(login_div)); } var obj_div=document.createElement(div); obj_div.id=login_div; obj_div.style.top =200 obj_div.style.left=100 obj_div.className=login_box; obj_div.innerHTML= <iframe style=’width:400px;height:400px;border:medium none;margin:0px;padding:0px’ name=’Login’ id=’Login’ marginwidth=’0′ marginheight=’0′ scrolling=’no’ frameborder=’0′ src=”> </iframe>; document.body.appendChild(obj_div); obj_div.getElementsByTagName(iframe)[0].src =http://www.qq.com/

}

8,

  为了实现提示框效果,从而使用动态嵌入iframe方案,但是实施过程中IE7下正常,IE6需要刷新才可正常显示,如下:

Js代码
  1. this.open = function(_sUrl)  
  2. {         
  3.     this.show();  
  4.     var openIframe = “<iframe width=’100%’ height=’100%’ name=’iframe_parent’ id=’iframe_parent’ src='” + _sUrl + “‘ frameborder=’0′ scrolling=’no’></iframe>”;  
  5.     myInnerHTML(‘dialogBody’, openIframe);  
  6. }  

该 方法主要将URL通过iframe形式嵌入到document中,但IE7可正常显示,IE显示空白,而且’iframe_parent’ 的src属性为“”,经过漫长的调试,最终通过增加一刷新iframe语句才得以解 决:document.frames(‘iframe_parent’).location.reload();
对于此类BUG的定位实在困难。

[转载]Fiddler真乃前端大杀器!!! - qjoycn - ITeye技术网站

mikel阅读(1041)

[转载]Fiddler真乃前端大杀器!!! – qjoycn – ITeye技术网站.

在老夫还在搞服务器端时候,老夫觉得能读能改js代码就差不多了吧;

当老夫刚刚踏入前端会想js能设置断点该多好呢?

当老夫手拥firebug、google、ie浏览器开发插件(叫不出名字…)时,老夫是多么的高兴啊!!!

但是有些项目就会让你不自主的感觉痛苦,因为改动非常痛苦!!!

比如我对项目做了一点修改,但是我没办法在正式环境测试;换个方式,若是qq首页哪里出了js错误,要马上定位、马上解决该怎么办呢?

前端杀器Fiddler

其实老夫也是最近才开始使用该软件的,其实老夫很多不懂,但是我就用其中一点东西就好了,只一眼便已爱如心扉!!!

先来个截图:

请注意矩形框位置,我就会用这个。。。。

大杀器在线编辑js/html/css

光说不练假把式!下面就让老夫试试这个大杀器~!

打开我们的博客园首页看看,然后观察我们的大杀器变化:

小样东西还不少呢!!!现在让我们来动点手脚了!!!

右键编辑之,出现:

算了,我想了下,还是不用博客园来测试了,用它估计老夫要丢人。。。因为首页可能用了页面片神马的,老夫没找到。。。

于是老夫又进了自己博客文章的页面,来看看有什么东东呢?

日本人,是拉的json数据啦!!!说实话ajax真是讨厌了,老夫又丢脸了,算了继续!!!

最后老夫找到了自己想要的东东,于是乎我随便加点文字,加点js后!!!

做这点变化保存后,刷新页面,非常奇迹的变了,我们用本地的文件替换了线上的请求!!!

这个东西是不得了的,我们根据这个特性完全可以做在线前端测试!!!

对js的操作

 对js的操作,我们换个地方,熟悉点的东西好操作一点,以免再出丑,我们来看大成网的首页:

 

我们将之存到桌面,晚上讲地址赋给fiddler:

 这样一保存,我们在本地的修改就直接同步到线上页面啦!!!!对小的来说,当真神器!!!

于是你可以说你在大成网自豪的弹了个框出来。。。。

结语

其实小的对Fiddler也不太熟,就只会这点点花招,并且还感觉不差呢!!!

它的出现主要有什么好处呢,好处就是我们可以在线测试前端的代码,修复前端的BUG,然后将修改后的文件上传即可;

这也许没什么意义,但当项目到一定规模,发布有一定门槛后,就变得非常有意义了!!!

[转载]jquery收藏本站——添加到浏览器的收藏夹 - 朱羽佳 - 博客园

mikel阅读(1380)

[转载]【jquery】收藏本站——添加到浏览器的收藏夹 – 朱羽佳 – 博客园.

基本上兼容了所有的浏览器,值得收藏。

直接上代码:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title>收藏本站</title>
</head>
<body>
    <a href="javascript:;" title="收藏本站" id="fav">收藏本站</a>
</body>
</html>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
//收藏本站代码
jQuery.fn.addFavorite = function(l, h) {
    return this.click(function() {
        var t = jQuery(this);
        if(jQuery.browser.msie) {
            window.external.addFavorite(h, l);
        } else if (jQuery.browser.mozilla || jQuery.browser.opera) {
            t.attr("rel", "sidebar");
            t.attr("title", l);
            t.attr("href", h);
        } else {
            alert("请使用Ctrl+D将本页加入收藏夹!");
        }
    });
};
$(function(){
    $('#fav').addFavorite('收藏本站',location.href);
});
</script>

ASP.NET MVC 动态路由解析Editable Routes

mikel阅读(1004)

.

UPDATE: 2011/02/13: This code is now included in the RouteMagic NuGet package! To use this code, simply run Install-Package RouteMagic within the NuGet Package Manager Console.

In general, once you deploy your ASP.NET MVC application, you can’t change the routes for your application without recompiling the application and redeploying the assembly where your routes are defined.

routesThis is partly by design as routes are generally considered application code, and should have associated unit tests to verify that the routes are correct. A misconfigured route could seriously tank your application.

Having said that, there are many situations in which the ability to change an application’s routes without having to recompile the application comes in very handy. This is the situation I find myself in as I build a blog engine where the folks who will install may want to tweak the routes without having to recompile the blog’s source code.

In this post, I’ll demonstrate an approach that’ll allow you to define your routes in a content file as code (no XML here!) which you deploy with your application as in the screenshot.

Routes File In Soultion

In my implementation, you need to place the routes in a Config folder in your web root. Note that I used Visual Studio’s Properties dialog to mark the file’s Build Action as “Content” so that it’s not compiled into my application.

Properties

What this means is that the code in the Routes.cs file is not compiled with the application. Instead, we will dynamically compile it. First, let’s look at the contents of that file. It shouldn’t be too surprising.

using System.Web.Mvc;
using System.Web.Routing;
using EditableRoutesWeb;

public class Routes : IRouteRegistrar
{
  public void RegisterRoutes(RouteCollection routes)
  {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
      "Default",
      "{controller}/{action}/{id}",
      new { controller = "Home", action = "Index", id = "" }
    );
  }
}

One thing you’ll notice is that this class implements an interface named IRouteRegistrar. This is an interface I created and added to my web application (though it could be defined in another assembly).

The code in Global.asax.cs for this application simply calls an extension method I wrote.

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RouteTable.Routes.RegisterRoutes("~/Config/Routes.cs");
}

It’s the code in this extension method where the real magic happens.

Before I show the code, there are two concepts at work here that make this work. The first is using the BuildManager to dynamically create an assembly from the Routes.cs file. From that assembly, we can create an instance of the type Routes and cast it to IRouteHandler.

var assembly = BuildManager.GetCompiledAssembly("~/Config/Routes.cs");
var registrar = assembly.CreateInstance("Routes") as IRouteRegistrar;

Once we have an instance of a route registrar, we can call RegisterRoutes on that instance.

The second concept is being able to get notification when the Routes.cs file changes. The clever trick that David told me about is using the ASP.NET Cache object to do that. When you add an item to the cache, you can give it a cache dependency pointing to a file and a method to call when the cache is invalidated.

With those two pieces, we can add a cache dependency pointing to Routes.cs and a callback method which will reload the routes when Routes.cs is changed.

Here’s the full listing for RouteRegistrationExtensions.

public static class RouteRegistrationExtensions
{
  public static void RegisterRoutes(this RouteCollection routes, 
      string virtualPath)
  {
    routes.ReloadRoutes(virtualPath);
    ConfigFileChangeNotifier.Listen(virtualPath, 
      vp => routes.ReloadRoutes(vp));
  }

  static void ReloadRoutes(this RouteCollection routes, string virtualPath)
  {
    var assembly = BuildManager.GetCompiledAssembly(virtualPath);
    var registrar = assembly.CreateInstance("Routes") as IRouteRegistrar;
    using(routes.GetWriteLock())
    {
      routes.Clear();
      registrar.RegisterRoutes(routes);
    }
  }
}

This uses a class called ConfigFileChangeNotifier which is based on some code David wrote for Dynamic Data.

public class ConfigFileChangeNotifier
{
  private ConfigFileChangeNotifier(Action<string> changeCallback)
    : this(HostingEnvironment.VirtualPathProvider, changeCallback)
  { 
  }

  private ConfigFileChangeNotifier(VirtualPathProvider vpp, 
      Action<string> changeCallback) {
    _vpp = vpp;
    _changeCallback = changeCallback;
  }

  VirtualPathProvider _vpp;
  Action<string> _changeCallback;

  // When the file at the given path changes, 
  // we'll call the supplied action.
  public static void Listen(string virtualPath, Action<string> action) {
    var notifier = new ConfigFileChangeNotifier(action);
    notifier.ListenForChanges(virtualPath);
  }

  void ListenForChanges(string virtualPath) {
    // Get a CacheDependency from the BuildProvider, 
    // so that we know anytime something changes
    var virtualPathDependencies = new List<string>();
    virtualPathDependencies.Add(virtualPath);
    CacheDependency cacheDependency = _vpp.GetCacheDependency(
      virtualPath, virtualPathDependencies, DateTime.UtcNow);
      HttpRuntime.Cache.Insert(virtualPath /*key*/,
        virtualPath /*value*/,
        cacheDependency,
        Cache.NoAbsoluteExpiration,
        Cache.NoSlidingExpiration,
        CacheItemPriority.NotRemovable,
        new CacheItemRemovedCallback(OnConfigFileChanged));
  }

  void OnConfigFileChanged(string key, object value, 
    CacheItemRemovedReason reason) {
    // We only care about dependency changes
    if (reason != CacheItemRemovedReason.DependencyChanged)
      return;

    _changeCallback(key);

    // Need to listen for the next change
    ListenForChanges(key);
  }
}

With this in place, you can now change routes within the Routes.cs file in the Config directory after you’ve deployed the application. Note that technically, a recompilation is happening, but it’s happening dynamically at runtime when the file changes and there’s no need to restart your entire App Domain, which is one benefit of this approach over using the code in App_Code.

If you want to try this code out, you can download a sample project here. The sample app is compiled against ASP.NET MVC 2 RC, but the same principles and code can be used with an ASP.NET MVC 1.0 application. In fact, it can also be used in an ASP.NET 4 Web Forms application since we now support page routing.

Note, if you want to see the old version of this code, I’ve archived it here.

[转载]JavasScript 实现Json查询方法 - HeartDawn - 博客园

mikel阅读(1109)

[转载]JS 实现Json查询方法 – HeartDawn – 博客园.

曾经看过一个大牛写的实现Json的一个模板类,今天突然没事就来自己试着写写。

还好,一些东西还记得,思路还算清晰。

在此感谢那位大牛,具体是哪个大牛,我也不知道了。因为担心自己以后会忘记这样的方式,所以在这里记录下来。

也就不直接废话了,直接上代码了,其实很简单,我这部分代码,前一部分是简单的实现如何使用JS写模板,第二个就是具体的实现了JSON查询的一个扩展。

以后查询Json就有了利器了。

View Code 

/*
         * 定义模板函数
        */
        var template = function (queryArr) {

            var count = 0;

            for (var i = 0; i < queryArr.length; i++) {

                var e = queryArr[i];

                if ($express) {
                    count++;
                }
            }

            return count;
        }

        /*
         * 模板创建函数
        */
        var createIntance = function (exp) {
            var fun = template.toString().replace("$express", exp).toString();
            return eval("0," + fun);
        }

        var testTodo = function () {

            var testArr = [
                { name: "张三", age: 20 },
                { name: "李四", age: 25 },
                { name: "王二麻子", age: 28 },
                { name: "小张", age: 30 }
            ];

            var func = createIntance("e.age>=25");

            alert(func(testArr));
        }

        /****************** JS 实现 JSON查询 **********************/
        
        // 定义常用的函数
        var len = function (s) { return s.length; }
        var left = function (s, n) { return s.substr(0, n); }
        var right = function (s, n) { return s.substr(-n); }
        var index = function (s, find) { return s.indexOf(find) + 1; }

        // 扩展原型方法
        var _proto = Object.prototype;

        // 缓存,解决快速查找
        var _cache = {};

        // 扩展运算符
        var _alias = [
            /@/g, "_e.",
            /AND/gi, "&&",
            /OR/gi, "||",
            /<>/g, "!=",
            /NOT/gi, "!",
            /([^=<>])=([^=]|$)/g, '$1==$2'
        ];

        var _rQuote = /""/g;
        var _rQuoteTemp = /!~/g;

        // 编译
        var _complite = function (code) {
            return eval("0," + code);
        }

        // 将扩展符号转换成标准的JS符号
        var _interpret = function (exp) {
            exp = exp.replace(_rQuote,"!~");
            var arr = exp.split('"');
            var i, n = arr.length;
            var k = _alias.length;

            for (var i = 0; i < n; i += 2) {
                var s = arr[i];
                for (var j = 0; j < k; j += 2) {
                    if (index(s, _alias[j]) > -1) {
                        s = s.replace(_alias[j], _alias[j + 1]);
                    }
                }
                arr[i] = s;
            }

            for (var i = 1; i < n; i += 2) {
                arr[i] = arr[i].replace(_rQuoteTemp, '\\"');
            }
            return arr.join('"');
        }

        // 定义模函数
        var _templ = function (_list) {
            var _ret = [];
            var _i = -1;

            for (var _k in _list) {
                var _e = _list[_k];
                if (_e != _proto[_k]) {
                    if ($C) {
                        _ret[++_i] = _e;
                    }
                }
            }
            return _ret;
        } .toString();

        // 扩展查询的方法
        _proto.Query = function (exp) {
            if (!exp) {
                return [];
            }

            var fn = _cache[exp];

            try {
                if (!fn) {
                    var code = _interpret(exp);
                    code = _templ.replace("$C", code);
                    fn = _cache[exp] = _complite(code);
                }
                return fn(this);
            } catch (e) {
                return [];
            }
        }

        var doTest = function () {

            var heros = [
            // 名============攻=====防=======力量====敏捷=====智力====
                {name: '冰室女巫', DP: 38, AP: 1.3, Str: 16, Dex: 16, Int: 21 },
                { name: '沉默术士', DP: 39, AP: 1.1, Str: 17, Dex: 16, Int: 21 },
                { name: '娜迦海妖', DP: 51, AP: 6.0, Str: 21, Dex: 21, Int: 18 },
                { name: '赏金猎人', DP: 39, AP: 4.0, Str: 17, Dex: 21, Int: 16 },
                { name: '剧毒术士', DP: 45, AP: 3.1, Str: 18, Dex: 22, Int: 15 },
                { name: '光之守卫', DP: 38, AP: 1.1, Str: 16, Dex: 15, Int: 22 },
                { name: '炼金术士', DP: 49, AP: 0.6, Str: 25, Dex: 11, Int: 25 }
            //...
            ];

            var match = heros.Query('@Str>20 AND @Dex>20');
            ShowResult(match[0]);
            
            // 查询:“士”结尾的
            // 结果:沉默术士,剧毒术士,炼金术士
            var match = heros.Query('right(@name,1)="士"');
            ShowResult(match[0]);
        }

        function ShowResult(result) {
            alert(result.name + " " + result.DP + " " + result.AP + " " + result.Str + " " + result.Dex + " " + result.Int);
        }

[转载]分享下.NET程序读取二代身份证(附源码 - 每天保持微笑 - 博客园

mikel阅读(1345)

转载分享下.NET程序读取二代身份证(附源码 – 每天保持微笑 – 博客园.

一般来说winform应用程序解决这个问题起来时很容易的,web应用程序就麻烦一点了。

这里我说说我的解决思路:

一、你必要有联机型居民身份证阅读器一个(带驱动光盘),这里我用的是精伦电子公司的iDR200,并有这个阅读器的开发接口说明。

二、新建一个winform控件项目ReadCardControl,添加一个主类ReadCard

1、  声明dll入口

      [DllImport(“Sdtapi.dll”)]

        private static extern int InitComm(int iPort);//初始化

        [DllImport(“Sdtapi.dll”)]

        private static extern int Authenticate();//卡认证

        [DllImport(“Sdtapi.dll”)]

        private static extern int ReadBaseInfos(StringBuilder Name, StringBuilder Gender, StringBuilder Folk, StringBuilder BirthDay, StringBuilder Code, StringBuilder Address, StringBuilder Agency, StringBuilder ExpireStart, StringBuilder ExpireEnd);//读取数据,推荐使用

        [DllImport(“Sdtapi.dll”)]

        private static extern int CloseComm();//关闭端口

       [DllImport(“Sdtapi.dll”)]

        private static extern int ReadBaseMsg(byte[] pMsg, ref int len);//读取数据,这里不用

        [DllImport(“Sdtapi.dll”)]

        private static extern int ReadBaseMsgW(byte[] pMsg, ref int len);//读取数据,这里不用

        [DllImport(“kernel32.dll”)]

        private static extern int Beep(int dwFreq, int dwDuration);//用来大吼一声

2、读卡方法

        private string[] arrys = null;//声明用来保存身份证信息的数组

        public void OnTimer()

        {

            StringBuilder Name = new StringBuilder(31);

           StringBuilder Gender = new StringBuilder(3);

            StringBuilder Folk = new StringBuilder(10);

            StringBuilder BirthDay = new StringBuilder(9);

            StringBuilder Code = new StringBuilder(19);

            StringBuilder Address = new StringBuilder(71);

            StringBuilder Agency = new StringBuilder(31);

            StringBuilder ExpireStart = new StringBuilder(9);

            StringBuilder ExpireEnd = new StringBuilder(9);

            //int len = 0;

            //string[] temp;

            char[] param = { ‘\0’ };

            byte[] pMsg = new byte[256];

            string[] baseinfo = new string[9];

            //打开端口

            int intOpenRet = InitComm(1001);

            if (intOpenRet != 1)

            {

                //SetText(“阅读机具未连接”, lblMsg);

                return;

            }

            //卡认证

            int intReadRet = Authenticate();

            if (intReadRet != 1)

            {

                //SetText(“卡认证失败”, lblMsg);

                CloseComm();

                return;

            }

            //ReadBaseInfos(推荐使用)

            int intReadBaseInfosRet = ReadBaseInfos(Name, Gender, Folk, BirthDay, Code, Address, Agency, ExpireStart, ExpireEnd);

            if (intReadBaseInfosRet != 1)

            {

                //SetText(“读卡失败”, lblMsg);

                CloseComm();

                return;

            }

            Beep(2047, 200);

            arrys = new string[10];

            arrys[0] = Code.ToString().Trim();

            arrys[1] = Name.ToString().Trim();

            arrys[2] = Gender.ToString().Trim();

            arrys[3] = Folk.ToString().Trim();

            arrys[4] = BirthDay.ToString().Trim();

            arrys[5] = Address.ToString().Trim();

            arrys[6] = Agency.ToString().Trim();

            arrys[7] = ExpireStart.ToString().Trim();

            arrys[8] = ExpireEnd.ToString().Trim();

            arrys[9] = System.IO.Directory.GetCurrentDirectory() + “\\photo.bmp”;

            //SetText(“读卡成功”, lblMsg);

            //SetText(“证件号码:” + Code.ToString(), label1);

            //SetText(“姓名:” + Name.ToString(), label2);

            //SetText(“性别:” + Gender.ToString(), label3);

            //SetText(“民族:” + Folk.ToString(), label4);

            //SetText(“出生日期:” + BirthDay.ToString(), label5);

            //SetText(“地址:” + Address.ToString(), label6);

            //SetText(“签发机关:” + Agency.ToString(), label7);

            //SetText(“签发时间:” + ExpireStart.ToString(), label8);

            //SetText(“有效截止时间:” + ExpireEnd.ToString(), label9);

            SetImage(“photo.bmp”, pictureBox1);

            CloseComm();

        }

2、  声明一个客户端调用的方法Test(这里没有直接用数组)

        public string Test()

        {

            string str = “”;

            foreach (string s in arrys)

            {

                str += s + “,”;

            }

            str = str.TrimEnd(‘,’);

            return str;

}

三、将这个控件封装成activex组件

在ReadCard类的头部添加

[Guid(“B9BB196C-5008-4156-840F-9FE37BA81502”)]

若要实现客户端访问服务器端数据,则必须实现IObjectSafe接口

接口声明如下:

   [ComImport, GuidAttribute(“CB5BDC81-93C1-11CF-8F20-00805F2CD064”)]

    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IObjectSafe

    {

        [PreserveSig]

        void GetInterfaceSafeOptions(ref Guid id, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);

        [PreserveSig()]

        void SetInterfaceSafeOptions(ref Guid id, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);

}

四、新建一个web应用程序项目

1、界面:

               <table style=”width:100%;” border=”1″ width=”100%” bordercolordark=”#FFFFFF” cellspacing=”0″ cellpadding=”2″ bordercolor=”#000000″>

            <tr>

                <td style=”width:33.3%; text-align: right;”>证件号码:</td>

                <td style=”width:33.3%” id=”td1″>&nbsp;</td>

                <td style=”width:33.3%; text-align: left;” rowspan=”6″>

                         <object id=”csharpActiveX” name=”csharpActiveX” classid=”clsid:B9BB196C-5008-4156-840F-9FE37BA81502″></object></td>

            </tr>

            <tr>

                <td style=”text-align: right”>姓名:</td>

                <td id=”td2″>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>性别:</td>

                <td id=”td3″>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>民族:</td>

                <td id=”td4″>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>出生日期:</td>

                <td id=”td5″>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>地址:</td>

                <td id=”td6″>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>签发机关:</td>

                <td id=”td7″>&nbsp;</td>

                <td>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>签发时间:</td>

                <td id=”td8″>&nbsp;</td>

                <td>&nbsp;</td>

            </tr>

            <tr>

                <td style=”text-align: right”>有效截止时间:</td>

                <td id=”td9″>&nbsp;</td>

                <td>&nbsp;</td>

            </tr>

        </table>

 

2、JavaScript代码:

      function Demo() {

            try {

                var x = document.getElementById(“csharpActiveX”);

                x.OnTimer();//读取图片

                var v = x.Test();//读取身份证其他信息

                var arrys = v.split(‘,’);

                if (arrys.length > 9) {

                    document.getElementById(“td1”).innerHTML = arrys[0];

                    document.getElementById(“td2”).innerHTML = arrys[1];

                    document.getElementById(“td3”).innerHTML = arrys[2];

                    document.getElementById(“td4”).innerHTML = arrys[3];

                    document.getElementById(“td5”).innerHTML = arrys[4];

                    document.getElementById(“td6”).innerHTML = arrys[5];

                    document.getElementById(“td7”).innerHTML = arrys[6];

                    document.getElementById(“td8”).innerHTML = arrys[7];

                    document.getElementById(“td9”).innerHTML = arrys[8];

                }

            }

            catch (e) {

                //alert(e.message)

            }

        }

        setInterval(Demo, 1000);//设置每隔1秒钟读取一次

大功告成。。。下载源码

截个效果图:

每天进步一点点…

[转载]OSGI:从面向接口编程来理解OSGI - 幸福框架 - 博客园

mikel阅读(1075)

[转载]OSGI:从面向接口编程来理解OSGI – 幸福框架 – 博客园.

接口的种类(API和SPI)

 

从接口的被调用方式和被实现方式看,接口有API和SPI之分,见下图:

 

 

API和SPI在物理组织方式上的建议(可根据情况选择其一)

 

    • 位于独立的Assembly中。
    • 位于调用方的Assembly中。

 

API和SPI的演化方式:

 

    • API可以增加功能,最好保持稳定。
    • SPI可以减少功能,最好保持稳定。

 

API和SPI的交互方式见下图:

 

 

如何实例化接口(避免不了的问题)

 

  • 简单工厂(三种工厂模式都引入了新的抽象,因此最终还是要用简单工厂创建抽象的。适用于根据上下文实例化不同实例的场景)。
  • 服务定位器(适用于实例化边界对象或根对象的场景)。
  • 依赖注入容器(适用于多数场景,推荐用这种方式)。

 

从面向接口编程的角度考虑,为什么采用OSGI?

刚才我们介绍了接口和如何实例化接口,OSGI只是更进一步,允许你动态的管理具体实现Assembly的生命周期。多数应用可能不需要这种动态, 但是如果您发现OSGI对程序的侵入性不大(它唯一的要求就是面向接口编程),而您已经早就习惯了面向接口编程,那么OSGI对于您来说应该不是大问题

[转载]使用StructureMap扩展ASP.NET MVC三层结构框架系列文章总结篇(附源码下载) - 天屹 - 博客园

mikel阅读(1007)

[转载]使用StructureMap扩展ASP.NET MVC三层结构框架系列文章总结篇(附源码下载) – 天屹 – 博客园.

本系统提供一个对默认的ASP.NET MVC扩展的三层结构框架,使用了StructureMapEnterprise Library进行依赖注入和异常日志记录处理,面向接口编程在本系统得到了充分的体现。

天屹不推荐直接下载代码,进行Copy and Paste,我们不是Coder而是Developer。希望这个代码可以作为你学习这个框架时的一个参考,而不是拿去就用。系统源码下载:下载地址

下面是对本系统各个模块实现的详细文章:

  1. 扩展ASP.NET MVC三层框架并使用StructureMap实现依赖注入1-Model层的实现
  2. 扩展ASP.NET MVC三层框架并使用StructureMap实现依赖注入2-Repository层的实现
  3. 扩展ASP.NET MVC三层框架并使用StructureMap实现依赖注入3-Service层的实现
  4. 扩展ASP.NET MVC三层框架并使用StructureMap实现依赖注入4-Controller和View的实现
  5. 扩展ASP.NET MVC三层框架并使用StructureMap实现依赖注入5-StructureMap的配置与实现
  6. 扩展ASP.NET MVC三层框架并使用Enterprise Library实现异常与日志记录(6)

本系统基于ASP.NET MVC4,使用了StructureMap实现依赖注入,Enterprise Library实现异常和日志的处理。相应的第三方DLL放在了Commons工程中,数据库使用开源Northwind数据库,Logging和 Northwind数据库备份也放到了Commons工程中。

下面看一下最终我们这个框架的完整结构:

mvc-structuremap-enterprise-library

9个工程功能介绍

  1. TYStudioDemo.Commons工程重要放一些公共的类,和一些工具方法。
  2. TYStudioDemo.DTO工程,主要放我们ViewModel,分离View和Entity Framework,降低代码耦合度。
  3. TYStudioDemo.Interfaces工程,统一管理整个框架的接口,所有的接口都在这一工程。
  4. TYStudioDemo.Membership工程,是我们的权限管理模块,可以参考天屹的MVC4 Simplemembership些列文章。
  5. TYStudioDemo.Models工程,这里没有其他的只有Entity Framework,Models层。
  6. TYStudioDemo.Repositories工程,如名字这里放置所有Repository,数据库持久层,只有这一层去和Model层打交道。
  7. TYStudioDemo.Services工程,业务逻辑层所有的业务逻辑都放在这一工程,通过StructureMap实现对Repository的注入,同时异常处理Service也在这里一层,使用Enterprise Library进行异常的日志记录。
  8. TYStudioDemo.StructureMap工程,这里放置的StructureMap的配置与管理。
  9. TYStudioDemo.WebUI工程,UI层,Controller和View都在这一层。

一些说明:

因为是Demo,所以没有写成一个完整的系统,只对Supplier一个表进行了操作,当然考虑到事务的问题,天屹在SupplierService的Create方法中模拟添加了Product表的内容,对本系统中的事务处理做了注释说明。

如果觉得系统还可以,请大家推荐分享一下,让更多的学习MVC的朋友看到,谢谢。

如果有什么问题,不明白的地方,欢迎来到TYStudio,留下你的问题,我们一起解决他们。原创文章,未经作者允许不可转载。

[转载]ASP.NET的路由系统:路由映射 - Artech - 博客园

mikel阅读(920)

[转载]ASP.NET的路由系统:路由映射 – Artech – 博客园.

总的来说,我们可以通过RouteTable的静态属性Routes得到一个基于应用的 全局路由表,通过上面的介绍我们知道这是一个类型的RouteCollection的集合对象,我们可以通过调用它的MapPageRoute进行路由映 射,即注册URL模板与某个物理文件的匹配关系。路由注册的核心就是在全局路由表中添加一个Route对象,该对象的绝大部分属性都可以通过 MapPageRoute方法的相关参数来指定。接下来我们通过实现演示的方式来说明路由注册的一些细节问题。

目录
一、变量默认值
二、约束
三、对现成文件的路由
四、注册路由忽略地址
五、直接添加路由对象

我 们已前面介绍的关于获取天气预报信息的路由地址,我们在创建的ASP.NET Web应用中创建一个Weather.aspx页面,不过我们并不打算在该页面中呈现任何天气信息,而是将基于该页面的路由信息打印出来。该页面主体部分 的HTML如下所示,我们不仅将基于当前页面的RouteData对象的Route和RouteHandler属性类型输出来,还将存储于Values和 DataTokens字典的变量显示出来。

 1: <body>
 2:     <form id="form1" runat="server">
 3:     <div>
 4:         <table>
 5:             <tr>
 6:                 <td>Route:</td>
 7:                 <td><%=RouteData.Route != null? RouteData.Route.GetType().FullName:"" %></td>
 8:             </tr>
 9:             <tr>
 10:                 <td>RouteHandler:</td>
 11:                 <td><%=RouteData.RouteHandler != null? RouteData.RouteHandler.GetType().FullName:"" %></td>
 12:             </tr>
 13:             <tr>
 14:                 <td>Values:</td>
 15:                 <td>
 16:                     <ul>
 17:                         <%foreach (var variable in RouteData.Values)
 18:                           {%>
 19:                         <li><%=variable.Key%>=<%=variable.Value%></li>
 20:                         <% }%>
 21:                     </ul>
 22:                 </td>
 23:             </tr>
 24:             <tr>
 25:                 <td>DataTokens:</td>
 26:                 <td>
 27:                     <ul>
 28:                         <%foreach (var variable in RouteData.DataTokens)
 29:                           {%>
 30:                         <li><%=variable.Key%>=<%=variable.Value%></li>
 31:                         <% }%>
 32:                     </ul>
 33:                 </td>
 34:             </tr>
 35:         </table>
 36:     </div>
 37:     </form>
 38: </body>

在添加的Global.asax文件中,我们将路由注册操作定义在Application_Start方法中。如下面的代码片断所示,映射到weather.aspx页面的URL模板为{areacode}/{days}。在调用MapPageRoute方法的时候,我们还为定义在URL模板的两个变量定义了默认值以及正则表达式。除此之外,我们还在注册的路由对象上附加了两个变量,表示对变量默认值的说明(defaultCity:BeiJing;defaultDays:2)。

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
 6:         var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
 7:         var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
 8:
 9:         RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
 10:     }
 11: }

一、变量默认值

由于我们为定义在URL模板中表示区号和天数的变量定义了默认值(areacode:010;days:2),如果我们希望返回北京地区未来两天的 天气,可以直接访问应用根地址,也可以只指定具体区号,或者同时指定区号和天数。如下图所示,当我们在浏览器地址栏中输入上述三种不同的URL会得到相同 的输出结果。

从下图所示的路由信息我们可以看到,默认情况下RouteData的Route属性类型正是Route,而RouteHandler属性则一个是PageRouteHandler对象,我们会在本章后续部分对PageRouteHandler进行详细介绍。通过地址解析出来的变量被存储数Values属性中,而在进行路由注册过程为Route对象DataTokens属性定义的变量被转移到了RouteData的同名属性中。[实例源代码下载]

clip_image002

二、约束

我们以电话区号代表对应的城市,为了确保用户在的请求地址中提供有效的区号,我们通过正则表达式(“0\d{2,3}”)对其进行了约束。此外,我们只能提供未来3天以内的天气情况,我们同样通过正则表达式(“[1-3]{1}”)是对请求地址中表示天数的变量进行了约束。如果请求地址中的内容不能符合相关变量段的约束条件,则意味着对应的路由对象与之不匹配。

对于本例来说,由于我们只注册了唯一的路由对象,如果请求地址不能满足我们定义的约束条件,则意味着找不到一个具体目标文件,会返回404错误。如下图所示,由于在请求地址中指定了不合法的区号(01)和天数(4),我们直接在浏览器界面上得到一个HTTP 404错误。

clip_image004

对于约束,除了可以通过字符串的形式为某个变量定义相应的正则表达式之外,我们还可以指定一个实现了IRouteConstraint接 口的类型的对象对整个请求进行约束。如下面的代码片断所示,IRouteConstraint具有唯一的方法Match用于定义约束的逻辑,该方法的5个 参数分别表示:HTTP上下文、当前路由对象、约束的名称(存储约束对象的RouteValueDictionary的Key)、解析被匹配URL得到的 变量集合以及表示路由的方向。

 1: public interface IRouteConstraint
 2: {
 3:     bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
 4: }
 5: public enum RouteDirection
 6: {
 7:     IncomingRequest,
 8:     UrlGeneration
 9: }

所谓路由的方向表示是针对请求匹配(入栈)还是针对URL的生成(出栈),分别通过如上所示的枚举类型RouteDirection的两个枚举值表示。具体来说,当调用路由对象的GetRouteData和GetVirtualPathData方法时,枚举值IncomingRequest和UrlGeneration分别被采用。

ASP.NET路由系统的应用编程接口中定义了如下一个实现了IRouteConstraint接口的HttpMethodConstraint类 型。顾名思义,HttpMethodConstraint提供针对HTTP方法(GET、POST、PUT、DELTE等)的约束。我们可以通过 HttpMethodConstraint为路由对象设置一个允许的HTTP方法列表,只有方法名称在这个指定的列表中的HTTP请求才允许被路由。这个 被允许被路由的HTTP方法列表对于HttpMethodConstraint的只读属性AllowedMethods,并在构造函数中初始化。

 1: public class HttpMethodConstraint : IRouteConstraint
 2: {
 3:     public HttpMethodConstraint(params string[] allowedMethods);
 4:     bool IRouteConstraint.Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
 5:     public ICollection<string> AllowedMethods {  get;  }
 6: }

同样是针对我们演示的例子,我们在进行路由注册的时候通过如下的代表应用了一个类型为HttpMethodConstraint的约束,并将允许的HTTP方法设置为POST,意味着被注册的Route对象仅限于路由POST请求。

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } };
 6:         var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" }, { "httpMethod", new HttpMethodConstraint("POST") } };
 7:         var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
 8:
 9:         RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
 10:     }
 11: }

现在我们采用与注册的URL模板相匹配的地址(/010/2)来访问Weather.aspx页面,依然会得到如下图所示的404错误。[实例源代码下载]

clip_image006

三、对现有文件的路由

在成功注册路由的情况下,如果我们按照传统的方式访问一个物理文件(比如.asxp、.css或者.js等),在请求地址满足某个路由的URL模板 模式的情况下,ASP.NET是否还是正常实施路由呢?我们不妨通过我们的实例还测试一下。为了让针对某个物理文件的访问地址也满足注册路由对象的URL 模板模式,我们需要按照如下的方式将上面定义的关于正则表达式约束删除。

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
 6:         //var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
 7:         var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
 8:
 9:         RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, null, dataTokens);
 10:     }
 11: }

当我们通过传统的方式来访问存放于根目录下的weather.aspx页面时会得到如下图所示的结果。从界面上的输出结果我们不难看出,虽然请求地 址完全满足我们注册路由对象的URL模板模式,但是ASP.NET并没有对请求地址实施路由。原因很简单,如果中间发生了路由,基于页面的 RouteData的各项属性都不可能为空。[实例源代码下载]

clip_image008

那么是否意味着如果请求地址对应着一个现存的物理文件,ASP.NET就会自动忽略路由呢?实则不然,或者说不对现有文件实施路由仅仅默认采用的行 为。是否对现有文件实施路由取决于代表全局路由表的RouteCollection对象的RouteExistingFiles属性,该属性默认情况下为 False,我们可以将此属性设置为True使ASP.NET路由系统忽略现有物理文件的存在,总是按照注册的路由表进行路由。为了演示这种情况下,我们 对Global.asax文件作了如下的改动,在进行路由注册之前将RouteTable的Routes属性代表的RouteCollection对象的 RouteExistingFiles属性设置为True。

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         RouteTable.Routes.RouteExistingFiles = true;
 6:         var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 } };
 7:         var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
 8:
 9:          RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, null, dataTokens);
 10:     }
 11: }

依旧是针对weather.aspx页面的访问,我们却得到不一样的结果。从下图中我们可以看到,针对页面的相对地址weather.aspx不再指向具体的Web页面,在这里就是一个表示获取的天气信息对应的目标城市(areacode=weather.aspx)。[实例源代码下载]

clip_image010

四、注册路由忽略地址

如果将代表全局路由表的RouteTable的静态属性Routes的RouteExistingFiles属性设置为True,意味着 ASP.NET针对所有抵达的请求都一视同仁,都按照注册的路由表进行注册,但这会代码一些问题。不知道读者有没有发现上图所示的页面具有不一样的格式 (标签部分没有加粗,也没有居右上对齐),这是因为这是采用了JQuery的方式来控制的,为此我们必须按照如下的方式来饮用JQuery相关的脚本文 件。

 1: <script src="/jquery-1.4.1.min.js" type="text/javascript"></script>

但是由于我们将全局路由表的RouteExistingFiles属性设置为True,意味着针对上面这个.js脚本文件的访问也会被路由。根据我 们注册的路由规则,针对这个文件的访问会自动被导向weather.aspx这个页面。如下图所示,我们直接在浏览器的地址栏中属性.js文件的地址,呈 现出来还是我们熟悉的界面(areacode=JQuery-1.4.1.min.js)。[实例源代码下载]

clip_image012

这是一个不得不解决的问题,因为它是我们无法正常地在页面中引用向JavaScript和css文件。我们可以通过调用 RouteCollection的Igore方法来注册一些需要让路由系统忽略的URL模板。从前面给出的关于RouteCollection的定义我们 可以看到它具有两个Igore重载,除了指定需要忽略的URL模板之外,我们还可以对相关的变量定义约束正则表达式。为了让ASP.NET路由系统忽略掉 针对.js文件请求,我们可以按照如下的方式在Global.asax中调用RouteTable的Routes属性的Ignore方法。[实例源代码下载]

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         RouteTable.Routes.RouteExistingFiles = true;
 6:         RouteTable.Routes.Ignore("{filename}.js/{*pathInfo}");
 7:         //其他操作
 8:     }
 9: }

五、直接添加路由对象

当我们调用RouteCollection对象的MapPageRoute方法进行路由注册的本质就在路由字典中添加Route对象,所以我们完全 调用Add方法添加一个手工创建的Route对象,如下所示的两种路由注册方式是完全等效的。如果我们需要添加一个继承自RouteBase的自定义路由 对象,我们不得不采用手工添加的方式。

 1: public class Global : System.Web.HttpApplication
 2: {
 3:     protected void Application_Start(object sender, EventArgs e)
 4:     {
 5:         var defaults = new RouteValueDictionary { { "areacode", "010" }, { "days", 2 }};
 6:         var constaints = new RouteValueDictionary { { "areacode", @"0\d{2,3}" }, { "days", @"[1-3]{1}" } };
 7:         var dataTokens = new RouteValueDictionary { { "defaultCity", "BeiJing" }, { "defaultDays", 2 } };
 8:
 9:         //路由注册方式1
 10:         RouteTable.Routes.MapPageRoute("default", "{areacode}/{days}", "~/weather.aspx", false, defaults, constaints, dataTokens);
 11:
 12:         //路由注册方式2
 13:         Route route = new Route("{areacode}/{days}", defaults, constaints, dataTokens, new PageRouteHandler("~/weather.aspx", false));
 14:         RouteTable.Routes.Add("default", route);
 15:     }
 16: }

 

ASP.NET的路由系统:URL与物理文件的分离
ASP.NET的路由系统:路由映射
ASP.NET的路由系统:根据路由规则生成URL