在ASP.NET MVC中使用IIS级别的URL Rewrite

原文 在ASP.NET MVC中使用IIS级别的URL Rewrite

大约一年半前,我在博客上写过一系列关于URL Rewrite的文章(2、3、4),把ASP.NET平台上进行URL Rewrit的方式和各自地特点进行了较为详细的描述。应该来说,已经讲的非常具体,可以应对90%的情况。其实IIS Rewrite的原理非常容易理解,进行一些简单的变化和推断之后,便可以得出一些问题的原因和解决方案。现在我们就来看一个真实案例:在ASP.NET MVC中使用IIS级别的URL Rewrite。

在当时的文章中我谈到,URL Rewrite分有IIS级别和ASP.NET两种级别,并且各有各的特点和限制。在ASP.NET MVC中我们常用的方式是ASP.NET级别的URL Routing,它的作用是从URL中捕获数据并交给程序使用(当然还有“构造”的功能,稍候再谈)。因此,在ASP.NET MVC中我们往往不需要使用ASP.NET级别的URL Rewrite。而如今使用IIS级别的URL Rewrite,也正是因为有某些特殊问题无法回避才“不得已而为之”的。

以下涉及到的URL都以http://51programming.com为例,这个域名已经被我泛解析为127.0.0.1,如果您需要的话可以用它来做实验。

在许多年前,一个URL的Path就是普通的路径,而动态的参数,如查询路径,是通过Query String提供的,例如:

http://51programming.com/products?keywords=helloworld

为了避免混淆,在这里我们先来澄清一些概念。什么是URL,什么是Path,而什么是QueryString。例如在上面的地址,这三者分别是:

  • URL:http://51programming.com/products?keywords=helloworld
  • Path:http://51programming.com/products
  • Query String:keywords=helloworld

后来SEO兴起之后,有人说这样的“动态地址”不利于搜索引擎中的权重优化,因此建议把关键字作为Path的一部分。于是就出现了这样的URL:

http://51programming.com/products/helloworld

这么看来问题并不大,但是您要知道,关键字往往是由用户输入的,可能会输入特殊字符。例如,如果用户输入了“200%”作为关键字,则两种形式下的URL就分别是:

http://51programming.com/products?keywords=200%25
http://51programming.com/products/200%25

如果您尝试一下便可以知道,第一个URL可以正常访问,而第二个URL便会引发Bad Request异常:

这是因为URL的Path部分出现了特殊字符,而这种字符只能出现在Query String中。

看到这个画面,您还意识到了什么信息?在定位问题的原因,以及设法解决问题的时候,首先要明确的是到底是哪里出现了问题。例如看到这个画面,您应该清楚地意识到一点:这是ASP.NET抛出的异常,换句话说,IIS并没有把它当作是非法的URL,它还是老老实实地将URL交给ASP.NET ISAPI处理。因此,我们便可以动用IIS级别的URL Rewrite,在进入ASP.NET执行引擎之前,就把URL替换成可接受的形式:

RewriteRule  ^/products/([^\?]*)\?(.+)    /products?$2&keywords=$1     [I,L,U]
RewriteRule  ^/products/([^\?]*)          /products?keywords=$1     [I,L,U]

第一行应对的是带有Query String的情况,而第二行则是没有Query String的情况。这里用到的组件是IIRF(Ionic's Isapi Rewrite Filter),这是一款开源产品,一年半前的文章里我推荐的也是这个,现在它已经有了升级。它的功能便是在进入ASP.NET ISAPI之前,就将URL重写为其他形式:

原本在第3步会出现的Bad Request,由于已经在第2步被URL Rewrite成合法的形式。因此剩余的处理也就没有任何问题了。

这些内容在一年半前的文章内已经提过,不过现在既然有了ASP.NET MVC,则事情又变得更为复杂。因为ASP.NET Routing除了“匹配”URL的功能之外,还担负着“组装”URL的职责。因此,让ASP.NET Routing能够识别出Rewrite后的URL不难,但是如何同时让它又可以“组装”出Rewrite前的URL,这就需要一些小技巧了。例如以下的Route配置只能识别出URL输入(/products?keywords=xxx)但不能组装出我们需要的URL(/products/xxx):

routes.MapRoute("Product.List","products",new { controller = "Product", action = "List" });

因此,我们必须这么做:

routes.MapRoute("Product.List","products/{*keywords}",new { controller = "Product", action = "List", keywords = "" });

请注意我们让keywords匹配Path后端全部内容,而由于我们又提供了keywords的默认值,因此即使是“/products”这样的Path输入,也能正确匹配到这条Route规则——只不过此时的Route Value中的keywords字段已经不是用户输入的内容了(因为用户输入的/products/xxx,已经被重写为/products?keywords=xxx)。换句话说,如果有如下的Action,那么它的keywords参数则永远是空字符串:

public ActionResult List(string keywords) { ... }

幸好,ASP.NET MVC中存在Model Binder机制,我们可以编写一个Model Binder来指定这个参数的获取位置:

public class FromQueryBinder : IModelBinder
{public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext){return controllerContext.HttpContext.Request.QueryString[bindingContext.ModelName];}
}

再将其运用到List的keywords参数上去:

public ActionResult List([ModelBinder(typeof(FromQueryBinder))]string keywords)

由于参数名是keywords,因此bindingContext.ModelName也是keywords,于是从Query String中便可以取到我们需要的内容了。至于在进行URL生成的时候,我们还是可以之间一样添加一个keywords字段到Route Value中去,于是在我们先前配置的Route规则中便会组装成合适的Path了(即/products/xxx)。

在这个例子中,我们让keywords匹配Path后端全部内容,但是如果是Path中间某一段需要有特殊字符怎么办呢?其实也一样,只是在进行URL Rewrite的时候,需要在最终重写的时候填写一个“假”的值就可以了,如这样的Route规则:

routes.MapRoute("Product.List","products/{keywords}/page",new { controller = "Product", action = "List" });

而IIS级别的URL Rewrite重写的规则就可以是:

RewriteRule  ^/products/([^/]*)/(.*)     /products/useless-segement/$2?keywords=$1     [I,L,U]

这样,如果用户输入/products/xxx/2就会被重写成/products/useless-token/2?keywords=xxx——事实上,在第一个示例中我们也可以这么做,只是我“不习惯”增加一个伪造的值而已。

以上解决方案可以在IIS 6与IIS 7的Classic Mode中正常使用,只可惜在IIS 7的Intergrated Mode中,可能是由于ASP.NET接管了IIS的部分逻辑,因此会很早抛出“IIS级别”,而不是“ASP.NET级别”的Bad Request异常。如果您遇到了这种方式,就必须通过以下三个步骤来摆脱这个麻烦的问题了:

  • 设置AllowRestrictedChars:KB820129(让IIS 7接受特殊字符)
  • 设置VerificationCompatibility:KB826437中除了“安装.NET 1.1 SP1”以外的步骤(让ASP.NET接受特殊字符)
  • 将ASP.NET页面的ValidateRequest设为False

其实您只要经过了这三步修改,对于目前这个案例,即使不用IIS级别的URL Rewrite应该也没有问题了。

posted on 2014-06-17 14:34 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/3792744.html

在ASP.NET MVC中使用IIS级别的URL Rewrite相关推荐

  1. 获取ASP.NET MVC中的完整操作URL [重复]

    本文翻译自:Getting full URL of action in ASP.NET MVC [duplicate] This question already has an answer here ...

  2. 通过源代码研究ASP.NET MVC中的Controller和View(三)

    通过源代码研究ASP.NET MVC中的Controller和View(一) 通过源代码研究ASP.NET MVC中的Controller和View(二) 第三篇来了,上一篇我已经把VirtualPa ...

  3. 通过源代码研究ASP.NET MVC中的Controller和View(二)

    通过源代码研究ASP.NET MVC中的Controller和View(一) 在开始之前,先来温习下上一篇文章中的结论(推论): IView是所有HTML视图的抽象 ActionResult是Cont ...

  4. 在Asp.Net MVC中实现RequiredIf标签对Model中的属性进行验证

    在Asp.Net MVC中可以用继承ValidationAttribute的方式,自定制实现RequiredIf标签对Model中的属性进行验证 具体场景为:某一属性是否允许为null的验证,要根据另 ...

  5. ASP.NET MVC中你必须知道的13个扩展点

         ScottGu在其最新的博文中推荐了Simone Chiaretta的文章13 ASP.NET MVC extensibility points you have to know,该文章为我 ...

  6. Asp.net mvc中的Ajax处理

    在Asp.net MVC中的使用Ajax, 可以使用通用的Jquery提供的ajax方法,也可以使用MVC中的AjaxHelper. 这篇文章不对具体如何使用做详细说明,只对于在使用Ajax中的一些需 ...

  7. 在 ASP.NET MVC 中使用 Chart 控件

    在 .NET 3.5 的时候,微软就提供了一个 Chart 控件,网络上有大量的关于在 VS2008 中使用这个控件的文章,在 VS2010 中,这个控件已经被集成到 ASP.NET 4.0 中,可以 ...

  8. ASP.NET MVC中实现多个按钮提交的几种方法

    有时候会遇到这种情况:在一个表单上需要多个按钮来完成不同的功能,比如一个简单的审批功能. 如果是用webform那不需要讨论,但asp.net mvc中一个表单只能提交到一个Action处理,相对比较 ...

  9. 在asp.net mvc中使用PartialView返回部分HTML段

    问题链接: MVC怎样实现异步调用输出HTML页面 该问题是个常见的 case, 故写篇文章用于提示新人. 在asp.net mvc中返回View时使用的是ViewResult,它继承自ViewRes ...

最新文章

  1. html 横屏内容显示不全_为什么我的文本显示不全?
  2. 沉沦17年,这位昔日科技霸主、最值钱企业,终于回来了……
  3. java 李刚 pdf_Java数据库技术详解(李刚) PDF_源雷技术空间
  4. RTP/RTCP/RTSP/SIP/SDP 关系(直接看总结)
  5. C++提高部分_C++函数模板的概念---C++语言工作笔记080
  6. 腾讯回应“QQ 冻结”;高德上线“家人地图”惹争议;Linux 内核讨论引入 Rust 代码| 极客头条...
  7. 计算理论笔记 9月27日
  8. 20200712每日一句
  9. IDEA使用maven命令打包
  10. jd页面与tianmao页面模仿jsonp跨域
  11. 网络监控解决方案及拓扑图
  12. 什么是Kurento
  13. 点击word页面自动弹出信息检索
  14. 磕磕碰碰三个月,终进字节
  15. 实现unity内2D物品延迟跟随鼠标移动
  16. 解决操作无法完成,因为其中的文件夹或文件已在另一程序中打开的问题
  17. php正则表达式懒惰匹配,正则表达式-贪婪与懒惰
  18. 构造器(有参、无参)
  19. 计算机 实验室安全准入制度,实验室安全准入制度
  20. http://codeforces.com/problemset/problem/158/B Taxi

热门文章

  1. python安装numpy-Python使用pip安装Numpy模块
  2. 初学者python编辑器-面向初学者的Python编辑器Mu
  3. python爬虫有什么用处-Python爬虫的作用与地位(附爬虫技术路线图)
  4. python 类-Python 类class定义 方法与属性教程
  5. python小游戏源码-导弹发射小游戏——Python源代码
  6. python快速编程入门教程-终于懂得python快速编程入门教程
  7. 爬虫python需要什么软件-python大神们!都在用什么爬虫工具呢?
  8. python编写程序的一般步骤-Python:开发_基本流程
  9. 自学python要下载什么软件-Python学习需要安装的工具
  10. python读取文件某一行-python和shell读取文件某一行