WCF Rest 4.0 中不带尾部斜杠的简单 URL 路由

发布于 2024-09-28 00:55:45 字数 1314 浏览 0 评论 0原文

我有一个基于 WCF REST 服务模板 40(CS) 的 WCF REST 4.0 项目。我想公开简单的服务端点 URL,没有尾部斜杠。例如:

  1. CarService.cs
  2. http://www.domain.com/cars/123">http://www.domain.com/cars/123 - GET 返回 ID 为 123 TruckService.cs 的

我将上述 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 中获得相同的行为?

I have a WCF REST 4.0 project based on the the WCF REST Service Template 40(CS). I'd like to expose simple service endpoint URLs without trailing slashes. For example:

  1. CarService.cs
  2. TruckService.cs

I look at the above URLs as resource requests (not directories), which is why I don't think trailing slashes are appropriate here.

Unfortunately, I can't seem to get the behavior I want because I am always redirected to /cars/ and /trucks/ with a trailing slash.

Here's how I've defined the "cars" route and service method - note that I have not included any slashes in any of the route or URI template definitions:

// Global.asax.cs
RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));

// CarService.cs
[WebGet(UriTemplate = "")]
public List<Car> GetCollection()
{
    return DataContext.GetAllCars();
}

Note that MVC does not work this way. With the MapRoute method I can route requests directly to http://www.domain.com/about without a redirect to /about/. How can I get the same behavior in WCF REST 4.0?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一笔一画续写前缘 2024-10-05 00:55:45

您遇到的主要问题是,当 WebGet 属性中的 UriTemplate 为空字符串时,当前版本的 WCF REST 会导致 307 重定向(到“/”)。据我所知,当前版本无法解决此问题。

但是,如果您想要一个解决方案,1) 允许您区分服务,2) 具有(相对)短的 URI,那么您的问题有几个“中间立场”解决方案。

第一个解决方案
您可以将其放入您的 global.asax 文件中(根据 此示例)。您可以为每项服务制定一条服务路线:

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 入门工具包 中的服务主机(即 WebServiceHost2Factory)。

RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHost2Factory(), typeof(CarService)));

当您使用上面尝试使用的 URI 时,这不会导致 307 重定向,因此可以准确满足您的需要。尽管我意识到使用该服务主机工厂而不是 WCF 4 附带的服务主机工厂感觉有点奇怪。

The primary issue that you're running into is that the current version of WCF REST causes a 307 redirect (to the "/") when you have an empty string for the UriTemplate in your WebGet attribute. As far as I know, there is no getting around this in the current version.

However, there are a couple of "middle ground" solution to your problem given that you want a solution that 1) allows you to differentiate services, and 2) have (relatively) short URIs.

First Solution
You can put this in your global.asax file (per this example). You can do a service route for each service:

RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", new WebServiceHostFactory(), typeof(TruckService)));

At this point you can populate your UriTemplate in each service:

[WebGet(UriTemplate = "all")]
CarPool GetAllCars();

[WebGet(UriTemplate = "{carName}")]
Car GetCar(string carName);

This will allow you URIs of:

www.domain.com/cars/all
www.domain.com/cars/123 or www.domain.com/cars/honda

similarly for trucks:

www.domain.com/trucks/all
www.domain.com/trucks/123 or www.domain.com/trucks/ford

Second Solution
Use the service host from the REST Starter Kit (i.e., the WebServiceHost2Factory).

RouteTable.Routes.Add(new ServiceRoute("cars", new WebServiceHost2Factory(), typeof(CarService)));

This does not result in a 307 redirect when using the URIs that you're attempting to use above and thus, gives you exactly what you need. Though I realize that feels a little weird using that service host factory rather than the one that ships with WCF 4.

ˉ厌 2024-10-05 00:55:45

你需要一个 UriTemplate,尝试这样的事情:

 [ServiceContract()]
 public interface ICarService
 {

     [OperationContract]
     [WebGet(UriTemplate = "/Car")]
     CarPool GetAllCars();

     [OperationContract]
     [WebGet(UriTemplate = "/Car/{carName}")]
     Car GetCar(string carName);

 }

You need a UriTemplate, Try something like this:

 [ServiceContract()]
 public interface ICarService
 {

     [OperationContract]
     [WebGet(UriTemplate = "/Car")]
     CarPool GetAllCars();

     [OperationContract]
     [WebGet(UriTemplate = "/Car/{carName}")]
     Car GetCar(string carName);

 }
谢绝鈎搭 2024-10-05 00:55:45

尝试将其放入 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
        }
    }

Try putting this in the 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
        }
    }
白云悠悠 2024-10-05 00:55:45

我正在处理这个确切的问题,并在 MS 在线文档中遇到了这个片段:

默认情况下,路由不处理映射到 Web 服务器上现有物理文件的请求。例如,请求 http://server/application/Products/Beverages/Coffee.aspx。即使请求与定义的模式匹配,例如 {controller}/{action}/{id},路由也不会处理该请求。


我意识到我的路由模式与我的服务所在的目录相匹配。看起来目录被视为与物理文件相同,并且与目录匹配的路由模式也被忽略。因此,按照文档,我将 RouteCollection 上的 RouteExistingFiles 属性设置为“true”。我的服务现在似乎可以正确路由请求,并且我已经能够保留我非常喜欢的 REST 语法。

I was dealing with this exact problem and ran across this snippet in the MS online docs:

By default, routing does not handle requests that map to an existing physical file on the Web server. For example, a request for http://server/application/Products/Beverages/Coffee.aspx is not handled by routing if a physical file exists at Products/Beverages/Coffee.aspx. Routing does not handle the request even if it matches a defined pattern, such as {controller}/{action}/{id}.

I realized that my route pattern matched the directory my service was hosted in. It appears that a directory is treated the same as a physical file, and route patterns that match a directory are ignored as well. So following the documentation, I set the RouteExistingFiles property to "true" on the RouteCollection. My service now seems to be routing the requests correctly and I've been able to keep the REST syntax that I love so very very much.

傲性难收 2024-10-05 00:55:45

较旧的问题,但这是我如何使用 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>

Older question but here's how I solved the problem with a WCF4 REST service (using the RouteTable in Global.asax to add ServiceRoutes). IIS7 is configured so that by the time the service is invoked I have an empty relative path so the handling method's UriTemplate is empty like Will's Car example. I used a rewrite rule in the service's web.config file to add a "/" if needed. It always matches the path then checks the original URI ({REQUEST_URI}) to see if it contains a path without a trailing "/".

    <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>
拧巴小姐 2024-10-05 00:55:45

更具可重用性:

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);
        }
    }

A bit more reusable:

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);
        }
    }
半葬歌 2024-10-05 00:55:45

尝试将 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)));

Try changing your code in the Global.asax from...

Routes.Add(new ServiceRoute("cars", new WebServiceHostFactory(), typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", new WebServiceHostFactory(), typeof(TruckService)));

...to...

WebServiceHostFactory factory = new WebServiceHostFactory();

Routes.Add(new ServiceRoute("cars", factory, typeof(CarService)));
RouteTable.Routes.Add(new ServiceRoute("trucks", factory, typeof(TruckService)));

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文