Simple URL routes in WCF Rest 4.0 without trailing slash

后端 未结 7 1421
太阳男子
太阳男子 2021-02-01 20:27

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:

相关标签:
7条回答
  • 2021-02-01 20:54

    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>
    
    0 讨论(0)
  • 2021-02-01 20:55

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

    0 讨论(0)
  • 2021-02-01 21:03

    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.

    0 讨论(0)
  • 2021-02-01 21:05

    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
            }
        }
    
    0 讨论(0)
  • 2021-02-01 21:14

    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.

    0 讨论(0)
  • 2021-02-01 21:16

    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);
            }
        }
    
    0 讨论(0)
提交回复
热议问题