Простые URL-маршруты в WCF Rest 4.0 без косой черты - PullRequest
19 голосов
/ 19 октября 2010

У меня есть проект WCF REST 4.0, основанный на шаблоне службы WCF REST 40 (CS).Я хотел бы предоставить простые URL-адреса конечных точек службы без конечных слешей.Например:

  1. CarService.cs
  2. TruckService.cs

Я рассматриваю вышеупомянутые URL как запросы ресурсов (не каталоги), поэтому я не думаю, что трейлингкосые черты здесь уместны.

К сожалению, я не могу получить желаемое поведение, потому что меня всегда перенаправляют на / cars / и / truck / с косой чертой.

Вот как я определил маршрут и метод обслуживания «автомобили» - обратите внимание, что я не включил ни одной косой черты ни в одно из определений маршрута или шаблона 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?

Ответы [ 7 ]

9 голосов
/ 20 октября 2010

Основная проблема, с которой вы сталкиваетесь, заключается в том, что текущая версия WCF REST вызывает перенаправление 307 (на "/"), когда в атрибуте WebGet есть пустая строка для UriTemplate. , Насколько я знаю, в текущей версии обойти это невозможно.

Тем не менее, есть несколько «средних» решений для вашей проблемы, учитывая, что вы хотите решение, которое 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 Starter Kit (т. Е. WebServiceHost2Factory).

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

Это не приводит к перенаправлению 307 при использовании URI, которые вы пытаетесь использовать выше, и, таким образом, дает вам именно то, что вам нужно. Хотя я понимаю, что это немного странно, если использовать эту фабрику хоста службы, а не ту, которая поставляется с WCF 4.

3 голосов
/ 01 июня 2012

Попробуйте поместить это в 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 голосов
/ 20 октября 2010

Вам нужен UriTemplate, попробуйте что-то вроде этого:

 [ServiceContract()]
 public interface ICarService
 {

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

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

 }
2 голосов
/ 19 июня 2013

Немного больше для повторного использования:

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 голосов
/ 02 мая 2012

Более старый вопрос, но вот как я решил проблему с REST-службой WCF4 (используя RouteTable в Global.asax для добавления ServiceRoutes). IIS7 настроен таким образом, что к моменту вызова службы у меня уже есть пустой относительный путь, поэтому UriTemplate метода обработки будет пустым, как в примере с машиной Уилла. Я использовал правило перезаписи в файле 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>
2 голосов
/ 03 января 2012

Я имел дело именно с этой проблемой и наткнулся на этот фрагмент в онлайн-документации MS:

По умолчанию маршрутизация не обрабатывает запросы, которые сопоставляются с существующим физическим файлом на веб-сервере.Например, запрос на http://server/application/Products/Beverages/Coffee.aspx не обрабатывается маршрутизацией, если физический файл существует в Products / Beverages / Coffee.aspx.Маршрутизация не обрабатывает запрос, даже если он соответствует определенному шаблону, например {controller} / {action} / {id}.

Я понял, что мой шаблон маршрута соответствует каталогу, в котором размещалась моя службав. Похоже, что каталог обрабатывается так же, как физический файл, и шаблоны маршрутов, соответствующие каталогу, также игнорируются.Поэтому, следуя документации, я установил для свойства RouteExistingFiles значение «true» в RouteCollection.Теперь моя служба, похоже, правильно направляет запросы, и я смог сохранить синтаксис REST, который мне очень нравится.

1 голос
/ 24 ноября 2010

Попробуйте изменить свой код в 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)));

...