您的位置:首页 > 理论基础 > 计算机网络

注册URL模式与HttpHandler的映射关系

2016-12-15 23:29 351 查看

注册URL模式与HttpHandler的映射关系

[code]












































[/code]
[/code]
我们说最终用于处理请求的HttpHandler最终体现为一个类型为RequestDelegate的委托对象,为此我们定义了如下一个与这个委托类型具有一致声明的方法WeatherForecast来处理针对天气的请求。如下面的代码片段所示,我们在这个方法中直接调用HttpContext的扩展方法GetRouteData得到RouterMiddleware中间件在路由解析过程中得到的路由参数。这个GetRouteData方法返回的是一个具有字典结构的对象,它的Key和Value分别代表路由参数的名称和值,我们通过预先定义的参数名(“city”和“days”)得到目标城市和预报天数。

[code][code]




























[/code]
[/code]
有了这两个核心参数之后,我们据此生成一个WeatherReport对象,并将它携带的天气信息以一个HTML文档的形式响应给客户端,图1所示就是这个HTML文档在浏览器上的呈现效果。由于目标城市最初以电话区号的形式体现,在呈现天气信息的过程中我们还会根据区号获取具体城市名称,简单起见,我们利用一个简单的字典来保存区号和城市之间的关系,并且只存储了三个城市而已。

接下来我们来完成所需的路由注册工作,实际上就是注册RouterMiddleware中间件。由于这各中间件定义在“Microsoft.AspNetCore.Routing”这个NuGet包中,所以我们需要添加对应的依赖。如下面的代码片段所示,针对RouterMiddleware中间件的注册实现在ApplicationBuilder的扩展方法UseRouter中。由于RouterMiddleware中间件在进行路由解析的过程中需要使用到一些服务,我们调用WebHostBuilder的ConfigureServices方法注册的就是这些服务。具体来说,这些与路由相关的服务是通过调用ServiceCollection的扩展方法AddRouting实现的。

[code][code]













[/code]
[/code]
RouterMiddleware中间件针对路由的解析依赖于一个名为Router的对象,对应的接口为IRouter。我们在程序中会先根据ApplicationBuilder对象创建一个RouteBuilder对象,并利用后者来创建这个Router。我们说路由注册从本质上体现为注册某种URL模式与一个RequestDelegate对象之间的映射,这个映射关系的建立是通过调用RouteBuilder的MapGet方法的调用。MapGet方法具有两个参数,第一个参数代表映射的URL模板,后者是处理请求的RequestDelegate对象。我们指定的URL模板为“weather/{city}/{days}”,其中携带两个路由参数({city}和{days}),我们知道它代表获取天气预报的目标城市和天数。由于针对天气请求的处理实现在我们定义的WeatherReport方法中,我们将指向这个方法的RequestDelegate对象作为第二个参数。

二、设置内联约束

在上面进行路由注册的实例中,我们在注册的URL模板中定义了两个参数({city}和{days})来分别代表获取天气预报的目标城市对应的区号和天数。区号应该具有一定的格式(以零开始的3-4位数字),而天数除了必须是一个整数之外,还应该具有一定的范围。由于我们在注册的时候并没有为这个两个路由参数的取值做任何的约束,所以请求URL携带的任何字符都是有效的。而处理请求的WeatherForecast方法也并没有对提取的数据做任何的验证,所以在执行过程中会直接抛出异常。如下图所示,由于请求URL(“/weather/0512/iv”)指定了天数不合法,所有客户端接收到一个状态为“500 Internal Server Error”的响应。





为了确保路由参数数值的有效性,我们在进行路由注册的时候可以采用内联(Inline)的方式直接将相应的约束规则定义在路由模板中。ASP.NET Core针对我们常用的验证规则定义了相应的约束表达式,我们可以根据需要为某个路由参数指定一个或者多个约束表达式。

如下面的代码片段所示,为了确保URL携带的是合法的区号,我们为路由参数{city}应用了一个针对正则表达式的约束(:regex(^0[1-9]{{2,3}}$))。由于路由模板在被解析的时候会将“{…}”这样的字符理解为路由参数,如果约束表达式需要使用“{}”字符(比如正则表达式“^0[1-9]{2,3}$)”),需要采用“{{}}”进行转义。至于另一个路由参数{days}则应用了两个约束,第一个是针对数据类型的约束(:int),它要求参数值必须是一个整数。另一个是针对区间的约束(:range(1,4)),意味着我们的应用最多只提供未来4天的天气。

[code][code]







[/code]
[/code]
如果我们在注册路由的时候应用了约束,那么当RouterMiddleware中间件在进行路由解析的时候除了要求请求路径必须与路由模板具有相同的模式,同时还要求携带的数据满足对应路由参数的约束条件。如果不能同时满足这两个条件,RouterMiddleware中间件将无法选择一个RequestDelegate对象来处理当前请求,在此情况下它将直接将请求递交给后续的中间件进行处理。对于我们演示的这个实例来说,如果我们提供一个不合法的区号(1014)和预报天数(5),客户端都将得到一个状态码为“404 Not Found”的响应。





三、为路由参数设置默认值

路由注册时提供的路由模板(比如“Weather/{city}/{days}”)可以包含静态的字符(比如“weather”),也可以包括动态的参数(比如{city}和{days}),我们将它们成为路由参数。并非每个路由参数都是必需的(要求路由参数的值必需存在请求路径中),有的路由参数是可以缺省的。还是以上面演示的实例来说,我们可以采用如下的方式在路由参数名后面添加一个问号(“?”),原本必需的路由参数变成了可以缺省的。可缺省的路由参数只能出现在路由模板尾部,这个应该不难理解。

[code][code]







[/code]
[/code]
既然可以路由变量占据的部分路径是可以缺省的,那么意味即使请求的URL不具有对应的内容(比如“weather”和“weather/010”),在进行路由解析的时候同样该请求与路由规则相匹配,但是在最终的路由参数字典中将找不到它们。由于表示目标城市和预测天数的两个路由参数都是可缺省的,我们需要对处理请求的WeatherForecast方法做作相应的改动。下面的代码片段表明如果请求URL为显式提供对应参数的数据,它们的默认值分别为“010”(北京)和4(天),也就是说应用默认提供北京地区未来四天的天气。

[code][code]












[/code]
[/code]
针对上述的改动,如果希望获取北京未来四天的天气状况,我们可以采用如下图所示的三种URL(“weather”和“weather/010”和“weather/010/4”),它们都是完全等效的。





上面我们的程序相当于是在进行请求处理的时候给予了可缺省路由参数一个默认值,实际上路由参数默认值得设置还具有一种更简单的方式,那就是按照如下所示的方式直接将默认值定义在路由模板中。如果采用这样的路由注册方式,我们针对WeatherForecast方法的改动就完全没有必要了。

[code][code]







[/code]
[/code]

四、特殊的路由参数

一个URL可以通过分隔符“/”划分为多个路径分段(Segment),路由模板中定义的路由参数一般来说会占据某个独立的分段(比如“weather/{city}/{days}”)。不过也有特例,我们即可以在一个单独的路径分段中定义多个路由参数,同样也可以让一个路由参数跨越对个连续的路径分段。

我们先来介绍在一个独立的路径分段中定义多个路由参数的情况。同样以我们演示的获取天气预报的URL为例,假设我们设计一种URL来获取某个城市某一天的天气信息,比如“/weather/010/2016.11.11”这样一个URL可以获取北京地区在2016年双11那天的天气,那么路由模板为“/weather/{city}/{year}.{month}.{day}”。

[code][code]




















[/code]
[/code]
由于URL采用了新的设计,所以我们按照如上的形式对相关的程序进行了相应的修改。现在我们采用匹配的URL(比如“/weather/010/2016.11.11”)就可以获取到某个城市指定日期的天气。





对于上面设计的这个URL来说,我们采用“.”作为日期分隔符,如果我们采用“/”作为日期分隔符(比如“2016/11/11”),这个路由默认应该如何定义呢?由于“/”同时也是URL得路径分隔符,如果表示日期的路由变量也采用相同的分隔符,意味着同一个路由参数跨越了多个路径分段,我们只能定义“通配符”路由参数的形式来达到这个目的。通配符路由参数采用“{*variable}”这样的形式,星号(“*”)表示路径“余下的部分”,所以这样的路由参数只能出现在模板的尾端。对我们的实例来说,路由模板可以定义成“/weather/{city}/{*date}”。

[code][code]





















[/code]
[/code]
我们可以对程序做如上的修改来使用新的URL模板(“/weather/{city}/{*date}”)。这样为了得到如上图所示的北京在2016年11月11日的天气,请求的URL可以替换成“/weather/010/2016/11/11”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: