WCF Rest 4.0 中没有斜杠的简单 URL 路由
Posted
技术标签:
【中文标题】WCF Rest 4.0 中没有斜杠的简单 URL 路由【英文标题】:Simple URL routes in WCF Rest 4.0 without trailing slash 【发布时间】:2011-04-27 14:57:38 【问题描述】:我有一个基于 WCF REST 服务模板 40(CS) 的 WCF REST 4.0 项目。我想公开简单的服务端点 URL, 没有尾随斜杠。例如:
-
汽车服务.cs
http://www.domain.com/cars - GET 返回所有汽车的列表
http://www.domain.com/cars/123 - GET 返回一辆 ID 为 123 的汽车
卡车服务.cs
http://www.domain.com/trucks - GET 返回所有卡车的列表
http://www.domain.com/trucks/456 - GET 返回一辆 ID 为 456 的卡车
我将上述 URL 视为资源请求(而不是目录),这就是为什么我认为尾部斜杠在这里不合适。
不幸的是,我似乎无法获得我想要的行为,因为我总是被重定向到 /cars/ 和 /trucks/ 带有尾随斜杠。
这是我定义“汽车”路线和服务方法的方式 - 请注意,我没有在任何路线或 URI 模板定义中包含任何斜杠:
// Global.asax.cs
RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));
// CarService.cs
[WebGet(UriTemplate = "")]
public List<Car> GetCollection()
return DataContext.GetAllCars();
请注意,MVC 不能以这种方式工作。使用 MapRoute 方法,我可以将请求直接路由到 http://www.domain.com/about,而无需重定向到 /about/。如何在 WCF REST 4.0 中获得相同的行为?
【问题讨论】:
【参考方案1】:更可重用:
public class Global : NinjectHttpApplication
protected override void OnApplicationStarted()
base.OnApplicationStarted();
RegisterRoutes();
private void RegisterRoutes()
RouteTable.Routes.Add(new ServiceRoute("login", new NinjectWebServiceHostFactory(), typeof(LoginService)));
RouteTable.Routes.Add(new ServiceRoute("incidents", new NinjectWebServiceHostFactory(), typeof(IncidentService)));
SetRoutePrefixes();
//This is a workaround for WCF forcing you to end with "/" if you dont have a urlTemplate and redirecting if you dont have
protected void Application_BeginRequest(object sender, EventArgs e)
string rawUrl = HttpContext.Current.Request.RawUrl.ToLower();
if (_routePrefixes.Any(rawUrl.EndsWith))
HttpContext.Current.RewritePath(rawUrl + "/"); // append trailing slash
private static List<string> _routePrefixes;
private static void SetRoutePrefixes()
_routePrefixes = new List<string>();
foreach (var route in RouteTable.Routes)
var r = route as Route;
var routePrefix = r.Url.Split('/').First();
_routePrefixes.Add(routePrefix);
【讨论】:
【参考方案2】:尝试将其放入 Global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
string rawUrl = HttpContext.Current.Request.RawUrl.ToLower();
if (rawUrl.EndsWith("/cars"))
HttpContext.Current.RewritePath(rawUrl + "/"); // append trailing slash
【讨论】:
【参考方案3】:较早的问题,但这是我如何使用 WCF4 REST 服务解决问题的方法(使用 Global.asax 中的 RouteTable 添加 ServiceRoutes)。 IIS7 被配置为在调用服务时我有一个空的相对路径,因此处理方法的 UriTemplate 是空的,就像 Will 的 Car 示例一样。如果需要,我在服务的 web.config 文件中使用了重写规则来添加“/”。它始终与路径匹配,然后检查原始 URI (REQUEST_URI) 以查看它是否包含没有尾随“/”的路径。
<rewrite>
<rules>
<!--
This rule will append a "/" after "/car" if
the client makes a request without a trailing "/".
ASP however must have a trailing "/" to find
the right handler.
-->
<rule name="FixCarPath" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="REQUEST_URI" pattern="/car\?" />
</conditions>
<action type="Rewrite" url="PATH_INFO/" />
</rule>
</rules>
</rewrite>
【讨论】:
【参考方案4】:我正在处理这个确切的问题,并在 MS 在线文档中遇到了这个 sn-p:
默认情况下,路由不处理映射到 Web 服务器上现有物理文件的请求。例如,如果 Products/Beverages/Coffee.aspx 中存在物理文件,则不会通过路由处理对 http://server/application/Products/Beverages/Coffee.aspx 的请求。路由不会处理请求,即使它与定义的模式匹配,例如 controller/action/id。
我意识到我的路由模式与我的服务所在的目录匹配。似乎目录被视为物理文件,并且与目录匹配的路由模式也被忽略。因此,按照文档,我在 RouteCollection 上将 RouteExistingFiles 属性设置为“true”。我的服务现在似乎可以正确地路由请求,并且我已经能够保持我非常喜欢的 REST 语法。
【讨论】:
+1 我有一个服务,我想调用或不使用尾部斜杠。您的解决方案效果很好。【参考方案5】:尝试从...更改 Global.asax 中的代码
Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", new WebServiceHostFactory(), typeof(TruckService)));
...到...
WebServiceHostFactory factory = new WebServiceHostFactory();
Routes.Add(new ServiceRoute("cars", factory, typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", factory, typeof(TruckService)));
【讨论】:
【参考方案6】:您遇到的主要问题是,当您的 WebGet 中有 UriTemplate 的空字符串时,当前 版本的 WCF REST 会导致 307 重定向(到“/”)属性。据我所知,当前版本无法解决这个问题。
但是,鉴于您需要一种解决方案,即 1) 允许您区分服务,并且 2) 具有(相对)较短的 URI,因此有几个“中间立场”解决方案可以解决您的问题。
第一个解决方案 你可以把它放在你的 global.asax 文件中(每个this example)。你可以为每个服务做一个服务路由:
RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", new WebServiceHostFactory(), typeof(TruckService)));
此时您可以在每个服务中填充您的 UriTemplate:
[WebGet(UriTemplate = "all")]
CarPool GetAllCars();
[WebGet(UriTemplate = "carName")]
Car GetCar(string carName);
这将允许您使用以下 URI:
www.domain.com/cars/all
www.domain.com/cars/123 or www.domain.com/cars/honda
对于卡车也是如此:
www.domain.com/trucks/all
www.domain.com/trucks/123 or www.domain.com/trucks/ford
第二个解决方案 使用来自 REST Starter Kit 的服务主机(即 WebServiceHost2Factory)。
RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHost2Factory(), typeof(CarService)));
当使用您在上面尝试使用的 URI 时,这不会导致 307 重定向,因此,可以准确地为您提供所需的内容。虽然我意识到使用该服务主机工厂而不是 WCF 4 附带的服务主机工厂感觉有点奇怪。
【讨论】:
感谢史蒂夫提供的信息。我已经考虑过使用您的“全部”建议(这是一个不错的解决方法),但我不愿意放弃我最初希望的规范 REST 语法。我正在研究随附的 WebServiceHostFactory 和 WebServiceHost2Factory 之间的差异,以及后者阻止 307 重定向的原因。如果这是我可以理解的微小变化,我会采用这种方法。 其实我只是尝试使用 WebServiceHost2Factory 并得到相同的重定向结果。 哎呀-我的第二个解决方案-您是正确的,因为 WebServiceHost2Factory 导致相同的行为。 (当我测试它时,我没有一个空的 UriTemplate)。但是第一个解决方案仍然有效。 @SteveMichelotti 但这引发了一个问题,你能指出这个Overriding existing Routes 的灵魂吗?我面临同样的情况【参考方案7】:你需要一个 UriTemplate,试试这样的:
[ServiceContract()]
public interface ICarService
[OperationContract]
[WebGet(UriTemplate = "/Car")]
CarPool GetAllCars();
[OperationContract]
[WebGet(UriTemplate = "/Car/carName")]
Car GetCar(string carName);
【讨论】:
是的,但是我需要 CarService 和 TruckService,所以我必须在 Global.asax.cs 中添加两条路线,对吗?为了区分它们,URL 会非常冗长:例如domain.com/carservice/cars 和 domain.com/truckservice/trucks。我希望有多个服务类 和 短 URL:例如domain.com/cars 和 domain.com/trucks 如果我愿意在 single 服务类中列出我的所有服务方法,我可以拥有无斜杠 URL,这是正确的。但对于大型 API,这可能是数百种方法。以上是关于WCF Rest 4.0 中没有斜杠的简单 URL 路由的主要内容,如果未能解决你的问题,请参考以下文章
保护 WebServer 和 ApplicationServer 之间的内部 WCF 4.0 REST 服务的最简单方法是啥?