c# - net - web api 2 route config
¿Se pueden usar los períodos en Asp.Net Web Api Routes? (5)
Estoy trabajando en mover un proyecto de API de manejadores http sin procesar donde estoy usando puntos en las rutas:
http://server/collection/id.format
Me gustaría seguir el mismo esquema de URL en una versión de Web Api (auto hospedado) y probé esto:
var c = new HttpSelfHostConfiguration(b);
c.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}.{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: null
);
Desafortunadamente, eso no parece resolverse (los 404 consistentes en / foo, / foo / bar y /foo/bar.txt). Un patrón similar con una barra inclinada antes de ''formato'' funciona bien:
var c = new HttpSelfHostConfiguration(b);
c.Routes.MapHttpRoute(
name: "DefaultApiRoute",
routeTemplate: "{controller}/{id}/{format}",
defaults: new { id = RouteParameter.Optional, format = RouteParameter.Optional },
constraints: null
);
Todavía no he profundizado en el código de la Web Api, y antes de eso pensé en preguntar aquí para ver si se trata de una limitación conocida, o incluso justificada en la Web Api.
ACTUALIZACIÓN: olvidé mencionar que "id" y "formato" son cadenas, lo que resulta importante para la solución de esta pregunta. Agregar una restricción para excluir períodos del token "id" resuelve el problema 404.
Estoy aceptando la respuesta de Darin (sí, los puntos se pueden usar en las urls de ruta) porque fue específicamente correcta para mi ejemplo, pero no me ayudó. Esto es mi culpa por no indicar específicamente que "id" es una cadena, no un entero.
Para usar un período después de un parámetro de cadena, el motor de enrutamiento necesita sugerencias en forma de restricción:
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
);
Agregar la restricción al token anterior permite que las URL entrantes se descompongan y procesen correctamente. Sin la sugerencia, el token "id" puede interpretarse para que coincida con la extensión restante de la URL. Este es solo un caso específico de necesidad de restricciones para delinear los límites entre los parámetros de cadena en general.
Sí, los períodos se pueden usar en las rutas URL en la API web de Asp.Net, pero si van a seguir un parámetro de cadena, asegúrese de aplicar la restricción correcta a la ruta.
IIS intercepta las solicitudes con un período de descarga de archivos. En su web.config, puede configurar IIS para ignorar rutas URL específicas porque el webapi manejará las solicitudes en su lugar. Si desea que IIS maneje las descargas de archivos y que procese las llamadas de webapi, puede agregar una configuración ManagedDllExtension a system.webServer.handlers en web.config.
<add name="ManagedDllExtension" path="collection/*.*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
No puedo reproducir el problema. Esto debería funcionar. Aquí está mi configuración:
- Crear una nueva aplicación de consola .NET 4.0
- Cambiar al perfil de .NET Framework 4.0
- Instale el
Microsoft.AspNet.WebApi.SelfHost
NuGet Definir un
Product
public class Product { public int Id { get; set; } public string Name { get; set; } }
Un controlador API correspondiente:
public class ProductsController : ApiController { public Product Get(int id) { return new Product { Id = id, Name = "prd " + id }; } }
Y un anfitrión:
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(); } } }
Ahora, cuando ejecute esta aplicación de consola, podría navegar a http://localhost:8080/products/123.xml
. Pero, por supuesto, puede navegar a http://localhost:8080/products/123.json
y aún obtendrá XML. Entonces la pregunta es: ¿Cómo habilitar la negociación de contenido usando un parámetro de ruta?
Podrías hacer lo siguiente:
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();
}
}
}
Y ahora puedes usar las siguientes urls:
http://localhost:8080/products/123.xml
http://localhost:8080/products/123.json
Ahora puede que se esté preguntando cuál es la relación entre el parámetro de ruta {ext}
que usamos en nuestra definición de ruta y el método AddUriPathExtensionMapping
porque en ninguna parte no lo especificamos. Bueno, adivina qué: está codificado en la clase UriPathExtensionMapping
hasta la ext
y no puedes modificarlo porque es de solo lectura:
public class UriPathExtensionMapping
{
public static readonly string UriPathExtensionKey;
static UriPathExtensionMapping()
{
UriPathExtensionKey = "ext";
}
...
}
Todo esto para responder a tu pregunta:
¿Se pueden usar los períodos en Asp.Net Web Api Routes?
Sí.
Pude lograr esto haciendo lo siguiente: reemplazar "*."
con "*"
en system.webServer.handlers en web.config, es decir, eliminar el período.
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
Tenga cuidado de establecer la opción runAllManagedModulesForAllRequests en los atributos de los módulos en su web.config
<modules runAllManagedModulesForAllRequests="true">..</modules>
De lo contrario, no funcionará en IIS (probablemente sería manejado por manejadores no administrados).