Compartir a través de


Enrutamiento a acciones de controlador en ASP.NET Core

Por Ryan Nowak, Kirk Larkin y Rick Anderson

Note

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 10 de este artículo.

Warning

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la Política de soporte de .NET y .NET Core. Para la versión actual, consulte la versión .NET 10 de este artículo.

ASP.NET Core utiliza middleware de enrutamiento para coincidir las URLs de las solicitudes entrantes y mapearlas a acciones. Plantillas de ruta:

  • Se definen al inicio o en atributos.
  • Describir cómo se asocian las rutas URL con las acciones.
  • Se usan para generar direcciones URL para vínculos. Normalmente, los vínculos generados se devuelven en las respuestas.

Las acciones se enrutan bien mediante convención o bien mediante atributos. Colocar una ruta en el controlador o la acción hace que se enrute mediante atributos. Consulte Enrutamiento mixto para obtener más información.

Este documento:

  • Explica las interacciones entre MVC y el enrutamiento:
    • Cómo las aplicaciones MVC típicas usan las características de enrutamiento.
    • Cubre ambos:
      • El enrutamiento convencional se usa normalmente con controladores y vistas.
      • Enrutamiento de atributos usado con las API. Si está interesado principalmente en el enrutamiento de las API, consulte la sección Enrutamiento de atributos para API.
    • Consulte Enrutamiento para obtener más información sobre enrutamiento avanzado.
  • Hace referencia al sistema de enrutamiento predeterminado como enrutamiento de puntos de conexión. Puede usar controladores con la versión anterior de enrutamiento con fines de compatibilidad. Consulte la guía de migración 2.2-3.0 para obtener instrucciones.

Configuración de una ruta convencional

La plantilla de ASP.NET Core MVC genera código de enrutamiento convencional similar al ejemplo siguiente:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Utiliza para crear una sola ruta. El recorrido es único. La mayoría de las aplicaciones con controladores y vistas utilizan una plantilla de ruta similar a la ruta . Las API deben usar el enrutamiento de atributos.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

La plantilla de ruta:

  • Coincide con una ruta URL como

  • Extrae los valores de ruta mediante la tokenización del camino. La extracción de valores de ruta da como resultado una coincidencia si la aplicación tiene un controlador denominado y una acción:

    public class ProductsController : Controller
    {
        public IActionResult Details(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }
    }
    

    MyDisplayRouteInfo es proporcionado por el paquete NuGet Rick.Docs.Samples.RouteInfo y muestra información de ruta.

  • model enlaza el valor de para establecer el parámetro en . Para obtener más información, vea Vinculación de modelos.

  • define como el valor predeterminado.

  • define como el valor predeterminado.

  • El caracter en define como opcional.

    • No es necesario que los parámetros de ruta opcionales y predeterminados estén presentes en la ruta de dirección URL para una coincidencia. Para obtener una descripción detallada de la sintaxis de la plantilla de ruta, consulte Referencia de plantilla de ruta.
  • Coincide con la ruta de acceso de la URL.

  • Genera valores de la ruta.

Los valores de y hacen uso de los valores predeterminados. no produce ningún valor ya que no hay ningún segmento correspondiente en la ruta URL. solo coincide si existe una acción y :

public class HomeController : Controller
{
    public IActionResult Index() { ... }
}

Con la definición del controlador anterior y la plantilla de ruta, la acción definida se ejecuta para las siguientes URL:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

La ruta de acceso de la URL usa la plantilla de ruta predeterminada para el controlador y la acción. La ruta URL utliza la acción por defecto de la plantilla de ruta.

El método de conveniencia :

app.MapDefaultControllerRoute();

Replaces:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Important

El enrutamiento se configura mediante el middleware de y . Para usar controladores:

  • Llame para mapear controladores enrutados por atributos.
  • Llame a o para mapear tanto los controladores enrutados de manera convencional como los controladores enrutados por atributos.

Normalmente, las aplicaciones no necesitan llamar a ni a . configura una canalización de middleware que encapsula el middleware agregado mediante el uso de y . Para obtener más información, consulte Routing en ASP.NET Core.

Enrutamiento convencional

Use el enrutamiento convencional con controladores y vistas. La ruta es:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

El código anterior es un ejemplo de una ruta convencional. Se denomina enrutamiento convencional porque establece una convención para las rutas URL:

  • El primer segmento de la ruta, , asigna el nombre del controlador.
  • El segundo segmento, , asigna el nombre de la acción.
  • El tercer segmento se usa para un parámetro opcional. El en lo hace opcional. se asigna a una entidad de modelo.

Con esta ruta, la ruta de URL:

  • se asigna a la acción.
  • El mapeo se realiza a y el modelo normalmente vincula el parámetro a 17.

Este mapeo:

  • Se basa en los nombres de controlador y acción solamente.
  • No se basa en espacios de nombres, ubicaciones de archivos de origen ni parámetros de método.

Al usar el enrutamiento convencional con la ruta predeterminada, no es necesario crear un nuevo patrón de dirección URL para cada acción. Para una aplicación con acciones de estilo CRUD, mantener la coherencia en las URLs a través de los controladores:

  • Ayuda a simplificar el código.
  • Hace que la interfaz de usuario sea más predecible.

Warning

La plantilla de ruta define [el elemento] como opcional. Las acciones se pueden ejecutar sin el identificador opcional proporcionado como parte de la dirección URL. Por lo general, cuando se omite de la URL:

  • El enlace de modelos establece el objeto al valor especificado.
  • No se encuentra ninguna entidad en la base de datos que coincida con .

El enrutamiento de atributos proporciona un control preciso para hacer que el ID sea necesario para algunas acciones y no para otras. Por convención, la documentación incluirá parámetros opcionales como cuando sea más probable que su uso sea correcto.

La mayoría de las aplicaciones deben elegir un esquema de enrutamiento básico y descriptivo para que las direcciones URL sean legibles y significativas. La ruta convencional predeterminada :

  • Admite un esquema de enrutamiento básico y descriptivo.
  • Se trata de un punto de partida útil para las aplicaciones basadas en la interfaz de usuario.
  • Es la única plantilla de ruta necesaria para muchas aplicaciones de interfaz de usuario web. Para aplicaciones de interfaz de usuario web de mayor tamaño, a menudo basta con utilizar Areas.

y :

  • Asignar automáticamente un valor de orden a sus puntos finales en función del orden en que son invocados.

Enrutamiento de puntos de conexión en ASP.NET Core:

  • No tiene un concepto de rutas.
  • No proporciona garantías de ordenación para la ejecución de la extensibilidad. Todos los puntos de conexión se procesan a la vez.

Habilite el registro para ver de qué forma las implementaciones de enrutamiento integradas, como , coinciden con las solicitudes.

El enrutamiento de atributos se explica más adelante en este documento.

Varias rutas convencionales

Puede configurar varias rutas convencionales agregando más llamadas a y . Al agregar estas llamadas, puede definir varias convenciones o agregar rutas convencionales dedicadas a una acción específica, como:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

La ruta del código anterior es una ruta convencional dedicada. Es una ruta convencional dedicada porque:

  • Utiliza el enrutamiento convencional.
  • Se dedica a una acción específica.

Dado que la plantilla de ruta no incluye ni como parámetros:

  • Solo pueden tener los valores predeterminados .
  • Esta ruta siempre se asigna a la acción .

, y son las únicas rutas de dirección URL que coinciden con la ruta del blog.

En el ejemplo anterior:

  • La ruta tiene una prioridad más alta para las coincidencias que la ruta porque la agrega primero.
  • Es un ejemplo de enrutamiento de estilo slug donde es habitual tener un nombre de artículo como parte de la dirección URL.

Warning

En ASP.NET Core, el enrutamiento no:

  • Defina un concepto denominado ruta. agrega coincidencia de rutas a la canalización de middleware. El middleware examina el conjunto de puntos finales definidos en la aplicación y selecciona el mejor punto final en función de la solicitud.
  • Proporcione garantías sobre el orden de ejecución de extensibilidad como o .

Consulte Enrutamiento para obtener material de referencia sobre el enrutamiento.

Orden de enrutamiento convencional

El enrutamiento convencional solo coincide con una combinación de acción y controlador que define la aplicación. Este enfoque simplifica los casos en los que las rutas convencionales se superponen. Al agregar rutas mediante , y , los puntos de conexión obtienen automáticamente un valor de pedido en función del orden en el que se invocan estos métodos. Las coincidencias de una ruta que aparece anteriormente en la lista tienen una prioridad mayor. El enrutamiento convencional depende del orden. En general, coloque rutas con áreas anteriores porque son más específicas que las rutas sin área. Rutas convencionales dedicadas con parámetros de ruta catch-all, como ", pueden hacer que una ruta sea demasiado voraz. Una ruta codiciosa coincide con direcciones URL que usted pretendía que coincidieran con otras rutas. Coloque las rutas codiciosas más adelante en la tabla de rutas para evitar coincidencias codiciosas.

Warning

Un parámetro catch-all puede relacionar rutas de forma incorrecta debido a un error en el enrutamiento. Las aplicaciones afectadas por este error tienen las características siguientes:

  • Una ruta global, por ejemplo,
  • La ruta catch-all causa un error al relacionar solicitudes que sí que debería relacionar.
  • Al eliminar otras rutas, la ruta catch-all comienza a funcionar.

Consulte los errores de GitHub 18677 y 16579 para casos de ejemplo en los que se produce este error.

Una corrección opcional para este error se incluye en el SDK de .NET Core 3.1.301 o posterior. En el código que hay a continuación se establece un cambio interno que corrige este error:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Resolución de acciones ambiguas

Cuando dos puntos de conexión coinciden a través del enrutamiento, el enrutamiento debe realizar uno de los pasos siguientes:

  • Elija el mejor candidato.
  • Lanzar una excepción.

Por ejemplo:

public class Products33Controller : Controller
{
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpPost]
    public IActionResult Edit(int id, Product product)
    {
        return ControllerContext.MyDisplayRouteInfo(id, product.name);
    }
}

El controlador anterior define dos acciones que coinciden con:

  • Camino de la URL
  • Datos de ruta .

Este es un patrón típico para los controladores MVC:

  • muestra un formulario para editar un producto.
  • procesa el formulario publicado.

Para resolver la ruta correcta:

  • es seleccionada cuando la solicitud es una HTTP.
  • se selecciona cuando el verbo HTTP es cualquier otra cosa. se suele llamar a través de .

, se proporciona al enrutamiento para que pueda elegir en función del método HTTP de la solicitud. El hace una mejor combinación que .

Es importante comprender el rol de atributos como . Los atributos similares se definen para otros verbos HTTP. En el enrutamiento convencional, las acciones suelen usar el mismo nombre de acción cuando forman parte de un formulario de visualización, flujo de trabajo para enviar el formulario. Por ejemplo, analice los dos métodos de acción de edición.

Si el enrutamiento no puede elegir el mejor candidato, lanza un error y enumera varios puntos de conexión coincidentes.

Nombres de ruta convencionales

Las cadenas y en los siguientes ejemplos son nombres de ruta convencionales:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

Los nombres de ruta asignan un nombre lógico a la ruta. La ruta con nombre puede utilizarse para la generación de URL. El uso de una ruta con nombre simplifica la creación de URL cuando el orden de las rutas podría complicar la generación de URL. Los nombres de ruta deben ser únicos en toda la aplicación.

Nombres de ruta:

  • No tiene ningún impacto en la coincidencia de direcciones URL ni en el control de las solicitudes.
  • Solo se usan para la generación de direcciones URL.

El concepto de nombre de ruta se representa en el enrutamiento como IEndpointNameMetadata. Los términos nombre de ruta y nombre del punto de conexión:

  • Son intercambiables.
  • El que se utiliza en la documentación y el código depende de la API que está siendo descrita.

Enrutamiento de atributos para las API

Las API deben usar el enrutamiento mediante atributos para modelar la funcionalidad de la aplicación como un conjunto de recursos donde las operaciones se representan mediante verbos HTTP.

El enrutamiento mediante atributos utiliza un conjunto de atributos para asignar acciones directamente a las plantillas de ruta. El siguiente código es típico para una API y se utiliza en el siguiente ejemplo:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

En el código anterior, se llama a un método para mapeo de controladores con enrutamiento por atributos.

En el ejemplo siguiente:

  • coincide con un conjunto de URL de manera similar a como lo hace la ruta convencional por defecto.
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La acción se ejecuta para cualquiera de las rutas de acceso URL , , , o .

Este ejemplo resalta una diferencia clave de programación entre el enrutamiento mediante atributos y el enrutamiento convencional. El enrutamiento de atributos requiere más entrada para especificar una ruta. La ruta predeterminada convencional controla las rutas más concisamente. Pero el enrutamiento mediante atributos permite (y requiere) un control preciso de las plantillas de ruta que se aplicarán a cada acción.

Con el enrutamiento de atributos, los nombres del controlador y la acción no influyen en la selección de la acción, a menos que se utilice el reemplazo de tokens. En el ejemplo siguiente se coinciden las mismas direcciones URL que el ejemplo anterior:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código siguiente se usa el reemplazo de tokens para y :

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El siguiente código se aplica al controlador.

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

En el código anterior, las plantillas de método deben anteponerse a las plantillas de ruta usando o . Las plantillas de ruta aplicadas a una acción que comienzan por o no se combinan con las plantillas de ruta que se aplican al controlador.

Consulte Precedencia de la plantilla de ruta para obtener información sobre la selección de plantillas de ruta.

Nombres de enrutamiento reservados

Las palabras clave siguientes son nombres de parámetros de ruta reservados cuando se usan Controladores o Páginas:

  • action
  • area
  • controller
  • handler
  • page

El uso de como parámetro de ruta con enrutamiento de atributos es un error común. Esta opción da como resultado un comportamiento incoherente y confuso con la generación de direcciones URL.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

La generación de direcciones URL usa estos nombres de parámetros especiales para determinar si una operación de generación de direcciones URL hace referencia a una página o a un controlador.

Las siguientes palabras clave están reservadas en el contexto de una vista o una página.

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

No use estas palabras clave para las generaciones de vínculos, los parámetros enlazados al modelo ni las propiedades de nivel superior.

Plantillas de verbos HTTP

ASP.NET Core incluye las siguientes plantillas de verbo HTTP:

  • [HttpGet]
  • [HttpPost]
  • [HttpPut]
  • [HttpDelete]
  • [HttpHead]
  • [HttpPatch]

Plantillas de ruta

ASP.NET Core incluye las siguientes plantillas de ruta:

  • Todas las plantillas de verbo HTTP son plantillas de ruta HTTP.
  • [Route]

Enrutamiento de atributos con atributos de verbo HTTP

Considere el siguiente controlador:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior:

  • Cada acción contiene el atributo , que restringe la coincidencia solo con las solicitudes HTTP GET.
  • La acción incluye la plantilla, por lo que se anexa a la plantilla en el controlador. La plantilla del método es . Por lo tanto, esta acción solo coincide con las solicitudes GET para el formulario , , , etc.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • La acción contiene la plantilla. La parte de la plantilla restringe los valores de ruta a cadenas que se pueden convertir en un entero. Una solicitud GET para :
    • No coincide con esta acción.
    • Devuelve un error 404 No encontrado.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • La acción contiene elementos en la plantilla, pero no limita estos valores a aquellos que se pueden convertir en un entero. Una solicitud GET a:
    • Coincide con esta ruta.
    • El enlace de modelos no logra convertir a un entero. El parámetro del método es entero.
    • Devuelve un error 400 Solicitud incorrecta porque el enlace de modelos no pudo convertir en un entero.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

El enrutamiento por atributos puede utilizar atributos como , y . Todos los atributos del verbo HTTP aceptan una plantilla de ruta. Este ejemplo muestra dos acciones que coinciden con la misma plantilla de ruta:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Usando la ruta URL:

  • La acción se ejecuta cuando el verbo HTTP es .
  • La acción se ejecuta cuando el verbo HTTP es .

Al crear una API, es raro que necesite usar un método de acción porque una acción acepta todos los métodos HTTP. Use el atributo de verbo HTTP más específico para ser preciso sobre lo que admite la API. Se espera que los clientes de las API de sepan qué rutas y verbos HTTP corresponden a operaciones lógicas específicas.

Las API deben usar el enrutamiento mediante atributos para modelar la funcionalidad de la aplicación como un conjunto de recursos donde las operaciones se representan mediante verbos HTTP. Este diseño significa que muchas operaciones, como GET y POST en el mismo recurso lógico, usan la misma dirección URL. El enrutamiento de atributos proporciona el nivel de control necesario para diseñar cuidadosamente el diseño del punto de conexión público de una API.

Puesto que una ruta de atributo se aplica a una acción específica, es fácil crear parámetros necesarios como parte de la definición de plantilla de ruta. En este ejemplo, se requiere como parte de la ruta de URL:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La acción :

  • Se ejecuta con la ruta URL, como
  • No se ejecuta con la ruta de URL.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Para obtener más información, consulte Definir los tipos de contenido de solicitud compatibles con el atributo Consumes.

Consulte Enrutamiento para obtener una descripción completa de las plantillas de ruta y las opciones relacionadas.

Para obtener más información sobre , vea Atributo ApiController.

Nombre de ruta

El siguiente código define un nombre de ruta :

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Use nombres de ruta para generar una dirección URL basada en una ruta específica. Nombres de ruta:

  • No tienen ningún impacto en el comportamiento de coincidencia de URL del enrutamiento.
  • Solo se usan para la generación de direcciones URL.

Los nombres de ruta deben ser únicos en toda la aplicación.

Contrasta el código anterior con la ruta por defecto convencional, que define el parámetro como opcional (). Esta capacidad de especificar con precisión las API tiene sus ventajas, como permitir que y se envíen a acciones diferentes.

Combinando rutas de atributos

Para que el enrutamiento de atributos sea menos repetitivo, combine atributos de ruta en el controlador con atributos de ruta en las acciones individuales. Las plantillas de ruta que defina en el controlador se anteponen a las plantillas de ruta definidas en las acciones. Al colocar un atributo de ruta en el controlador, todas las acciones del controlador usan el enrutamiento de atributos.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el ejemplo anterior:

  • La ruta de la URL puede coincidir con
  • La ruta URL puede coincidir con .

Ambas acciones sólo coinciden con HTTP porque están marcadas con el atributo .

Plantillas de ruta que se aplican a una acción y que comienzan por o no se combinan con plantillas de ruta que se aplican al controlador. El siguiente ejemplo coincide con un conjunto de rutas URL similares a la ruta por defecto.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

En la tabla siguiente se explican los atributos del código anterior:

Attribute Combina con Define la plantilla de ruta
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Orden de ruta de atributo

El enrutamiento crea un árbol y empareja simultáneamente todos los endpoints.

  • Las entradas de ruta funcionan como si estuvieran colocadas en un orden ideal.
  • Las rutas más específicas tienen la oportunidad de ejecutarse antes que las rutas más generales.

Por ejemplo, una ruta de atributos como es más específica que una ruta de atributos como . La ruta tiene una prioridad más alta, de forma predeterminada, porque es más específica. Mediante el enrutamiento convencional, el desarrollador es responsable de colocar rutas en el orden deseado.

Las rutas de atributo pueden configurar un orden usando la propiedad. Todos los atributos de ruta proporcionados por el marco incluyen . Las rutas se procesan de acuerdo con el orden ascendente de la propiedad. El orden predeterminado es . El establecimiento de una ruta utilizando [nombre del método] se ejecuta antes que las rutas que no establecen un orden. Si se configura una ruta usando runs, se ejecutará después de la ordenación predeterminada de rutas.

Evite depender de . Si el espacio de URL de una app requiere valores de orden explícitos para redirigir correctamente, es probable que también sea confuso para los clientes. Por lo general, el enrutamiento mediante atributos seleccionará la ruta correcta con la coincidencia de dirección URL. Si el orden predeterminado que se usa para la generación de direcciones URL no funciona, normalmente es más sencillo utilizar el nombre de ruta como una sobrescritura que aplicar la propiedad correspondiente.

Tenga en cuenta los dos controladores siguientes que definen la coincidencia de rutas.

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La solicitud mediante el código anterior produce una excepción similar a la siguiente:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Agregar a uno de los atributos de ruta resuelve la ambigüedad:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Con el código anterior, ejecuta el punto de conexión. Para llegar a , solicite . Note:

  • El código anterior es un ejemplo de diseño de enrutamiento deficiente. Ilustra la propiedad .
  • La propiedad solo resuelve la ambigüedad. Esa plantilla no puede coincidir. Es mejor quitar la plantilla.

Para obtener información sobre el orden de las rutas con Pages, consulte Convenciones de rutas y apps: Orden de rutas.

En algunos casos, se devuelve un error HTTP 500 con rutas ambiguas. Usa el registro de log para ver qué puntos de conexión provocaron el problema.

Reemplazo de tokens en plantillas de ruta [controller], [action], [area]

Para mayor comodidad, las rutas de atributo admiten el reemplazo de tokens mediante la inclusión de un token entre corchetes (, ). Los tokens , y se reemplazan por los valores del nombre de la acción, el nombre del área y el nombre del controlador de la acción donde se define la ruta:

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Coincidencias
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Coincidencias

El reemplazo de tokens se realiza como el último paso al crear las rutas de atributo. El ejemplo anterior se comporta igual que el código siguiente:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Si está leyendo esto en un idioma distinto del inglés, háganoslo saber en este GitHub problema de discusión si desea ver los comentarios de código en su idioma nativo.

También puede combinar rutas de atributo con herencia. Esta combinación es eficaz cuando se usa el reemplazo de tokens. El reemplazo de token también se aplica a los nombres de ruta definidos por rutas definidas por atributos. genera un nombre de ruta único para cada acción:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Para que el delimitador literal de reemplazo de token "(" o ")" coincida, repítalo, es decir, utilice el carácter "(" o ")" como una secuencia de escape.

Usar un transformador de parámetro para personalizar el reemplazo de tokens

Puede personalizar el reemplazo de tokens mediante un transformador de parámetros. Un transformador de parámetro implementa y transforma el valor de parámetros. Por ejemplo, un transformador de parámetros personalizado cambia el valor de ruta a :

using System.Text.RegularExpressions;

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string? TransformOutbound(object? value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString()!,
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

es una convención de modelo de aplicación que:

  • Aplique un transformador de parámetros a todas las rutas de atributo de una aplicación.
  • Personaliza los valores de los tokens de las rutas de atributos al ser reemplazados.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El método anterior coincide con .

El está registrado como opción:

using Microsoft.AspNetCore.Mvc.ApplicationModels;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(options =>
{
    options.Conventions.Add(new RouteTokenTransformerConvention(
                                 new SlugifyParameterTransformer()));
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Para obtener la definición de slug, consulte la documentación web de MDN sobre Slug.

Warning

Cuando se usa para procesar entradas que no son de confianza, pase un tiempo de expiración. Un usuario malintencionado puede proporcionar entradas que provoquen un ataque por denegación de servicio. APIs del marco ASP.NET Core que usan RegularExpressions para pasar un tiempo de espera.

Varias rutas de atributos

El enrutamiento mediante atributos permite definir varias rutas que llegan a la misma acción. El uso más común de esto es imitar el comportamiento de la ruta convencional predeterminada tal como se muestra en el ejemplo siguiente:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Poner múltiples atributos de ruta en el controlador significa que cada uno se combina con cada uno de los atributos de ruta en los métodos de acción:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Todas las restricciones de ruta de verbo HTTP implementan .

Cuando se colocan varios atributos de ruta que implementan en una acción:

  • Cada restricción de acción se combina con la plantilla de ruta aplicada al controlador.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El uso de varias rutas en acciones puede parecer útil y eficaz, es mejor mantener el espacio de direcciones URL de la aplicación básico y bien definido. Utilice varias rutas en acciones solo cuando sea necesario, por ejemplo, para apoyar a clientes existentes.

Especificación de parámetros opcionales de ruta de atributo, valores predeterminados y restricciones

Las rutas de atributo admiten la misma sintaxis en línea que las rutas convencionales para especificar parámetros opcionales, valores predeterminados y restricciones.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior, aplica una restricción de ruta. La acción solo coincidirá con rutas URL como . La parte de la plantilla de ruta restringe ese segmento a solo enteros.

Para obtener una descripción detallada de la sintaxis de la plantilla de ruta, consulte Referencia de plantilla de ruta.

Atributos de ruta personalizados mediante IRouteTemplateProvider

Todos los atributos de ruta implementan . El entorno de ejecución de ASP.NET Core:

  • Busca atributos en clases de controlador y métodos de acción cuando se inicia la aplicación.
  • Utiliza los atributos que implementan para construir el conjunto inicial de rutas.

Implemente para definir atributos de ruta personalizados. Cada le permite definir una única ruta con una plantilla de ruta, un orden y un nombre personalizados:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; } = string.Empty;
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El método anterior devuelve .

Uso del modelo de aplicación para personalizar las rutas de atributo

El modelo de aplicación:

  • Es un modelo de objetos creado al iniciar en .
  • Contiene todos los metadatos usados por ASP.NET Core para enrutar y ejecutar las acciones en una aplicación.

El modelo de aplicación incluye todos los datos recogidos de los atributos de ruta. La implementación proporciona los datos de los atributos de ruta. Conventions:

  • Se puede escribir para modificar el modelo de aplicación para personalizar el comportamiento del enrutamiento.
  • Se leen en el inicio de la aplicación.

En esta sección se muestra un ejemplo básico de personalización del enrutamiento mediante el modelo de aplicación. El código siguiente hace que las rutas se alinean aproximadamente con la estructura de carpetas del proyecto.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

El código siguiente impide que la convención se aplique a los controladores que están enrutados por atributos:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

Por ejemplo, el controlador siguiente no usa :

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

El método:

  • No hace nada si el controlador está enrutado por atributos.
  • Establece la plantilla de controlador basada en , con la base eliminada.

El [objeto/tecnología/dispositivo] puede aplicarse en:

using My.Application.Controllers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(options =>
{
    options.Conventions.Add(
     new NamespaceRoutingConvention(typeof(HomeController).Namespace!));
});

var app = builder.Build();

Por ejemplo, considere el siguiente controlador:

using Microsoft.AspNetCore.Mvc;

namespace My.Application.Admin.Controllers
{
    public class UsersController : Controller
    {
        // GET /admin/controllers/users/index
        public IActionResult Index()
        {
            var fullname = typeof(UsersController).FullName;
            var template = 
                ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
            var path = Request.Path.Value;

            return Content($"Path: {path} fullname: {fullname}  template:{template}");
        }

        public IActionResult List(int? id)
        {
            var path = Request.Path.Value;
            return Content($"Path: {path} ID:{id}");
        }
    }
}

En el código anterior:

  • La base es .
  • El nombre completo del controlador anterior es .
  • establece la plantilla de controladores en .

También se puede aplicar como un atributo en un controlador:

[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
    // /admin/controllers/test/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        var actionname = ControllerContext.ActionDescriptor.ActionName;
        return Content($"Action- {actionname} template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Enrutamiento mixto: enrutamiento mediante atributos frente a enrutamiento convencional

ASP.NET Core aplicaciones pueden mezclar el uso del enrutamiento convencional y el enrutamiento de atributos. Normalmente, se usan rutas convencionales para controladores que sirven páginas HTML a exploradores y enrutamiento de atributos para controladores que sirven a las API.

Las acciones pueden ser ruteadas ya sea por convención o por atributos. Cuando se coloca una ruta en el controlador o en la acción, se convierte en un enrutamiento por atributos. No se puede acceder a acciones que definan rutas de atributo a través de las rutas convencionales y viceversa. Cualquier atributo de ruta en el controlador hace que todas las acciones del controlador sean enrutadas.

El enrutamiento de atributos y el enrutamiento convencional usan el mismo motor de enrutamiento.

Enrutamiento con caracteres especiales

El enrutamiento con caracteres especiales puede dar lugar a resultados inesperados. Por ejemplo, considere un controlador con el siguiente método de acción:

[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null || todoItem.Name == null)
    {
        return NotFound();
    }

    return todoItem.Name;
}

Cuando contiene los siguientes valores codificados, pueden producirse resultados inesperados:

ASCII Encoded
/ %2F
+

Los parámetros de ruta no siempre están descodificados por URL. Este problema puede abordarse en el futuro. Para obtener más información, vea este GitHub problema;

Generación de direcciones URL y valores ambientales

Las aplicaciones pueden utilizar las funciones de generación de URL de enrutamiento para generar enlaces URL a las acciones. La generación de URL elimina la necesidad de codificarlas, lo que hace que el código sea más robusto y fácil de mantener. Esta sección se centra en las características de generación de direcciones URL proporcionadas por MVC y solo aborda los conceptos básicos de su funcionamiento. Consulte Enrutamiento para obtener una descripción detallada de la generación de direcciones URL.

La interfaz es la pieza subyacente de la infraestructura entre MVC y el enrutamiento para la generación de direcciones URL. Encontrará una instancia de disponible a través de la propiedad en los controladores, las vistas y los componentes de vista.

En el siguiente ejemplo, la interfaz se utiliza a través de la propiedad para generar una URL a otra acción.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Si la aplicación usa la ruta convencional predeterminada, el valor de la variable es la cadena de la ruta URL. El enrutamiento crea esta ruta de acceso url mediante la combinación de:

  • Los valores de ruta de la solicitud actual, que se denominan valores de ambiente.
  • Los valores pasados a y sustituyendo esos valores en la plantilla de ruta:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

En la plantilla de ruta, cada parámetro de ruta tiene su valor sustituido por nombres que coinciden con los valores y los valores ambientales. Un parámetro de ruta que no tiene un valor puede:

  • Use un valor predeterminado si tiene uno.
  • Se omitirá si es opcional. Por ejemplo, el de la plantilla de ruta .

La generación de URL falla si algún parámetro de ruta requerido no tiene un valor correspondiente. Si se produce un error en la generación de direcciones URL para una ruta, se prueba con la ruta siguiente hasta que se hayan probado todas las rutas o se encuentra una coincidencia.

El ejemplo anterior asume el enrutamiento convencional. La generación de URL funciona de forma similar al enrutamiento de atributos, aunque los conceptos son diferentes. Con enrutamiento convencional:

  • Los valores de ruta se usan para expandir una plantilla.
  • Los valores de ruta de y normalmente aparecen en esa plantilla. Esto funciona porque las direcciones URL coincidentes con el enrutamiento cumplen una convención.

En el ejemplo siguiente se usa el enrutamiento de atributos:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

La acción del código anterior genera .

LinkGenerator se agregó en ASP.NET Core 3.0 como alternativa a IUrlHelper. ofrece una funcionalidad similar pero más flexible. Cada método de tiene también una familia de métodos correspondiente.

Generación de direcciones URL por nombre de acción

Url.Action, LinkGenerator.GetPathByAction y todas las sobrecargas relacionadas están diseñadas para generar el punto de conexión de destino especificando un nombre de controlador y un nombre de acción.

Cuando se usa , el tiempo de ejecución proporciona los valores de ruta actuales para y .

  • Los valores de y forman parte tanto de los valores de ambiente como de los valores. El método siempre utiliza los valores actuales de y y genera una ruta de dirección URL que dirige a la acción actual.

El enrutamiento intenta utilizar los valores del entorno para rellenar la información que no se proporcionó al generar una URL. Considere una ruta, por ejemplo, con valores ambientales:

  • El enrutamiento tiene suficiente información para generar una dirección URL sin valores adicionales.
  • El enrutamiento tiene suficiente información porque todos los parámetros de ruta tienen un valor.

Si se añade el valor :

  • El valor se omite.
  • La ruta de acceso URL generada es .

Advertencia: Las rutas URL son jerárquicas. En el ejemplo anterior, si se agrega el valor :

  • Ambos valores se omiten.
  • Ya no hay ningún valor para y se produce un error en la generación de direcciones URL.
  • Debe especificar los valores deseados de y para generar una dirección URL.

Es posible que se produzca este problema con la ruta predeterminada. Este problema es poco frecuente en la práctica porque siempre especifica explícitamente un valor y .

Varias sobrecargas de Url.Action toman un objeto de valores de ruta para proporcionar valores para parámetros de ruta distintos de y . El objeto de valores de ruta se usa con frecuencia con . Por ejemplo: . Objeto de valores de ruta:

  • Por convención, normalmente es un objeto de tipo anónimo.
  • Puede ser un objeto o un POCO).

Los valores de ruta adicionales que no coinciden con los parámetros de ruta van en la cadena de consulta.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url!);
}

El código anterior genera .

El código siguiente genera una dirección URL absoluta:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url!);
}

Para crear una dirección URL absoluta, use una de las siguientes opciones:

  • Una sobrecarga que acepta un parámetro. Por ejemplo, el código anterior.
  • LinkGenerator.GetUriByAction, que genera URI absolutos de forma predeterminada.

Generación de direcciones URL por ruta

El código anterior muestra la generación de una URL pasando el controlador y el nombre de la acción. también proporciona la familia de métodos Url.RouteUrl. Estos métodos son similares a Url.Action, pero no copian los valores actuales de y a los valores de la ruta . El uso más común de :

  • Especifica un nombre de ruta para generar la dirección URL.
  • Por lo general, no especifica un nombre de controlador o acción.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

El siguiente archivo genera un vínculo HTML a :

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

Generar direcciones URL en HTML y

proporciona los métodos Html.BeginForm y Html.ActionLink para generar los elementos

y respectivamente. Estos métodos utilizan el método Url.Action para generar una URL y aceptan argumentos similares. Los métodos complementarios de son y , cuya funcionalidad es similar.

TagHelpers generan direcciones URL a través de la funcionalidad del TagHelper y TagHelper. Ambos usan para su implementación. Para obtener más información, vea Asistentes de etiquetas en formularios.

Dentro de las vistas, está disponible a través de la propiedad para cualquier generación de direcciones URL ad hoc no cubierta por los métodos anteriores.

Generación de direcciones URL en los resultados de acciones

En los ejemplos anteriores se muestra cómo usar en un controlador. El uso más común en un controlador es generar una URL como parte del resultado de una acción.

Las clases base y proporcionan métodos de conveniencia para los resultados de acción que hacen referencia a otra acción. Un uso típico es redirigir después de aceptar la entrada del usuario:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

Los métodos de fábrica de resultados de acciones, como los métodos en , siguen un patrón similar.

Caso especial para rutas convencionales dedicadas

El enrutamiento convencional puede utilizar un tipo especial de definición de ruta denominada ruta convencional dedicada. En el ejemplo siguiente, la ruta llamada es una ruta convencional dedicada:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

Con las definiciones de ruta anteriores, genera la ruta de URL mediante la ruta, pero ¿por qué? Puede adivinar que los valores de ruta son suficientes para generar una dirección URL mediante y el resultado sería .

Las rutas convencionales dedicadas dependen de un comportamiento especial de los valores predeterminados que no tienen un parámetro de ruta correspondiente, lo que impide que la ruta sea demasiado ambiciosa para la generación de URLs. En este caso, los valores predeterminados son , y ni ni aparecen como un parámetro de ruta. Cuando el enrutamiento realiza la generación de direcciones URL, los valores proporcionados deben coincidir con los valores predeterminados. La generación de la URL mediante [método/función] falla porque los valores no coinciden. Después, el enrutamiento vuelve para probar , operación que se realiza correctamente.

Areas

Las áreas son una característica MVC utilizada para organizar funcionalidades relacionadas en un grupo como separadas:

  • Espacio de nombres de enrutamiento para acciones del controlador.
  • Estructura de carpetas para vistas.

El uso de áreas permite que una aplicación tenga varios controladores con el mismo nombre, siempre que tengan áreas diferentes. El uso de áreas crea una jerarquía para el enrutamiento mediante la adición de otro parámetro de ruta, a y . En esta sección se describe cómo interactúa el enrutamiento con las áreas. Consulte Áreas para obtener más información sobre cómo se utilizan las áreas con las vistas.

En el ejemplo siguiente se configura MVC para usar la ruta predeterminada convencional y una ruta de área que se denomina:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{    
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

app.Run();

En el código anterior, se llama a la función para crear el . El segundo parámetro, , es el nombre del área.

Cuando coincide con una ruta de acceso de dirección URL como , la ruta genera los valores de ruta . El valor de ruta procede de un valor predeterminado para . La ruta creada por es equivalente al código siguiente:

app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

crea una ruta con un valor predeterminado y una restricción para mediante el nombre de área proporcionado, en este caso . El valor predeterminado garantiza que la ruta siempre produzca un resultado, y la restricción exige el valor para la generación de la URL.

El enrutamiento convencional depende del orden. En general, coloque rutas con áreas anteriores, ya que son más específicas que las rutas sin área.

Utilizando el ejemplo anterior, los valores de ruta coinciden con la siguiente acción:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

El atributo [Área] es el que indica que un controlador forma parte de un área. Este controlador está en el área. Los controladores sin un atributo no son miembros de ningún área y no coinciden cuando el enrutamiento proporciona el valor de ruta. En el ejemplo siguiente, solo el primer controlador enumerado puede coincidir con los valores de ruta .

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

Por completitud, aquí se muestra el espacio de nombres de cada controlador. Si los controladores anteriores usaban el mismo espacio de nombres, se generaría un error del compilador. Los espacios de nombres de clase no tienen ningún efecto en el enrutamiento de MVC.

Los dos primeros controladores son miembros de áreas específicas y solo coinciden cuando su respectivo nombre de área es proporcionado por el valor de ruta. El tercer controlador no es miembro de ningún área y solo puede coincidir cuando el enrutamiento no proporciona ningún valor para .

En términos de coincidencia de ningún valor, la ausencia del valor es igual que si el valor para fuese nulo o una cadena vacía.

Al ejecutar una acción en un área, el valor de ruta para estará disponible como un valor de ambiente para que el enrutamiento pueda usarlo en la generación de direcciones URL. Esto significa que, de forma predeterminada, las áreas actúan de forma adhesiva para la generación de direcciones URL, tal como se muestra en el ejemplo siguiente.

app.MapAreaControllerRoute(name: "duck_route",
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute(name: "default",
                             pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

El siguiente código genera una URL para :

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Definición de acción

Los métodos públicos de un controlador, excepto aquellos con el atributo NonAction, son acciones.

Código de ejemplo

  • MyDisplayRouteInfo es proporcionado por el paquete NuGet Rick.Docs.Samples.RouteInfo y muestra información de ruta.
  • Vea o descargue el código de ejemplo (cómo descargarlo)

Diagnósticos de depuración

Para obtener una salida detallada del diagnóstico de cálculo de rutas, establezca en . En el entorno, establezca el nivel de registro en :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

ASP.NET Core utiliza middleware de enrutamiento para coincidir las URLs de las solicitudes entrantes y mapearlas a acciones. Plantillas de ruta:

  • Se definen en el código de inicio o en los atributos.
  • Describir cómo se asocian las rutas URL con las acciones.
  • Se usan para generar direcciones URL para vínculos. Normalmente, los vínculos generados se devuelven en las respuestas.

Las acciones se enrutan bien mediante convención o bien mediante atributos. Colocar una ruta en el controlador o la acción hace que se enrute mediante atributos. Consulte Enrutamiento mixto para obtener más información.

Este documento:

  • Explica las interacciones entre MVC y el enrutamiento:
    • Cómo las aplicaciones MVC típicas usan las características de enrutamiento.
    • Cubre ambos:
      • El enrutamiento convencional se usa normalmente con controladores y vistas.
      • Enrutamiento de atributos usado con las API. Si está interesado principalmente en el enrutamiento de las API, consulte la sección Enrutamiento de atributos para API.
    • Consulte Enrutamiento para obtener más información sobre enrutamiento avanzado.
  • Hace referencia al sistema de enrutamiento predeterminado agregado en ASP.NET Core 3.0, denominado enrutamiento de puntos de conexión. Es posible usar controladores con la versión anterior de enrutamiento con fines de compatibilidad. Consulte la guía de migración 2.2-3.0 para obtener instrucciones. Consulte la versión 2.2 de este documento para obtener material de referencia sobre el sistema de enrutamiento heredado.

Configuración de una ruta convencional

Normalmente, tiene código similar al ejemplo siguiente al usar el enrutamiento convencional:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

Dentro de la llamada a , se utiliza para crear una única ruta. La ruta única se denomina ruta. La mayoría de las aplicaciones con controladores y vistas utilizan una plantilla de ruta similar a la ruta . Las API deben usar el enrutamiento de atributos.

La plantilla de ruta:

  • Coincide con una ruta URL como

  • Extrae los valores de ruta mediante la tokenización del camino. La extracción de valores de ruta da como resultado una coincidencia si la aplicación tiene un controlador denominado y una acción:

    public class ProductsController : Controller
    {
        public IActionResult Details(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }
    }
    

    MyDisplayRouteInfo es proporcionado por el paquete NuGet Rick.Docs.Samples.RouteInfo y muestra información de ruta.

  • model enlaza el valor de para establecer el parámetro en . Para obtener más información, vea Vinculación de modelos.

  • define como el valor predeterminado.

  • define como el valor predeterminado.

  • El caracter en define como opcional.

  • No es necesario que los parámetros de ruta opcionales y predeterminados estén presentes en la ruta de dirección URL para una coincidencia. Para obtener una descripción detallada de la sintaxis de la plantilla de ruta, consulte Referencia de plantilla de ruta.

  • Coincide con la ruta de acceso de la URL.

  • Genera valores de la ruta.

Los valores de y hacen uso de los valores predeterminados. no produce ningún valor ya que no hay ningún segmento correspondiente en la ruta URL. solo coincide si existe una acción y :

public class HomeController : Controller
{
  public IActionResult Index() { ... }
}

Con la definición del controlador anterior y la plantilla de ruta, la acción definida se ejecuta para las siguientes URL:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

La ruta de acceso de la URL usa la plantilla de ruta predeterminada para el controlador y la acción. La ruta URL utliza la acción por defecto de la plantilla de ruta.

El método de conveniencia :

endpoints.MapDefaultControllerRoute();

Replaces:

endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

Important

El enrutamiento se configura mediante el middleware , y . Para usar controladores:

  • Llame internamente para asignar los controladores enrutados por atributos.
  • Llame a o para asignar controladores enrutados convencionalmente y controladores enrutados de atributo .

Enrutamiento convencional

Use el enrutamiento convencional con controladores y vistas. La ruta es:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

El código anterior es un ejemplo de una ruta convencional. Se denomina enrutamiento convencional porque establece una convención para las rutas URL:

  • El primer segmento de la ruta, , asigna el nombre del controlador.
  • El segundo segmento, , asigna el nombre de la acción.
  • El tercer segmento se usa para un parámetro opcional. El en lo hace opcional. se asigna a una entidad de modelo.

Con esta ruta, la ruta de URL:

  • se asigna a la acción.
  • El mapeo se realiza a y el modelo normalmente vincula el parámetro a 17.

Este mapeo:

  • Se basa en los nombres de controlador y acción solamente.
  • No se basa en espacios de nombres, ubicaciones de archivos de origen ni parámetros de método.

Al usar el enrutamiento convencional con la ruta predeterminada, no es necesario crear un nuevo patrón de dirección URL para cada acción. Para una aplicación con acciones de estilo CRUD, mantener la coherencia en las URLs a través de los controladores:

  • Ayuda a simplificar el código.
  • Hace que la interfaz de usuario sea más predecible.

Warning

La plantilla de ruta define [el elemento] como opcional. Las acciones se pueden ejecutar sin el identificador opcional proporcionado como parte de la dirección URL. Por lo general, cuando se omite de la URL:

  • El enlace de modelos establece el objeto al valor especificado.
  • No se encuentra ninguna entidad en la base de datos que coincida con .

El enrutamiento de atributos proporciona un control preciso para hacer que el ID sea necesario para algunas acciones y no para otras. Por convención, la documentación incluirá parámetros opcionales como cuando sea más probable que su uso sea correcto.

La mayoría de las aplicaciones deben elegir un esquema de enrutamiento básico y descriptivo para que las direcciones URL sean legibles y significativas. La ruta convencional predeterminada :

  • Admite un esquema de enrutamiento básico y descriptivo.
  • Se trata de un punto de partida útil para las aplicaciones basadas en la interfaz de usuario.
  • Es la única plantilla de ruta necesaria para muchas aplicaciones de interfaz de usuario web. Para aplicaciones de interfaz de usuario web de mayor tamaño, a menudo basta con utilizar Areas.

y :

  • Asignar automáticamente un valor de orden a sus puntos finales en función del orden en que son invocados.

Enrutamiento de punto de conexión en ASP.NET Core 3.0 o posterior:

  • No tiene un concepto de rutas.
  • No proporciona garantías de ordenación para la ejecución de la extensibilidad. Todos los puntos de conexión se procesan a la vez.

Habilite el registro para ver de qué forma las implementaciones de enrutamiento integradas, como , coinciden con las solicitudes.

El enrutamiento de atributos se explica más adelante en este documento.

Varias rutas convencionales

Se pueden añadir múltiples rutas convencionales dentro de añadiendo más llamadas a y . Hacerlo permite definir múltiples convenciones, o añadir rutas convencionales que se dedican a una acción específica , como:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

La ruta del código anterior es una ruta convencional dedicada. Es una ruta convencional dedicada porque:

  • Utiliza el enrutamiento convencional.
  • Se dedica a una acción específica.

Dado que la plantilla de ruta no incluye ni como parámetros:

  • Solo pueden tener los valores predeterminados .
  • Esta ruta siempre se asigna a la acción .

, y son las únicas rutas de dirección URL que coinciden con la ruta del blog.

En el ejemplo anterior:

  • La ruta tiene una prioridad más alta para las coincidencias que la ruta porque la agrega primero.
  • Es un ejemplo de enrutamiento de estilo slug donde es habitual tener un nombre de artículo como parte de la dirección URL.

Warning

En ASP.NET Core 3.0 o posterior, el enrutamiento no:

  • Defina un concepto denominado ruta. agrega coincidencia de rutas a la canalización de middleware. El middleware examina el conjunto de puntos finales definidos en la aplicación y selecciona el mejor punto final en función de la solicitud.
  • Proporcione garantías sobre el orden de ejecución de extensibilidad como o .

Consulte Enrutamiento para obtener material de referencia sobre el enrutamiento.

Orden de enrutamiento convencional

El enrutamiento convencional solo coincide con una combinación de acción y controlador que define la aplicación. Este enfoque simplifica los casos en los que las rutas convencionales se superponen. Al agregar rutas mediante , y , los puntos de conexión obtienen automáticamente un valor de pedido en función del orden en el que se invocan estos métodos. Las coincidencias de una ruta que aparece anteriormente en la lista tienen una prioridad mayor. El enrutamiento convencional depende del orden. En general, coloque rutas con áreas anteriores porque son más específicas que las rutas sin área. Rutas convencionales dedicadas con parámetros de ruta catch-all, como ", pueden hacer que una ruta sea demasiado voraz. Una ruta codiciosa coincide con direcciones URL que usted pretendía que coincidieran con otras rutas. Coloque las rutas codiciosas más adelante en la tabla de rutas para evitar coincidencias codiciosas.

Warning

Un parámetro catch-all puede relacionar rutas de forma incorrecta debido a un error en el enrutamiento. Las aplicaciones afectadas por este error tienen las características siguientes:

  • Una ruta global, por ejemplo,
  • La ruta catch-all causa un error al relacionar solicitudes que sí que debería relacionar.
  • Al eliminar otras rutas, la ruta catch-all comienza a funcionar.

Consulte los errores de GitHub 18677 y 16579 para casos de ejemplo en los que se produce este error.

Una corrección opcional para este error se incluye en el SDK de .NET Core 3.1.301 o posterior. En el código que hay a continuación se establece un cambio interno que corrige este error:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Resolución de acciones ambiguas

Cuando dos puntos de conexión coinciden a través del enrutamiento, el enrutamiento debe realizar uno de los pasos siguientes:

  • Elija el mejor candidato.
  • Lanzar una excepción.

Por ejemplo:

    public class Products33Controller : Controller
    {
        public IActionResult Edit(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }

        [HttpPost]
        public IActionResult Edit(int id, Product product)
        {
            return ControllerContext.MyDisplayRouteInfo(id, product.name);
        }
    }
}

El controlador anterior define dos acciones que coinciden con:

  • Camino de la URL
  • Datos de ruta .

Este es un patrón típico para los controladores MVC:

  • muestra un formulario para editar un producto.
  • procesa el formulario publicado.

Para resolver la ruta correcta:

  • es seleccionada cuando la solicitud es una HTTP.
  • se selecciona cuando el verbo HTTP es cualquier otra cosa. Generalmente se invoca mediante .

, se proporciona al enrutamiento para que pueda elegir en función del método HTTP de la solicitud. El [elementoA] hace una mejor combinación que [elementoB].

Es importante comprender el rol de atributos como . Los atributos similares se definen para otros verbos HTTP. En el enrutamiento convencional, es común que las acciones utilicen el mismo nombre de acción cuando son parte de un flujo de trabajo de mostrar formulario y enviar formulario. Por ejemplo, examina los dos métodos de acción "Editar".

Si el enrutamiento no puede elegir el mejor candidato, lanza una excepción y enumera los varios puntos de conexión coincidentes.

Nombres de ruta convencionales

Las cadenas y en los siguientes ejemplos son nombres de ruta convencionales:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

Los nombres de ruta asignan un nombre lógico a la ruta. La ruta con nombre puede utilizarse para la generación de URL. El uso de una ruta con nombre simplifica la creación de URL cuando el orden de las rutas podría complicar la generación de URL. Los nombres de ruta deben ser únicos en toda la aplicación.

Nombres de ruta:

  • No tiene ningún impacto en la coincidencia de direcciones URL ni en el control de las solicitudes.
  • Solo se usan para la generación de direcciones URL.

El concepto de nombre de ruta se representa en el enrutamiento como IEndpointNameMetadata. Los términos nombre de ruta y nombre del punto de conexión:

  • Son intercambiables.
  • El uso en la documentación y el código depende de la API que se está describiendo.

Enrutamiento de atributos para las API

Las API deben usar el enrutamiento mediante atributos para modelar la funcionalidad de la aplicación como un conjunto de recursos donde las operaciones se representan mediante verbos HTTP.

El enrutamiento mediante atributos utiliza un conjunto de atributos para asignar acciones directamente a las plantillas de ruta. El siguiente código es típico para una API y se utiliza en el siguiente ejemplo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

En el código anterior, se invoca a una función dentro de otra para mapear controladores con enrutamiento por atributo.

En el ejemplo siguiente:

  • coincide con un conjunto de URL similares a las que coincide la ruta convencional por defecto.
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La acción se ejecuta para cualquiera de las rutas de acceso de dirección URL , , , o .

Este ejemplo resalta una diferencia clave de programación entre el enrutamiento mediante atributos y el enrutamiento convencional. El enrutamiento de atributos requiere más entrada para especificar una ruta. La ruta predeterminada convencional controla las rutas más concisamente. Pero el enrutamiento mediante atributos permite (y requiere) un control preciso de las plantillas de ruta que se aplicarán a cada acción.

Con el enrutamiento por atributos, los nombres del controlador y la acción no influyen en qué acción se coincide, a menos que se utilice la sustitución de tokens. En el ejemplo siguiente se coinciden las mismas direcciones URL que el ejemplo anterior:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código siguiente se usa el reemplazo de tokens para y :

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El siguiente código se aplica al controlador:

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

En el código anterior, las plantillas de método deben anteponer o a las plantillas de ruta. Las plantillas de ruta aplicadas a una acción que comienzan con «[placeholder1]» o «[placeholder2]» no se combinan junto con las plantillas de ruta que se aplican al controlador.

Consulte Precedencia de la plantilla de ruta para obtener información sobre la selección de plantillas de ruta.

Nombres de enrutamiento reservados

Las palabras clave siguientes son nombres de parámetros de ruta reservados cuando se usan Controladores o Páginas:

  • action
  • area
  • controller
  • handler
  • page

El uso de como parámetro de ruta con enrutamiento de atributos es un error común. Esta opción da como resultado un comportamiento incoherente y confuso con la generación de direcciones URL.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

La generación de direcciones URL usa estos nombres de parámetros especiales para determinar si una operación de generación de direcciones URL hace referencia a una página o a un controlador.

Las siguientes palabras clave están reservadas en el contexto de una vista o una página:

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

No use estas palabras clave para las generaciones de vínculos, los parámetros enlazados al modelo ni las propiedades de nivel superior.

Plantillas de verbo HTTP

ASP.NET Core incluye las siguientes plantillas de verbo HTTP:

  • [HttpGet]
  • [HttpPost]
  • [HttpPut]
  • [HttpDelete]
  • [HttpHead]
  • [HttpPatch]

Plantillas de ruta

ASP.NET Core incluye las siguientes plantillas de ruta:

  • Todas las plantillas de verbo HTTP son plantillas de ruta.
  • [Route]

Enrutamiento de atributos con atributos de verbo HTTP

Considere el siguiente controlador:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior:

  • Cada acción contiene el atributo , que restringe la coincidencia solo con las solicitudes HTTP GET.
  • La acción incluye la plantilla, por lo que se anexa a la plantilla en el controlador. La plantilla del método es . Por lo tanto, esta acción solo coincide con las solicitudes GET para el formulario , , , etc.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • La acción contiene la plantilla. La parte de la plantilla restringe los valores de ruta a cadenas que se pueden convertir en un entero. Una solicitud GET a:
    • No coincide con esta acción.
    • Devuelve un error 404 No encontrado.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • La acción contiene elementos en la plantilla, pero no limita estos valores a aquellos que se pueden convertir en un entero. Una solicitud GET a:
    • Coincide con esta ruta.
    • El enlace de modelos no logra convertir a un entero. El parámetro del método es entero.
    • Devuelve un error 400 Solicitud incorrecta porque el enlace de modelos no pudo convertir en un entero.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

El enrutamiento por atributos puede utilizar atributos como , y . Todos los atributos del verbo HTTP aceptan una plantilla de ruta. Este ejemplo muestra dos acciones que coinciden con la misma plantilla de ruta:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Usando la ruta URL:

  • La acción se ejecuta cuando el verbo HTTP es .
  • La acción se ejecuta cuando el verbo HTTP es .

Cuando construyes una API, es raro que necesites utilizarla en el método de una acción porque esta acepta todos los métodos HTTP. Es mejor usar el atributo de verbo HTTP más específico para precisar qué admite tu API. Se espera que los clientes de las API de sepan qué rutas y verbos HTTP corresponden a operaciones lógicas específicas.

Las API deben usar el enrutamiento mediante atributos para modelar la funcionalidad de la aplicación como un conjunto de recursos donde las operaciones se representan mediante verbos HTTP. Este diseño significa que muchas operaciones, como GET y POST en el mismo recurso lógico, usan la misma dirección URL. El enrutamiento de atributos proporciona el nivel de control necesario para diseñar cuidadosamente el diseño del punto de conexión público de una API.

Puesto que una ruta de atributo se aplica a una acción específica, es fácil crear parámetros necesarios como parte de la definición de plantilla de ruta. En este ejemplo, se requiere como parte de la ruta de URL:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La acción :

  • Se ejecuta con la ruta URL, como
  • No se ejecuta con la ruta de URL.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Para obtener más información, consulte Definir los tipos de contenido de solicitud compatibles con el atributo Consumes.

Consulte Enrutamiento para obtener una descripción completa de las plantillas de ruta y las opciones relacionadas.

Para obtener más información sobre , vea Atributo ApiController.

Nombre de ruta

El siguiente código define un nombre de ruta :

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Use nombres de ruta para generar una dirección URL basada en una ruta específica. Nombres de ruta:

  • No tienen ningún impacto en el comportamiento de coincidencia de URL del enrutamiento.
  • Solo se usan para la generación de direcciones URL.

Los nombres de ruta deben ser únicos en toda la aplicación.

Contrasta el código anterior con la ruta por defecto convencional, que define el parámetro como opcional (). Esta capacidad de especificar con precisión las API tiene sus ventajas, como permitir que y se envíen a acciones diferentes.

Combinando rutas de atributos

Para que el enrutamiento de atributos sea menos repetitivo, combine atributos de ruta en el controlador con atributos de ruta en las acciones individuales. Las plantillas de ruta que defina en el controlador se anteponen a las plantillas de ruta definidas en las acciones. Al colocar un atributo de ruta en el controlador, todas las acciones del controlador usan el enrutamiento de atributos.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el ejemplo anterior:

  • La ruta URL puede coincidir con
  • La ruta URL puede coincidir con .

Ambas acciones sólo coinciden con HTTP porque están marcadas con el atributo .

Plantillas de ruta que se aplican a una acción y que comienzan por o no se combinan con plantillas de ruta que se aplican al controlador. El siguiente ejemplo coincide con un conjunto de rutas URL similares a la ruta por defecto.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

En la tabla siguiente se explican los atributos del código anterior:

Attribute Combina con Define la plantilla de ruta
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Orden de rutas por atributos

El enrutamiento crea un árbol y compara todos los puntos de conexión simultáneamente.

  • Las entradas de ruta se comportan como si se colocaran en un orden ideal.
  • Las rutas más específicas tienen la oportunidad de ejecutarse antes que las rutas más generales.

Por ejemplo, una ruta de atributos como es más específica que una ruta de atributos como . La ruta tiene una prioridad más alta, de forma predeterminada, porque es más específica. En el enrutamiento convencional, el desarrollador es responsable de colocar las rutas en el orden deseado.

Las rutas de atributo pueden configurar un orden mediante la propiedad. Todos los atributos de ruta proporcionados por el marco incluyen . Las rutas se procesan de acuerdo con el orden ascendente de la propiedad. El orden predeterminado es . El establecimiento de una ruta utilizando procesos se ejecuta antes que las rutas que no establecen un orden. Si una ruta se configura con , se ejecutará después del orden de rutas predeterminado.

Evite depender de . Si su espacio de URL requiere valores de orden explícitos para un enrutamiento correcto, es probable que también sea confuso para los clientes. Por lo general, el enrutamiento mediante atributos seleccionará la ruta correcta con la coincidencia de dirección URL. Si el orden predeterminado que se usa para la generación de direcciones URL no funciona, normalmente es más sencillo utilizar el nombre de ruta como una anulación en lugar de aplicar la propiedad.

Tenga en cuenta los dos controladores siguientes que definen la correspondencia de la ruta:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

La solicitud mediante el código anterior produce una excepción similar a la siguiente:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Agregar a uno de los atributos de ruta resuelve la ambigüedad:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Con el código anterior, ejecuta el punto de conexión. Para llegar a , solicite . Note:

  • El código anterior es un ejemplo de diseño de enrutamiento deficiente. Ilustra la propiedad .
  • La propiedad solo resuelve la ambigüedad. Esa plantilla no puede coincidir. Es mejor quitar la plantilla.

Para obtener información sobre el orden de ruta con Pages, consulte Enrutamiento de páginas y convenciones de aplicación: Orden de ruta.

En algunos casos, se devuelve un error HTTP 500 con rutas ambiguas. Utiliza registros para ver qué extremos causaron el problema.

Reemplazo de tokens en plantillas de ruta [controller], [action], [area]

Para mayor comodidad, las rutas de atributo admiten el reemplazo de tokens mediante la inclusión de un token entre corchetes (, ). Los tokens , y se reemplazan por los valores del nombre de la acción, el nombre del área y el nombre del controlador de la acción donde se define la ruta:

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Coincidencias
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Coincidencias

El reemplazo de tokens se produce como el último paso para construir las rutas de atributos. El ejemplo anterior se comporta igual que el código siguiente:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Si está leyendo esto en un idioma distinto del inglés, háganoslo saber en este GitHub problema de discusión si desea ver los comentarios de código en su idioma nativo.

También puede combinar rutas de atributo con herencia. Esta combinación es eficaz cuando se usa el reemplazo de tokens. El reemplazo de token también se aplica a los nombres de ruta definidos mediante rutas por atributo. genera un nombre de ruta único para cada acción:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Para que el delimitador de reemplazo de token o coincida, repita el carácter ( o ) para usarlo como secuencia de escape.

Usar un transformador de parámetro para personalizar el reemplazo de tokens

Puede personalizar el reemplazo de tokens mediante un transformador de parámetros. Un transformador de parámetro implementa y transforma el valor de parámetros. Por ejemplo, un transformador de parámetros personalizado cambia el valor de ruta a :

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString(),
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

es una convención de modelo de aplicación que:

  • Aplique un transformador de parámetros a todas las rutas de atributo de una aplicación.
  • Personaliza los valores de los tokens de las rutas de atributos al ser reemplazados.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El método anterior coincide con .

está registrado como una opción en .

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(
                                     new SlugifyParameterTransformer()));
    });
}

Para obtener la definición de slug, consulte la documentación web de MDN sobre Slug.

Warning

Cuando se usa para procesar entradas que no son de confianza, pase un tiempo de expiración. Un usuario malintencionado puede proporcionar entradas que provoquen un ataque por denegación de servicio. APIs del marco ASP.NET Core que usan RegularExpressions para pasar un tiempo de espera.

Varias rutas de atributos

El enrutamiento mediante atributos permite definir varias rutas que llegan a la misma acción. El uso más común de esto es imitar el comportamiento de la ruta convencional predeterminada tal como se muestra en el ejemplo siguiente:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Poner múltiples atributos de ruta en el controlador significa que cada uno se combina con cada uno de los atributos de ruta en los métodos de acción:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Todas las restricciones de ruta de verbo HTTP implementan .

Cuando se colocan varios atributos de ruta que implementan en una acción:

  • Cada restricción de acción se combina con la plantilla de ruta aplicada al controlador.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El uso de varias rutas en acciones puede parecer útil y eficaz, es mejor mantener el espacio de direcciones URL de la aplicación básico y bien definido. Utilice varias rutas en acciones solo cuando sea necesario, por ejemplo, para apoyar a clientes existentes.

Especificación de parámetros opcionales de ruta de atributo, valores predeterminados y restricciones

Las rutas de atributo admiten la misma sintaxis en línea que las rutas convencionales para especificar parámetros opcionales, valores predeterminados y restricciones.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

En el código anterior, aplica una restricción de ruta. La acción solo coincidirá con rutas URL como . La parte de la plantilla de ruta restringe ese segmento a solo enteros.

Para obtener una descripción detallada de la sintaxis de la plantilla de ruta, consulte Referencia de plantilla de ruta.

Atributos de ruta personalizados mediante IRouteTemplateProvider

Todos los atributos de ruta implementan . El entorno de ejecución de ASP.NET Core:

  • Busca atributos en clases de controlador y métodos de acción cuando se inicia la aplicación.
  • Utiliza los atributos que implementan para construir el conjunto inicial de rutas.

Implemente para definir atributos de ruta personalizados. Cada le permite definir una única ruta con una plantilla de ruta, un orden y un nombre personalizados:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; }
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

El método anterior devuelve .

Uso del modelo de aplicación para personalizar las rutas de atributo

El modelo de aplicación:

  • Es un modelo de objetos creado en el inicio.
  • Contiene todos los metadatos usados por ASP.NET Core para enrutar y ejecutar las acciones en una aplicación.

El modelo de aplicación incluye todos los datos recogidos de los atributos de ruta. La implementación proporciona los datos de los atributos de ruta. Conventions:

  • Se puede escribir para modificar el modelo de aplicación para personalizar el comportamiento del enrutamiento.
  • Se leen en el inicio de la aplicación.

En esta sección se muestra un ejemplo básico de personalización del enrutamiento mediante el modelo de aplicación. El código siguiente hace que las rutas se alinean aproximadamente con la estructura de carpetas del proyecto.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

El código siguiente impide que la convención se aplique a los controladores que están enrutados por atributos:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

Por ejemplo, el controlador siguiente no usa :

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

El método:

  • No hace nada si el controlador está enrutado por atributos.
  • Establece la plantilla de controlador basada en , con la base eliminada.

El [objeto/tecnología/dispositivo] puede aplicarse en:

namespace My.Application
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(options =>
            {
                options.Conventions.Add(
                    new NamespaceRoutingConvention(typeof(Startup).Namespace));
            });
        }
        // Remaining code ommitted for brevity.

Por ejemplo, considere el siguiente controlador:

using Microsoft.AspNetCore.Mvc;

namespace My.Application.Admin.Controllers
{
    public class UsersController : Controller
    {
        // GET /admin/controllers/users/index
        public IActionResult Index()
        {
            var fullname = typeof(UsersController).FullName;
            var template = 
                ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
            var path = Request.Path.Value;

            return Content($"Path: {path} fullname: {fullname}  template:{template}");
        }

        public IActionResult List(int? id)
        {
            var path = Request.Path.Value;
            return Content($"Path: {path} ID:{id}");
        }
    }
}

En el código anterior:

  • La base es .
  • El nombre completo del controlador anterior es .
  • establece la plantilla de controladores en .

También se puede aplicar como un atributo en un controlador:

[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
    // /admin/controllers/test/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        var actionname = ControllerContext.ActionDescriptor.ActionName;
        return Content($"Action- {actionname} template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Enrutamiento mixto: enrutamiento mediante atributos frente a enrutamiento convencional

ASP.NET Core aplicaciones pueden mezclar el uso del enrutamiento convencional y el enrutamiento de atributos. Normalmente, se usan rutas convencionales para controladores que sirven páginas HTML a exploradores y enrutamiento de atributos para controladores que sirven a las API.

Las acciones pueden ser ruteadas ya sea por convención o por atributos. Colocar una ruta en el controlador o la acción hace que se enrute mediante atributos. Las acciones que definen rutas de atributo no se pueden alcanzar a través de las rutas convencionales y viceversa. Cualquier atributo de ruta en el controlador hace que todas las acciones del controlador sean enrutadas.

El enrutamiento de atributos y el enrutamiento convencional usan el mismo motor de enrutamiento.

Generación de direcciones URL y valores ambientales

Las aplicaciones pueden utilizar las funciones de generación de URL de enrutamiento para generar enlaces URL a las acciones. La generación de URL elimina la codificación de las mismas, lo que hace que el código sea más robusto y fácil de mantener.Generating URLs eliminates hardcoding URLs, making code more robust and maintainable. Esta sección se centra en las características de generación de direcciones URL proporcionadas por MVC y solo aborda los conceptos básicos de su funcionamiento. Consulte Enrutamiento para obtener una descripción detallada de la generación de direcciones URL.

La interfaz es la pieza subyacente de la infraestructura entre MVC y el enrutamiento para la generación de direcciones URL. Encontrará una instancia de disponible a través de la propiedad en los controladores, las vistas y los componentes de vista.

En el siguiente ejemplo, la interfaz se utiliza a través de la propiedad para generar una URL a otra acción.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Si la aplicación usa la ruta convencional predeterminada, el valor de la variable es la cadena de la ruta URL. El enrutamiento crea esta ruta de acceso url mediante la combinación de:

  • Los valores de ruta de la solicitud actual, que se denominan valores de ambiente.
  • Los valores pasados a y sustituyendo esos valores en la plantilla de ruta:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

En la plantilla de ruta, cada parámetro de ruta tiene su valor sustituido por nombres que coinciden con los valores y los valores ambientales. Un parámetro de ruta que no tiene un valor puede:

  • Use un valor predeterminado si tiene uno.
  • Se omitirá si es opcional. Por ejemplo, el de la plantilla de ruta .

La generación de URL falla si algún parámetro de ruta requerido no tiene un valor correspondiente. Si se produce un error en la generación de direcciones URL para una ruta, se prueba con la ruta siguiente hasta que se hayan probado todas las rutas o se encuentra una coincidencia.

El ejemplo anterior asume el enrutamiento convencional. La generación de URL funciona de forma similar al enrutamiento de atributos, aunque los conceptos son diferentes. Con enrutamiento convencional:

  • Los valores de ruta se usan para expandir una plantilla.
  • Los valores de ruta de y normalmente aparecen en esa plantilla. Esto funciona porque las direcciones URL coincidentes con el enrutamiento cumplen una convención.

En el ejemplo siguiente se usa el enrutamiento de atributos:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

La acción del código anterior genera .

LinkGenerator se agregó en ASP.NET Core 3.0 como alternativa a IUrlHelper. ofrece una funcionalidad similar pero más flexible. Cada método de tiene también una familia de métodos correspondiente.

Generación de direcciones URL por nombre de acción

Url.Action, LinkGenerator.GetPathByAction y todas las sobrecargas relacionadas están diseñadas para generar el punto de conexión de destino especificando un nombre de controlador y un nombre de acción.

Cuando se usa , el tiempo de ejecución proporciona los valores de ruta actuales para y .

  • Los valores de y forman parte tanto de los valores de ambiente como de los valores. El método siempre utiliza los valores actuales de y y genera una ruta de dirección URL que dirige a la acción actual.

El enrutamiento intenta utilizar los valores del entorno para rellenar la información que no se proporcionó al generar una URL. Considere una ruta, por ejemplo, con valores ambientales:

  • El enrutamiento tiene suficiente información para generar una dirección URL sin valores adicionales.
  • El enrutamiento tiene suficiente información porque todos los parámetros de ruta tienen un valor.

Si se añade el valor :

  • El valor se omite.
  • La ruta de acceso URL generada es .

Advertencia: Las rutas URL son jerárquicas. En el ejemplo anterior, si se agrega el valor :

  • Ambos valores se omiten.
  • Ya no hay ningún valor y se produce un error en la generación de URL.
  • Debe especificar los valores deseados de y para generar una dirección URL.

Es posible que se produzca este problema con la ruta predeterminada. Este problema es poco frecuente en la práctica porque siempre se especifica explícitamente un valor A y un valor B.

Varias sobrecargas de Url.Action toman un objeto de valores de ruta para asignar valores a parámetros de ruta distintos de y . El objeto de valores de ruta se usa con frecuencia con . Por ejemplo: . Objeto de valores de ruta:

  • Por convención, normalmente es un objeto de tipo anónimo.
  • Puede ser un objeto o un POCO.

Los valores de ruta adicionales que no coinciden con los parámetros de ruta van en la cadena de consulta.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url);
}

El código anterior genera .

El código siguiente genera una dirección URL absoluta:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url);
}

Para crear una dirección URL absoluta, use una de las siguientes opciones:

  • Una sobrecarga que acepta un objeto . Por ejemplo, el código anterior.
  • LinkGenerator.GetUriByAction, que genera URI absolutos de forma predeterminada.

Generación de direcciones URL por ruta

El código anterior demuestra cómo generar una URL pasando el nombre del controlador y de la acción. también proporciona la familia de métodos Url.RouteUrl. Estos métodos son similares a Url.Action, pero no copian los valores actuales de y a los valores de la ruta . El uso más común de :

  • Especifica un nombre de ruta para generar la dirección URL.
  • Por lo general, no especifica un nombre de controlador o acción.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

El siguiente archivo genera un vínculo HTML a :

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

Generar direcciones URL en HTML y

proporciona los métodos Html.BeginForm y Html.ActionLink para generar los elementos de formulario y enlace respectivamente. Estos métodos utilizan el método Url.Action para generar una URL y aceptan argumentos similares. Los métodos complementarios de son y , cuya funcionalidad es similar.

Los TagHelpers generan direcciones URL a través del TagHelper específico y del TagHelper correspondiente. Ambos usan para su implementación. Para obtener más información, vea Asistentes de etiquetas en formularios.

Dentro de las vistas, está disponible a través de la propiedad para cualquier generación de direcciones URL ad hoc no cubierta por los métodos anteriores.

Generación de direcciones URL en los resultados de acciones

En los ejemplos anteriores se muestra cómo usar en un controlador. El uso más común en un controlador es generar una URL como parte del resultado de una acción.

Las clases base y proporcionan métodos de conveniencia para los resultados de acción que hacen referencia a otra acción. Un uso típico es redirigir después de aceptar la entrada del usuario:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

Los métodos de fábrica de resultados de acciones, como los métodos en , siguen un patrón similar.

Caso especial para rutas convencionales dedicadas

El enrutamiento convencional puede utilizar un tipo especial de definición de ruta denominada ruta convencional dedicada. En el ejemplo siguiente, la ruta llamada es una ruta convencional dedicada:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

Con las definiciones de ruta anteriores, genera la ruta de URL mediante la ruta, pero ¿por qué? Puede adivinar que los valores de ruta son suficientes para generar una dirección URL mediante y el resultado sería .

Las rutas convencionales dedicadas dependen de un comportamiento especial de los valores predeterminados que no tienen un parámetro de ruta correspondiente, lo que impide que la ruta sea demasiado ambiciosa para la generación de URLs. En este caso, los valores predeterminados son , y ni ni aparecen como un parámetro de ruta. Cuando el enrutamiento realiza la generación de direcciones URL, los valores proporcionados deben coincidir con los valores predeterminados. La generación de la URL mediante [método/función] falla porque los valores no coinciden. Después, el enrutamiento vuelve para probar , operación que se realiza correctamente.

Areas

Las áreas son una característica MVC utilizada para organizar funcionalidades relacionadas en un grupo como separadas:

  • Espacio de nombres de enrutamiento para acciones del controlador.
  • Estructura de carpetas para vistas.

El uso de áreas permite que una aplicación tenga varios controladores con el mismo nombre, siempre que tengan áreas diferentes. El uso de áreas crea una jerarquía para el enrutamiento mediante la adición de otro parámetro de ruta, a y . En esta sección se describe cómo interactúa el enrutamiento con las áreas. Consulte Áreas para obtener más información sobre cómo se utilizan las áreas con las vistas.

En el ejemplo siguiente se configura MVC para usar la ruta predeterminada convencional y una ruta de área que se denomina:

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

En el código anterior, se llama a la función para crear el . El segundo parámetro, , es el nombre del área.

Cuando coincide con una ruta de acceso de dirección URL como , la ruta genera los valores de ruta . El valor de ruta procede de un valor predeterminado para . La ruta creada por es equivalente al código siguiente:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

crea una ruta con un valor predeterminado y una restricción para mediante el nombre de área proporcionado, en este caso . El valor predeterminado garantiza que la ruta siempre produzca un resultado, y la restricción exige el valor para la generación de la URL.

El enrutamiento convencional depende del orden. En general, coloque rutas con áreas anteriores, ya que son más específicas que las rutas sin área.

Utilizando el ejemplo anterior, los valores de ruta coinciden con la siguiente acción:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

El atributo [Área] es el que indica que un controlador forma parte de un área. Este controlador está en el área. Los controladores sin un atributo no son miembros de ningún área y no coinciden cuando el enrutamiento proporciona el valor de ruta. En el ejemplo siguiente, solo el primer controlador enumerado puede coincidir con los valores de ruta .

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

Por completitud, aquí se muestra el espacio de nombres de cada controlador. Si los controladores anteriores usaban el mismo espacio de nombres, se generaría un error del compilador. Los espacios de nombres de clase no tienen ningún efecto en el enrutamiento de MVC.

Los dos primeros controladores son miembros de áreas específicas y solo coinciden cuando su respectivo nombre de área es proporcionado por el valor de ruta. El tercer controlador no es miembro de ningún área y solo puede coincidir cuando el enrutamiento no proporciona ningún valor para .

En términos de coincidencia de ningún valor, la ausencia del valor es igual que si el valor para fuese nulo o una cadena vacía.

Al ejecutar una acción en un área, el valor de ruta para estará disponible como un valor de ambiente para que el enrutamiento pueda usarlo en la generación de direcciones URL. Esto significa que, de forma predeterminada, las áreas actúan de forma adhesiva para la generación de direcciones URL, tal como se muestra en el ejemplo siguiente.

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute(name: "duck_route", 
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute(name: "default",
                                 pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

El siguiente código genera una URL para :

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Definición de acción

Los métodos públicos de un controlador, excepto aquellos con el atributo NonAction, son acciones.

Código de ejemplo

  • MyDisplayRouteInfo es proporcionado por el paquete NuGet Rick.Docs.Samples.RouteInfo y muestra información de ruta.
  • Vea o descargue el código de ejemplo (cómo descargarlo)

Diagnósticos de depuración

Para obtener una salida detallada del diagnóstico de cálculo de rutas, establezca en . En el entorno, establezca el nivel de registro en :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}