Можно ли использовать периоды в маршрутах App.Net Web Api?
Я работаю над перемещением проекта API из необработанных обработчиков HTTP, где я использую периоды в путях:
http://server/collection/id.format
Я хотел бы следовать той же схеме URL-адресов в версии веб-Api (самостоятельно) и пробовал это:
var c = new HttpSelfHostConfiguration(b);
c.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}.{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: null
);
К сожалению, это не похоже на разрешение (согласованное 404 on/foo,/foo/bar и /foo/bar.txt). Аналогичный шаблон с использованием косой черты перед "форматом" отлично работает:
var c = new HttpSelfHostConfiguration(b);
c.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}/{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: null
);
Я еще не углубился в код для Web Api, и прежде чем я подумал, что попрошу здесь узнать, является ли это известным или, возможно, даже оправданным ограничением в Web Api.
ОБНОВЛЕНИЕ: Я забыл упомянуть, что "id" и "format" - это строки, которые, как оказалось, важны для решения этого вопроса. Добавление ограничения для исключения периодов из маркера "id" решает проблему 404.
Ответы
Ответ 1
Я не могу воспроизвести проблему. Это должно сработать. Здесь моя настройка:
- Создайте новое консольное приложение .NET 4.0
- Перейти к профилю .NET Framework 4.0
- Установите
Microsoft.AspNet.WebApi.SelfHost
NuGet
-
Определите Product
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
-
Соответствующий API-контроллер:
public class ProductsController : ApiController
{
public Product Get(int id)
{
return new Product
{
Id = id,
Name = "prd " + id
};
}
}
-
И хост:
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}.{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: null
);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
Теперь, когда вы запускаете это консольное приложение, вы можете перейти к http://localhost:8080/products/123.xml
. Но, конечно, вы можете перейти к http://localhost:8080/products/123.json
, и вы все равно получите XML. Поэтому возникает вопрос: как включить согласование контента с использованием параметра маршрута?
Вы можете сделать следующее:
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Formatters.XmlFormatter.AddUriPathExtensionMapping("xml", "text/html");
config.Formatters.JsonFormatter.AddUriPathExtensionMapping("json", "application/json");
config.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}.{ext}",
defaults: new { id = RouteParameter.Optional, formatter = RouteParameter.Optional },
constraints: null
);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
и теперь вы можете использовать следующие URL:
http://localhost:8080/products/123.xml
http://localhost:8080/products/123.json
Теперь вам может быть интересно, какая связь между параметром маршрута {ext}
, который мы использовали в нашем определении маршрута, и методом AddUriPathExtensionMapping
, потому что мы нигде не указали его. Ну, угадайте, что: он жестко закодирован в классе UriPathExtensionMapping
до ext
, и вы не можете его модифицировать, потому что он доступен только для чтения:
public class UriPathExtensionMapping
{
public static readonly string UriPathExtensionKey;
static UriPathExtensionMapping()
{
UriPathExtensionKey = "ext";
}
...
}
Все это, чтобы ответить на ваш вопрос:
Можно использовать периоды в маршрутах App.Net Web Api?
Да.
Ответ 2
Я смог добиться этого, выполнив следующие действия:
замените "*."
на "*"
в system.webServer.handlers в web.config, т.е. удалите период.
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
Ответ 3
Будьте осторожны, чтобы установить параметр runAllManagedModulesForAllRequests в атрибуте modules в вашем web.config
<modules runAllManagedModulesForAllRequests="true">..</modules>
В противном случае он не будет работать в IIS (вероятно, он будет обрабатываться не управляемыми обработчиками).
Ответ 4
Я принимаю ответ Дарина (да, периоды можно использовать в URL-адресах маршрута), потому что это было точно правильно для моего примера, но бесполезно для меня. Это моя ошибка, поскольку я не указываю, что "id" - это строка, а не целое число.
Чтобы использовать период, следующий за строковым параметром, движок маршрутизации нуждается в подсказках в виде ограничения:
var c = new HttpSelfHostConfiguration(b);
c.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}.{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: new { id = "[^\\.]+" } // anything but a period
);
Добавление ограничения на предыдущий токен позволяет правильно разложить и обработать входящие URL-адреса. Без подсказки маркер "id" можно интерпретировать в соответствии с оставшейся степенью URL. Это всего лишь конкретный случай необходимости ограничить границы между строковыми параметрами в целом.
Да, периоды могут использоваться в маршрутах URL в веб-API Asp.Net, но если они должны следовать строковому параметру, обязательно примените правильное ограничение к маршруту.
Ответ 5
IIS перехватывает запросы с периодом загрузки файлов. В вашем web.config вы можете настроить IIS на игнорирование определенных URL-адресов, потому что webapi будет обрабатывать запросы вместо этого. Если вы хотите, чтобы IIS обрабатывал загрузку файлов, а также вызовы webapi процесса, вы можете добавить конфигурацию ManagedDllExtension в system.webServer.handlers в web.config.
<add name="ManagedDllExtension" path="collection/*.*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />