Простые URL-адреса в WCF Rest 4.0 без конечной косой черты
У меня есть проект WCF REST 4.0, основанный на шаблоне 40 службы сервиса REST WCF (CS). Я бы хотел показать простые URL-адреса конечных точек без привязки косых черт. Например:
- CarService.cs
- TruckService.cs
Я просматриваю вышеуказанные URL-адреса как запросы ресурсов (не каталоги), поэтому я не думаю, что конечные косые черты здесь подходят.
К сожалению, я не могу получить поведение, которое я хочу, потому что меня всегда перенаправляют на/cars/and/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
Основная проблема, с которой вы столкнулись, заключается в том, что текущая версия WCF REST вызывает перенаправление 307 (к "/" ), когда у вас есть пустая строка для UriTemplate в вашем WebGet атрибут. Насколько мне известно, в текущей версии этого не происходит.
Тем не менее, существует пара решений "среднего уровня" для вашей проблемы, учитывая, что вы хотите решение, которое 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, которые вы пытаетесь использовать выше, и, таким образом, дает вам именно то, что вам нужно. Хотя я понимаю, что немного странно использовать этот хост службы factory, а не тот, который поставляется с WCF 4.
Ответ 2
Вам нужен UriTemplate, попробуйте что-то вроде этого:
[ServiceContract()]
public interface ICarService
{
[OperationContract]
[WebGet(UriTemplate = "/Car")]
CarPool GetAllCars();
[OperationContract]
[WebGet(UriTemplate = "/Car/{carName}")]
Car GetCar(string carName);
}
Ответ 3
Попробуйте поместить это в файл 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
}
}
Ответ 4
Я имел дело с этой точной проблемой и просмотрел этот фрагмент в онлайн-документах MS:
По умолчанию маршрутизация не обрабатывает запросы, которые сопоставляются с существующим физическим файлом на веб-сервере. Например, запрос http://server/application/Products/Beverages/Coffee.aspx не обрабатывается маршрутизацией, если физический файл существует в Products/Beverages/Coffee.aspx. Маршрутизация не обрабатывает запрос, даже если он соответствует определенному шаблону, например {controller}/{action}/{id}.
Я понял, что мой шаблон маршрута соответствует каталогу, в котором была размещена моя служба. Похоже, что каталог обрабатывается так же, как физический файл, и шаблоны маршрутов, которые соответствуют каталогу, также игнорируются. Поэтому, следуя документации, я установил свойство RouteExistingFiles в значение "true" на RouteCollection. Моя служба теперь, кажется, правильно маршрутизирует запросы, и я смог сохранить синтаксис REST, который мне очень нравится.
Ответ 5
Более старый вопрос, но вот как я решил проблему с сервисом REST WCF4 (используя RouteTable в Global.asax для добавления ServiceRoutes). IIS7 настроен так, что к моменту запуска службы у меня есть пустой относительный путь, так что метод обработки UriTemplate пуст, как пример Will Car. Я использовал правило перезаписи в файле service.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>
Ответ 6
Попробуйте изменить свой код в 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)));
Ответ 7
Немного больше повторного использования:
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);
}
}