Middleware de reescritura de URL en ASP.NET Core

Por Kirk Larkin y Rick Anderson

En este artículo se ofrece una introducción a la reescritura de URL e instrucciones sobre cómo usar el middleware de reescritura de URL en aplicaciones ASP.NET Core.

La reescritura de URL consiste en modificar varias URL de solicitud basadas en una o varias reglas predefinidas. La reescritura de URL crea una abstracción entre las ubicaciones de recursos y sus direcciones para que las ubicaciones y las direcciones no estén estrechamente vinculadas. La reescritura de URL es útil en varios escenarios para:

  • Mover o reemplazar recursos del servidor de forma temporal o permanente a la vez que se mantienen localizadores estables para esos recursos.
  • Dividir el procesamiento de solicitudes entre otras aplicaciones o entre áreas de una aplicación.
  • Quitar, agregar o reorganizar segmentos de dirección URL en solicitudes entrantes.
  • Optimizar direcciones URL públicas para la optimización del motor de búsqueda (SEO).
  • Permitir el uso de direcciones URL públicas descriptivas para ayudar a los visitantes a predecir el contenido devuelto mediante la solicitud de un recurso.
  • Redirigir solicitudes no seguras a puntos de conexión seguros.
  • Evitar la vinculación activa, en la que un sitio externo usa un recurso estático hospedado en otro sitio mediante la vinculación del recurso a su propio contenido.

La reescritura de URL puede reducir el rendimiento de una aplicación. Limite el número y la complejidad de las reglas.

Redireccionamiento y reescritura de URL

La diferencia entre los términos redirección de URL y reescritura de URL es sutil, pero tiene importantes implicaciones para proporcionar recursos a los clientes. El middleware de reescritura de URL de ASP.NET Core es capaz de satisfacer las necesidades de ambos.

Una redirección de URL implica una operación del lado cliente, donde al cliente se le indica que acceda a un recurso en una dirección distinta a la que ha solicitado originalmente. Esto requiere un recorrido de ida y vuelta al servidor. La URL de redireccionamiento que se devuelve al cliente aparece en la barra de direcciones del explorador cuando el cliente realiza una nueva solicitud para el recurso.

Si /resource se redirige a /different-resource, el servidor responde que el cliente debe obtener el recurso en /different-resource con un código de estado que indica que la redirección es temporal o permanente.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

Al redirigir las solicitudes a otra dirección URL, se indica si la redirección es permanente o temporal mediante la especificación del código de estado con la respuesta:

  • El código de estado 301 - Moved Permanently se usa cuando el recurso tiene una nueva dirección URL permanente y todas las solicitudes futuras para el recurso deben usar la nueva dirección URL. El cliente puede almacenar en caché la respuesta cuando se recibe un código de estado 301.

  • El código de estado 302 - Found se usa cuando el redireccionamiento es temporal o en general está sujeto a cambios. El código de estado 302 indica al cliente que no almacene la dirección URL y la use en el futuro.

Para más información sobre los códigos de estado, consulte RFC 9110: definiciones de código de estado.

La reescritura de URL es una operación del lado servidor que proporciona un recurso desde una dirección de recursos distinta a la que el cliente ha solicitado. La reescritura de una dirección URL no requiere un recorrido de ida y vuelta al servidor. La dirección URL reescrita no se devuelve al cliente y no aparece en la barra de direcciones del explorador.

Si /resource se reescribe como /different-resource, el servidor la obtiene internamente y devuelve el recurso en /different-resource.

Aunque es posible que el cliente pueda recuperar el recurso en la dirección URL reescrita, no se le informa de que el recurso existe en la dirección URL reescrita cuando realiza su solicitud y recibe la respuesta.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Aplicación de ejemplo de reescritura de URL

Explore las características del middleware de reescritura de URL con la aplicación de ejemplo. La aplicación emplea las reglas de redireccionamiento y reescritura, y muestra la URL redirigida o reescrita para varios escenarios.

Cuándo usar el middleware de reescritura de URL

Use el middleware de reescritura de URL cuando no pueda usar los enfoques siguientes:

Utilice el middleware de reescritura de URL cuando la aplicación esté hospedada en el servidor HTTP.sys.

Las principales razones para usar la tecnologías de reescritura de URL basadas en servidor en IIS, Apache y Nginx son las siguientes:

  • El middleware no es compatible con todas las características de estos módulos.

    Algunas de las características de los módulos de servidor no funcionan con proyectos de ASP.NET Core, como las restricciones IsFile y IsDirectory del módulo de reescritura de IIS. En estos casos, es mejor usar el middleware.

  • El rendimiento del middleware probablemente no coincida con el de los módulos.

    La única manera de saber con certeza con qué enfoque el rendimiento disminuye más, o si la disminución de este es insignificante, es mediante una comparación.

Extensión y opciones

Establezca las reglas de reescritura y redirección de URL mediante la creación de una instancia de la clase RewriteOptions con métodos de extensión para cada una de las reglas de reescritura. Encadene varias reglas en el orden en que se deben procesar. Las RewriteOptions se pasan al middleware de reescritura de URL cuando se agrega a la canalización de solicitudes con UseRewriter:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

En el código anterior, MethodRules es una clase definida por el usuario. Consulte RewriteRules.cs este artículo para obtener más información.

Redirigir solicitudes que distintas de www a www

Hay tres opciones que permiten a la aplicación redirigir solicitudes distintas de www a www:

Redirección de URL

Use AddRedirect para redirigir las solicitudes. El primer parámetro contiene la expresión regular de .NET (Regex) para hacer coincidir la ruta de acceso de la URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, si está presente, especifica el código de estado. Si no se especifica el código de estado, el valor predeterminado es 302 - Encontrado, lo que indica que el recurso se ha movido o reemplazado temporalmente.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

En un explorador con herramientas de desarrollo habilitadas, realice una solicitud a la aplicación de ejemplo con la ruta de acceso /redirect-rule/1234/5678. La expresión regular coincide con la ruta de acceso de la solicitud en redirect-rule/(.*) y la ruta de acceso se reemplaza por /redirected/1234/5678. La dirección URL de redireccionamiento se devuelve al cliente con un código de estado 302 - Encontrado. El explorador realiza una solicitud nueva en la URL de redireccionamiento, que aparece en la barra de direcciones del explorador. Como ninguna de las reglas de la aplicación de ejemplo coincide con la dirección URL de redireccionamiento:

  • La segunda solicitud recibe una respuesta 200: correcto de la aplicación.
  • En el cuerpo de la respuesta se muestra la dirección URL de redireccionamiento.

Se realiza un recorrido de ida y vuelta al servidor cuando se redirige una dirección URL.

Advertencia

Tenga cuidado al establecer las reglas de redirección. Las reglas de redirección se evalúan en cada solicitud enviada a la aplicación, incluso después de una redirección. Es fácil crear por error un bucle de redirecciones infinitas.

La parte de la expresión incluida entre paréntesis se denomina grupo de capturas. El punto (.) de la expresión significa buscar cualquier carácter. El asterisco (*) indica buscar el carácter precedente cero o más veces. Por tanto, el grupo de capturas (.*) busca los dos últimos segmentos de la ruta de acceso de la URL, 1234/5678. Cualquier valor que se proporcione en la URL de la solicitud después de redirect-rule/ se captura por este grupo de capturas único.

En la cadena de reemplazo, se insertan los grupos capturados en la cadena con el signo de dólar ($) seguido del número de secuencia de la captura. Se obtiene el valor del primer grupo de capturas con $1, el segundo con $2, y así siguen en secuencia para los grupos de capturas de la expresión regular. Solo hay un grupo capturado en la expresión regular de la regla de redirección de redirect-rule/(.*), por lo que solo hay un grupo insertado en la cadena de reemplazo, que es $1. Cuando se aplica la regla, la URL se convierte en /redirected/1234/5678.

Pruebe /redirect-rule/1234/5678 con las herramientas del explorador en la pestaña de red.

Redirección de URL a un punto de conexión segura

Use AddRedirectToHttps para redirigir las solicitudes HTTP al mismo host y ruta de acceso mediante el protocolo HTTPS. Si no se proporciona el código de estado, el middleware muestra de forma predeterminada 302 - Encontrado. Si no se proporciona el puerto:

  • El middleware tiene como valor predeterminado null.
  • El esquema cambia a https (protocolo HTTPS), y el cliente accede al recurso en el puerto 443.

En el ejemplo siguiente se muestra cómo establecer el código de estado en 301 - Moved Permanently y cambiar el puerto al puerto HTTPS utilizado por Kestrel en localhost. En producción, el puerto HTTPS se establece en null:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

int? localhostHTTPSport = null;
if (app.Environment.IsDevelopment())
{
    localhostHTTPSport = Int32.Parse(Environment.GetEnvironmentVariable(
                   "ASPNETCORE_URLS")!.Split(new Char[] { ':', ';' })[2]);
}

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        // localhostHTTPport not needed for production, used only with localhost.
        .AddRedirectToHttps(301, localhostHTTPSport)
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Use AddRedirectToHttpsPermanent para redirigir las solicitudes no seguras al mismo host y ruta de acceso mediante el protocolo HTTPS seguro en el puerto 443. El middleware establece el código de estado en 301 - Moved Permanently.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Nota

Al redirigir a un punto de conexión seguro sin la necesidad de reglas de redirección adicionales, se recomienda usar el Middleware de redirección de HTTPS. Para más información, vea Aplicación de HTTPS.

En esta aplicación de ejemplo se muestra cómo usar AddRedirectToHttps o AddRedirectToHttpsPermanent. Realice una solicitud HTTP poco segura a la aplicación en http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz. Al probar el redireccionamiento de HTTP a HTTPS con localhost:

  • Use la dirección URL HTTP, que tiene un puerto diferente de la dirección URL HTTPS. La dirección URL HTTP está en el archivo Properties/launchSettings.json.
  • Se produce un error al quitar s de https://localhost/{port} porque localhost no responde en HTTP al puerto HTTPS.

En la imagen siguiente se muestra la imagen de las herramientas del explorador F12 de una solicitud a http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz con el código anterior:

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

Reescritura de URL

Use AddRewrite para crear una regla para reescribir URL. El primer parámetro contiene la expresión regular para buscar coincidencias en la ruta de dirección de URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, skipRemainingRules: {true|false}, indica al middleware si al aplicar la regla actual tiene que omitir o no alguna regla de redirección adicional.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Prueba de la solicitud a https://redirect6.azurewebsites.net/rewrite-rule/1234/5678

El acento circunflejo (^) al principio de la expresión significa que la búsqueda de coincidencias empieza al principio de la ruta de dirección URL.

En el ejemplo anterior de la regla de redireccionamiento (redirect-rule/(.*)), no hay ningún acento circunflejo (^) al principio de la expresión regular. Por tanto, cualquier carácter puede preceder a redirect-rule/ en la ruta de acceso para que la coincidencia sea correcta.

Ruta de acceso Coincidir con
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

La regla de reescritura, ^rewrite-rule/(\d+)/(\d+), solo encuentra rutas de acceso que empiezan con rewrite-rule/. En la tabla siguiente, observe la diferencia de coincidencia.

Ruta de acceso Coincidir con
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

Después de la parte ^rewrite-rule/ de la expresión, hay dos grupos de captura, (\d+)/(\d+). \d significa buscar un dígito (número) . El signo más (+) significa buscar uno o más de los caracteres anteriores. Por tanto, la URL debe contener un número seguido de una barra diagonal, seguida de otro número. Estos grupos de capturas se insertan en la URL de reescritura como $1 y $2. La cadena de reemplazo de la regla de reescritura coloca los grupos capturados en la cadena de consulta. La ruta de acceso solicitada /rewrite-rule/1234/5678 se reescribe para obtener el recurso en /rewritten?var1=1234&var2=5678. Si una cadena de consulta está presente en la solicitud original, se conserva cuando se reescribe la dirección URL.

No hay ningún recorrido de ida y vuelta al servidor para devolver el recurso. Si el recurso existe, se captura y se devuelve al cliente con un código de estado 200 - Correcto. Como el cliente no se redirige, la dirección URL no cambia en la barra de direcciones del explorador. Los clientes no pueden detectar que se ha producido una operación de reescritura de URL en el servidor.

Sugerencias de rendimiento para la reescritura y redireccionamiento de direcciones URL

Para obtener la respuesta más rápida de la aplicación:

  • Ordene las reglas de reescritura desde la que coincida con más frecuencia a la que coincida con menos frecuencia.
  • Use skipRemainingRules: true siempre que sea posible, ya que las reglas de coincidencia consumen muchos recursos y aumentan el tiempo de respuesta de aplicación. Omita el procesamiento de las reglas restantes cuando se produzca una coincidencia; no es necesario ningún procesamiento de reglas adicional.

Advertencia

Un usuario malintencionado puede proporcionar una entrada costosa de procesar a RegularExpressions y provocar un ataque por denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de expiración. Por ejemplo, las clases RedirectRule y RewriteRule pasan un tiempo de espera de un segundo.

mod_rewrite de Apache

Aplique reglas mod_rewrite de Apache con AddApacheModRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. Para obtener más información y ejemplos de reglas mod_rewrite, vea Apache mod_rewrite (mod_rewrite de Apache).

Se usa StreamReader para leer las reglas del archivo de reglas ApacheModRewrite.txt:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

La aplicación de ejemplo redirige las solicitudes de /apache-mod-rules-redirect/(.\*) a /redirected?id=$1. El código de estado de la respuesta es 302: Encontrado.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Prueba de la solicitud a https://redirect6.azurewebsites.net/apache-mod-rules-redirect/1234

El middleware Apache admite las siguientes variables de servidor mod_rewrite de Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Reglas del Módulo URL Rewrite para IIS

Para usar el mismo conjunto de reglas que se aplica al módulo URL Rewrite para IIS, use AddIISUrlRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. No dirija el middleware para que use el archivo web.config de la aplicación cuando se ejecute en Windows Server IIS. Con IIS, estas reglas se deben almacenar fuera del archivo web.config de la aplicación para evitar conflictos con el Módulo URL Rewrite para IIS. Para obtener más información y ejemplos de reglas del Módulo URL Rewrite para IIS, vea Using Url Rewrite Module 2.0 (Uso del Módulo URL Rewrite 2.0) y URL Rewrite Module Configuration Reference (Referencia de configuración del Módulo URL Rewrite).

Se usa StreamReader para leer las reglas del archivo de reglas IISUrlRewrite.xml:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

La aplicación de ejemplo reescribe las solicitudes de /iis-rules-rewrite/(.*) a /rewritten?id=$1. La respuesta se envía al cliente con un código de estado 200 - Correcto.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Prueba de la solicitud a https://redirect6.azurewebsites.net/iis-rules-rewrite/xyz

Aplicaciones que tienen un módulo de reescritura de IIS activo con reglas de nivel de servidor configuradas que afectan a la aplicación de maneras no deseadas:

  • Considere la posibilidad de deshabilitar el módulo de reescritura de IIS para la aplicación.
  • Para más información, vea Disabling IIS modules (Deshabilitación de módulos de IIS).

Características no admitidas

El middleware no admite las siguientes características de Módulo URL Rewrite para IIS:

  • Reglas de salida
  • Variables de servidor personalizadas
  • Caracteres comodín
  • LogRewrittenUrl

Variables de servidor compatibles

El middleware admite las siguientes variables de servidor del Módulo URL Rewrite para IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

IFileProvider puede obtenerse a través de PhysicalFileProvider. Con este enfoque logrará mayor flexibilidad para la ubicación de los archivos de reglas de reescritura. Asegúrese de que los archivos de reglas de reescritura se implementan en el servidor en la ruta de acceso que proporcione.

var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Regla basada en métodos

Use Add para implementar la lógica de la regla personalizada en un método. Add expone el elemento RewriteContext, lo que hace que HttpContext esté disponible para usarlo en métodos de redireccionamiento. La propiedad RewriteContext.Result determina cómo se administra el procesamiento adicional en la canalización. Establezca el valor en uno de los campos RuleResult que se describen en la tabla siguiente.

Volver a escribir el resultado del contexto Acción
RuleResult.ContinueRules (valor predeterminado) Continuar aplicando las reglas.
RuleResult.EndResponse Dejar de aplicar las reglas y enviar la respuesta.
RuleResult.SkipRemainingRules Dejar de aplicar las reglas y enviar el contexto al middleware siguiente.
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

La aplicación de ejemplo muestra un método que redirige las solicitudes para las rutas de acceso que terminen con .xml. Cuando se realiza una solicitud a /file.xml:

  • La solicitud se redirige a /xmlfiles/file.xml.
  • El código de estado se establece en 301 - Moved Permanently. Cuando el explorador realiza una solicitud nueva a /xmlfiles/file.xml, el middleware de archivos estáticos sirve el archivo al cliente desde la carpeta wwwroot/xmlfiles. Para un redireccionamiento, debe establecer de forma explícita el código de estado de la respuesta. En caso contrario, se devuelve un código de estado 200: correcto y no se produce el redireccionamiento en el cliente.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")) ||
        request.Path.Value==null)
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Este enfoque también permite volver a escribir las solicitudes. En la aplicación de ejemplo se muestra cómo volver a escribir la ruta de acceso para cualquier solicitud de archivo de texto para proporcionar el archivo de texto file.txt desde la carpeta wwwroot. El middleware de archivos estáticos proporciona el archivo en función de la ruta de acceso de la solicitud actualizada:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value != null &&
        request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Regla basada en IRule

Utilice Add para usar la lógica de reglas en una clase que implemente la interfaz IRule. IRule proporciona mayor flexibilidad que si se usa el enfoque de reglas basadas en métodos. La clase de implementación puede incluir un constructor que permita pasar parámetros para el método ApplyRule.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Se comprueba que los valores de los parámetros en la aplicación de ejemplo para extension y newPath cumplen ciertas condiciones. extension debe contener un valor, que debe ser .png, .jpg o .gif. Si newPath no es válido, se genera ArgumentException . Si se realiza una solicitud de image.png, la solicitud se redirige a /png-images/image.png. Si se realiza una solicitud de image.jpg, la solicitud se redirige a /jpg-images/image.jpg. El código de estado se establece en 301 - Moved Permanently, y context.Result se establece para detener el procesamiento de las reglas y enviar la respuesta.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)) ||
            request.Path.Value == null)
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Pruebe lo siguiente:

  • Solicitud PNG: https://redirect6.azurewebsites.net/image.png
  • Solicitud JPG: https://redirect6.azurewebsites.net/image.jpg

Ejemplos de expresiones regulares

Objetivo Cadena de expresión regular &
Ejemplo de coincidencia
Cadena de reemplazo &
Ejemplo de resultado
Ruta de acceso de reescritura en la cadena de consulta ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Quitar barra diagonal final ^path2/(.*)/$
/path2/xyz/
$1
/path2/xyz
Exigir barra diagonal final ^path3/(.*[^/])$
/path3/xyz
$1/
/path3/xyz/
Evitar reescritura de solicitudes específicas ^(.*)(?<!\.axd)$, o bien
^(?!.*\.axd$)(.*)$
Sí: /path4/resource.htm
No: /path4/resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Reorganizar los segmentos de URL path5/(.*)/(.*)/(.*)
path5/1/2/3
path5/$3/$2/$1
path5/3/2/1
Reemplazar un segmento de URL ^path6/(.*)/segment2/(.*)
^path6/segment1/segment2/segment3
path6/$1/replaced/$2
/path6/segment1/replaced/segment3

Los vínculos de la tabla anterior usan el código siguiente implementado en Azure:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)

        // Rewrite path to QS.
        .AddRewrite(@"^path/(.*)/(.*)", "path?var1=$1&var2=$2",
            skipRemainingRules: true)
        // Skip trailing slash.
        .AddRewrite(@"^path2/(.*)/$", "path2/$1",
            skipRemainingRules: true)
         // Enforce trailing slash.
         .AddRewrite(@"^path3/(.*[^/])$", "path3/$1/",
            skipRemainingRules: true)
         // Avoid rewriting specific requests.
         .AddRewrite(@"^path4/(.*)(?<!\.axd)$", "rewritten/$1",
            skipRemainingRules: true)
         // Rearrange URL segments
         .AddRewrite(@"^path5/(.*)/(.*)/(.*)", "path5/$3/$2/$1",
            skipRemainingRules: true)
          // Replace a URL segment
          .AddRewrite(@"^path6/(.*)/segment2/(.*)", "path6/$1/replaced/$2",
            skipRemainingRules: true)

        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

En la mayoría de los ejemplos de expresiones regulares anteriores, el literal path se usa para crear reglas únicas de reescritura que se pueden probar para el ejemplo implementado. Normalmente, la expresión regular no incluiría path. Por ejemplo, consulte esta tabla de ejemplos de expresiones regulares.

En este documento se ofrece una introducción a la reescritura de URL e instrucciones sobre cómo usar el middleware de reescritura de URL en aplicaciones ASP.NET Core.

La reescritura de URL consiste en modificar varias URL de solicitud basadas en una o varias reglas predefinidas. La reescritura de URL crea una abstracción entre las ubicaciones de recursos y sus direcciones para que las ubicaciones y las direcciones no estén estrechamente vinculadas. La reescritura de URL es útil en varios escenarios para:

  • Mover o reemplazar recursos del servidor de forma temporal o permanente a la vez que se mantienen localizadores estables para esos recursos.
  • Dividir el procesamiento de solicitudes entre otras aplicaciones o entre áreas de una aplicación.
  • Quitar, agregar o reorganizar segmentos de dirección URL en solicitudes entrantes.
  • Optimizar direcciones URL públicas para la optimización del motor de búsqueda (SEO).
  • Permitir el uso de direcciones URL públicas descriptivas para ayudar a los visitantes a predecir el contenido devuelto mediante la solicitud de un recurso.
  • Redirigir solicitudes no seguras a puntos de conexión seguros.
  • Evitar la vinculación activa, en la que un sitio externo usa un recurso estático hospedado en otro sitio mediante la vinculación del recurso a su propio contenido.

Nota

La reescritura de URL puede reducir el rendimiento de una aplicación. Cuando sea factible, limite el número y la complejidad de las reglas.

Vea o descargue el código de ejemplo (cómo descargarlo)

Redireccionamiento y reescritura de URL

La diferencia entre los términos redirección de URL y reescritura de URL es sutil, pero tiene importantes implicaciones para proporcionar recursos a los clientes. El middleware de reescritura de URL de ASP.NET Core es capaz de satisfacer las necesidades de ambos.

Una redirección de URL implica una operación del lado cliente, donde al cliente se le indica que acceda a un recurso en una dirección distinta a la que ha solicitado originalmente. Esto requiere un recorrido de ida y vuelta al servidor. La URL de redireccionamiento que se devuelve al cliente aparece en la barra de direcciones del explorador cuando el cliente realiza una nueva solicitud para el recurso.

Si /resource se redirige a /different-resource, el servidor responde que el cliente debe obtener el recurso en /different-resource con un código de estado que indica que la redirección es temporal o permanente.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

Al redirigir las solicitudes a otra dirección URL, se indica si la redirección es permanente o temporal mediante la especificación del código de estado con la respuesta:

  • El código de estado 301 - Moved Permanently se usa cuando el recurso tiene una nueva dirección URL permanente y se quiere indicar al cliente que todas las solicitudes futuras para el recurso deben usar la nueva dirección URL. El cliente puede almacenar en caché la respuesta cuando se recibe un código de estado 301.

  • El código de estado 302: encontrado se usa cuando el redireccionamiento es temporal o en general está sujeto a cambios. El código de estado 302 indica al cliente que no almacene la dirección URL y la use en el futuro.

Para más información sobre los códigos de estado, consulte RFC 9110: definiciones de código de estado.

La reescritura de URL es una operación del lado servidor que proporciona un recurso desde una dirección de recursos distinta a la que el cliente ha solicitado. La reescritura de una dirección URL no requiere un recorrido de ida y vuelta al servidor. La dirección URL reescrita no se devuelve al cliente y no aparece en la barra de direcciones del explorador.

Si /resource se reescribe como /different-resource, el servidor la obtiene internamente y devuelve el recurso en /different-resource.

Aunque es posible que el cliente pueda recuperar el recurso en la dirección URL reescrita, no se le informa de que el recurso existe en la dirección URL reescrita cuando realiza su solicitud y recibe la respuesta.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Aplicación de ejemplo de reescritura de URL

Puede explorar las características del middleware de reescritura de URL con la aplicación de ejemplo. La aplicación emplea las reglas de redireccionamiento y reescritura, y muestra la URL redirigida o reescrita para varios escenarios.

Cuándo usar el middleware de reescritura de URL

Use el middleware de reescritura de URL cuando no pueda usar los enfoques siguientes:

Utilice el middleware de reescritura de URL cuando la aplicación esté hospedada en el servidor HTTP.sys.

Las principales razones para usar la tecnologías de reescritura de URL basadas en servidor en IIS, Apache y Nginx son las siguientes:

  • El middleware no es compatible con todas las características de estos módulos.

    Algunas de las características de los módulos de servidor no funcionan con proyectos de ASP.NET Core, como las restricciones IsFile y IsDirectory del módulo de reescritura de IIS. En estos casos, es mejor usar el middleware.

  • El rendimiento del middleware probablemente no coincida con el de los módulos.

    La única manera de saber con certeza con qué enfoque el rendimiento disminuye más, o si la disminución de este es insignificante, es mediante una comparación.

Package

El middleware de reescritura de URL está disponible en el paquete Microsoft.AspNetCore.Rewrite, incluido implícitamente en las aplicaciones ASP.NET Core.

Extensión y opciones

Establezca las reglas de reescritura y redirección de URL mediante la creación de una instancia de la clase RewriteOptions con métodos de extensión para cada una de las reglas de reescritura. Encadene varias reglas en el orden que quiera procesarlas. Las RewriteOptions se pasan al middleware de reescritura de URL cuando se agrega a la canalización de solicitudes con UseRewriter:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Redirigir solicitudes que distintas de www a www

Hay tres opciones que permiten a la aplicación redirigir solicitudes distintas de www a www:

Redirección de URL

Use AddRedirect para redirigir las solicitudes. El primer parámetro contiene la expresión regular para hacer coincidir la ruta de acceso de la URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, si está presente, especifica el código de estado. Si no se especifica el código de estado, el valor predeterminado es 302 - Encontrado, lo que indica que el recurso se ha movido o reemplazado temporalmente.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

En un explorador con herramientas de desarrollo habilitadas, realice una solicitud a la aplicación de ejemplo con la ruta de acceso /redirect-rule/1234/5678. La expresión regular coincide con la ruta de acceso de la solicitud en redirect-rule/(.*) y la ruta de acceso se reemplaza con /redirected/1234/5678. La dirección URL de redireccionamiento se devuelve al cliente con un código de estado 302 - Encontrado. El explorador realiza una solicitud nueva en la URL de redireccionamiento, que aparece en la barra de direcciones del explorador. Como ninguna de las reglas de la aplicación de ejemplo coincide con la dirección URL de redireccionamiento:

  • La segunda solicitud recibe una respuesta 200: correcto de la aplicación.
  • En el cuerpo de la respuesta se muestra la dirección URL de redireccionamiento.

Se realiza un recorrido de ida y vuelta al servidor cuando se redirige una dirección URL.

Advertencia

Tenga cuidado al establecer las reglas de redirección. Las reglas de redirección se evalúan en cada solicitud enviada a la aplicación, incluso después de una redirección. Es fácil crear por error un bucle de redirecciones infinitas.

Solicitud original: /redirect-rule/1234/5678

Browser window with developer tools tracking the requests and responses: Add redirect

La parte de la expresión incluida entre paréntesis se denomina grupo de capturas. El punto (.) de la expresión significa buscar cualquier carácter. El asterisco (*) indica buscar el carácter precedente cero o más veces. Por tanto, el grupo de capturas (.*) busca los dos últimos segmentos de la ruta de acceso de la URL, 1234/5678. Cualquier valor que se proporcione en la URL de la solicitud después de redirect-rule/ es capturado por este grupo de capturas único.

En la cadena de reemplazo, se insertan los grupos capturados en la cadena con el signo de dólar ($) seguido del número de secuencia de la captura. Se obtiene el valor del primer grupo de capturas con $1, el segundo con $2, y así siguen en secuencia para los grupos de capturas de la expresión regular. Solo hay un grupo capturado en la expresión regular de la regla de redirección de la aplicación de ejemplo, por lo que solo hay un grupo insertado en la cadena de reemplazo, que es $1. Cuando se aplica la regla, la URL se convierte en /redirected/1234/5678.

Redirección de URL a un punto de conexión segura

Use AddRedirectToHttps para redirigir las solicitudes HTTP al mismo host y ruta de acceso mediante el protocolo HTTPS. Si no se proporciona el código de estado, el middleware muestra de forma predeterminada 302 - Encontrado. Si no se proporciona el puerto:

  • El middleware tiene como valor predeterminado null.
  • El esquema cambia a https (protocolo HTTPS), y el cliente accede al recurso en el puerto 443.

En el ejemplo siguiente se muestra cómo establecer el código de estado en 301 - Moved Permanently y cambiar el puerto a 5001.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

Use AddRedirectToHttpsPermanent para redirigir las solicitudes no seguras al mismo host y ruta de acceso mediante el protocolo HTTPS seguro en el puerto 443. El middleware establece el código de estado en 301 - Moved Permanently.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

Nota

Al redirigir a un punto de conexión seguro sin la necesidad de reglas de redirección adicionales, se recomienda usar el Middleware de redirección de HTTPS. Para más información, vea el tema Aplicación de HTTPS.

La aplicación de ejemplo es capaz de mostrar cómo usar AddRedirectToHttps o AddRedirectToHttpsPermanent. Agregue el método de extensión a RewriteOptions. Realice una solicitud poco segura a la aplicación en cualquier URL. Descarte la advertencia de seguridad del explorador que indica que el certificado autofirmado no es de confianza o cree una excepción para confiar en el certificado en cuestión.

Solicitud original mediante AddRedirectToHttps(301, 5001): http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

Solicitud original mediante AddRedirectToHttpsPermanent: http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS permanent

Reescritura de URL

Use AddRewrite para crear una regla para reescribir URL. El primer parámetro contiene la expresión regular para buscar coincidencias en la ruta de dirección de URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, skipRemainingRules: {true|false}, indica al middleware si al aplicar la regla actual tiene que omitir o no alguna regla de redirección adicional.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Solicitud original: /rewrite-rule/1234/5678

Browser window with developer tools tracking the request and response: Add rewrite

El acento circunflejo (^) al principio de la expresión significa que la búsqueda de coincidencias empieza al principio de la ruta de dirección URL.

En el ejemplo anterior de la regla de redireccionamiento (redirect-rule/(.*)), no hay ningún acento circunflejo (^) al principio de la expresión regular. Por tanto, cualquier carácter puede preceder a redirect-rule/ en la ruta de acceso para que la coincidencia sea correcta.

Ruta de acceso Coincidir con
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

La regla de reescritura, ^rewrite-rule/(\d+)/(\d+), solo encuentra rutas de acceso que empiezan con rewrite-rule/. En la tabla siguiente, observe la diferencia de coincidencia.

Ruta de acceso Coincidir con
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

Después de la parte ^rewrite-rule/ de la expresión, hay dos grupos de captura, (\d+)/(\d+). \d significa buscar un dígito (número) . El signo más (+) significa buscar uno o más de los caracteres anteriores. Por tanto, la URL debe contener un número seguido de una barra diagonal, seguida de otro número. Estos grupos de capturas se insertan en la URL de reescritura como $1 y $2. La cadena de reemplazo de la regla de reescritura coloca los grupos capturados en la cadena de consulta. La ruta de acceso solicitada de /rewrite-rule/1234/5678 se reescribe para obtener el recurso en /rewritten?var1=1234&var2=5678. Si una cadena de consulta está presente en la solicitud original, se conserva cuando se reescribe la dirección URL.

No hay ningún recorrido de ida y vuelta al servidor para obtener el recurso. Si el recurso existe, se captura y se devuelve al cliente con un código de estado 200 - Correcto. Como el cliente no se redirige, la dirección URL no cambia en la barra de direcciones del explorador. Los clientes no pueden detectar que se ha producido una operación de reescritura de URL en el servidor.

Nota

Use skipRemainingRules: true siempre que sea posible, ya que las reglas de coincidencia consumen muchos recursos y aumentan el tiempo de respuesta de aplicación. Para obtener la respuesta más rápida de la aplicación:

  • Ordene las reglas de reescritura desde la que coincida con más frecuencia a la que coincida con menos frecuencia.
  • Omita el procesamiento de las reglas restantes cuando se produzca una coincidencia; no es necesario ningún procesamiento de reglas adicional.

mod_rewrite de Apache

Aplique reglas mod_rewrite de Apache con AddApacheModRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. Para obtener más información y ejemplos de reglas mod_rewrite, vea Apache mod_rewrite (mod_rewrite de Apache).

Se usa StreamReader para leer las reglas del archivo de reglas ApacheModRewrite.txt:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo redirige las solicitudes de /apache-mod-rules-redirect/(.\*) a /redirected?id=$1. El código de estado de la respuesta es 302: Encontrado.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Solicitud original: /apache-mod-rules-redirect/1234

Browser window with developer tools tracking the requests and responses: Add Apache mod redirect

El middleware admite las siguientes variables de servidor mod_rewrite de Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Reglas del Módulo URL Rewrite para IIS

Para usar el mismo conjunto de reglas que se aplica al módulo URL Rewrite para IIS, use AddIISUrlRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. No dirija el middleware para que use el archivo web.config de la aplicación cuando se ejecute en Windows Server IIS. Con IIS, estas reglas se deben almacenar fuera del archivo web.config de la aplicación para evitar conflictos con el Módulo URL Rewrite para IIS. Para obtener más información y ejemplos de reglas del Módulo URL Rewrite para IIS, vea Using Url Rewrite Module 2.0 (Uso del Módulo URL Rewrite 2.0) y URL Rewrite Module Configuration Reference (Referencia de configuración del Módulo URL Rewrite).

Se usa StreamReader para leer las reglas del archivo de reglas IISUrlRewrite.xml:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo reescribe las solicitudes de /iis-rules-rewrite/(.*) a /rewritten?id=$1. La respuesta se envía al cliente con un código de estado 200 - Correcto.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Solicitud original: /iis-rules-rewrite/1234

Browser window with developer tools tracking the request and response: Add IIS URL rewrite

Si tiene un Módulo URL Rewrite para IIS activo con reglas configuradas en el nivel de servidor que podrían afectar a la aplicación de manera no deseada, puede deshabilitar el Módulo URL Rewrite para IIS para una aplicación. Para más información, vea Disabling IIS modules (Deshabilitación de módulos de IIS).

Características no admitidas

El middleware no admite las siguientes características de Módulo URL Rewrite para IIS:

  • Reglas de salida
  • Variables de servidor personalizadas
  • Caracteres comodín
  • LogRewrittenUrl

Variables de servidor compatibles

El middleware admite las siguientes variables de servidor del Módulo URL Rewrite para IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

Nota

También puede obtener IFileProvider a través de PhysicalFileProvider. Con este enfoque logrará mayor flexibilidad para la ubicación de los archivos de reglas de reescritura. Asegúrese de que los archivos de reglas de reescritura se implementan en el servidor en la ruta de acceso que proporcione.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Regla basada en métodos

Use Add para implementar su propia lógica de la regla en un método. Add expone el elemento RewriteContext, lo que hace que HttpContext esté disponible para usarlo en el método. RewriteContext.Result determina cómo se administra el procesamiento adicional en la canalización. Establezca el valor en uno de los campos RuleResult que se describen en la tabla siguiente.

Volver a escribir el resultado del contexto Acción
RuleResult.ContinueRules (valor predeterminado) Continuar aplicando las reglas.
RuleResult.EndResponse Dejar de aplicar las reglas y enviar la respuesta.
RuleResult.SkipRemainingRules Dejar de aplicar las reglas y enviar el contexto al middleware siguiente.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo muestra un método que redirige las solicitudes para las rutas de acceso que terminen con .xml. Si se realiza una solicitud de /file.xml, la solicitud se redirige a /xmlfiles/file.xml. El código de estado se establece en 301 - Moved Permanently. Cuando el explorador realiza una solicitud nueva a /xmlfiles/file.xml, el middleware de archivos estáticos sirve el archivo al cliente desde la carpeta wwwroot/xmlfiles. Para un redireccionamiento, debe establecer de forma explícita el código de estado de la respuesta. En caso contrario, se devuelve un código de estado 200: correcto y no se produce el redireccionamiento en el cliente.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Este enfoque también permite volver a escribir las solicitudes. En la aplicación de ejemplo se muestra cómo volver a escribir la ruta de acceso para cualquier solicitud de archivo de texto para proporcionar el archivo de texto file.txt desde la carpeta wwwroot. El middleware de archivos estáticos proporciona el archivo en función de la ruta de acceso de la solicitud actualizada:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Regla basada en IRule

Utilice Add para usar la lógica de reglas en una clase que implemente la interfaz IRule. IRule proporciona mayor flexibilidad que si se usa el enfoque de reglas basadas en métodos. La clase de implementación puede incluir un constructor que permita pasar parámetros para el método ApplyRule.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Se comprueba que los valores de los parámetros en la aplicación de ejemplo para extension y newPath cumplen ciertas condiciones. extension debe contener un valor, que debe ser .png, .jpg o .gif. Si newPath no es válido, se genera ArgumentException . Si se realiza una solicitud de image.png, la solicitud se redirige a /png-images/image.png. Si se realiza una solicitud de image.jpg, la solicitud se redirige a /jpg-images/image.jpg. El código de estado se establece en 301 - Moved Permanently, y context.Result se establece para detener el procesamiento de las reglas y enviar la respuesta.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Solicitud original: /image.png

Browser window with developer tools tracking the requests and responses for image.png

Solicitud original: /image.jpg

Browser window with developer tools tracking the requests and responses for image.jpg

Ejemplos de expresiones regulares

Objetivo Cadena de expresión regular &
Ejemplo de coincidencia
Cadena de reemplazo &
Ejemplo de resultado
Ruta de acceso de reescritura en la cadena de consulta ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Quitar barra diagonal final (.*)/$
/path/
$1
/path
Exigir barra diagonal final (.*[^/])$
/path
$1/
/path/
Evitar reescritura de solicitudes específicas ^(.*)(?<!\.axd)$ o ^(?!.*\.axd$)(.*)$
Sí: /resource.htm
No: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Reorganizar los segmentos de URL path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
Reemplazar un segmento de URL ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

En este documento se ofrece una introducción a la reescritura de URL e instrucciones sobre cómo usar el middleware de reescritura de URL en aplicaciones ASP.NET Core.

La reescritura de URL consiste en modificar varias URL de solicitud basadas en una o varias reglas predefinidas. La reescritura de URL crea una abstracción entre las ubicaciones de recursos y sus direcciones para que las ubicaciones y las direcciones no estén estrechamente vinculadas. La reescritura de URL es útil en varios escenarios para:

  • Mover o reemplazar recursos del servidor de forma temporal o permanente a la vez que se mantienen localizadores estables para esos recursos.
  • Dividir el procesamiento de solicitudes entre otras aplicaciones o entre áreas de una aplicación.
  • Quitar, agregar o reorganizar segmentos de dirección URL en solicitudes entrantes.
  • Optimizar direcciones URL públicas para la optimización del motor de búsqueda (SEO).
  • Permitir el uso de direcciones URL públicas descriptivas para ayudar a los visitantes a predecir el contenido devuelto mediante la solicitud de un recurso.
  • Redirigir solicitudes no seguras a puntos de conexión seguros.
  • Evitar la vinculación activa, en la que un sitio externo usa un recurso estático hospedado en otro sitio mediante la vinculación del recurso a su propio contenido.

Nota

La reescritura de URL puede reducir el rendimiento de una aplicación. Cuando sea factible, limite el número y la complejidad de las reglas.

Vea o descargue el código de ejemplo (cómo descargarlo)

Redireccionamiento y reescritura de URL

La diferencia entre los términos redirección de URL y reescritura de URL es sutil, pero tiene importantes implicaciones para proporcionar recursos a los clientes. El middleware de reescritura de URL de ASP.NET Core es capaz de satisfacer las necesidades de ambos.

Una redirección de URL implica una operación del lado cliente, donde al cliente se le indica que acceda a un recurso en una dirección distinta a la que ha solicitado originalmente. Esto requiere un recorrido de ida y vuelta al servidor. La URL de redireccionamiento que se devuelve al cliente aparece en la barra de direcciones del explorador cuando el cliente realiza una nueva solicitud para el recurso.

Si /resource se redirige a /different-resource, el servidor responde que el cliente debe obtener el recurso en /different-resource con un código de estado que indica que la redirección es temporal o permanente.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

Al redirigir las solicitudes a otra dirección URL, se indica si la redirección es permanente o temporal mediante la especificación del código de estado con la respuesta:

  • El código de estado 301 - Moved Permanently se usa cuando el recurso tiene una nueva dirección URL permanente y se quiere indicar al cliente que todas las solicitudes futuras para el recurso deben usar la nueva dirección URL. El cliente puede almacenar en caché la respuesta cuando se recibe un código de estado 301.

  • El código de estado 302: encontrado se usa cuando el redireccionamiento es temporal o en general está sujeto a cambios. El código de estado 302 indica al cliente que no almacene la dirección URL y la use en el futuro.

Para más información sobre los códigos de estado, consulte RFC 9110: definiciones de código de estado.

La reescritura de URL es una operación del lado servidor que proporciona un recurso desde una dirección de recursos distinta a la que el cliente ha solicitado. La reescritura de una dirección URL no requiere un recorrido de ida y vuelta al servidor. La dirección URL reescrita no se devuelve al cliente y no aparece en la barra de direcciones del explorador.

Si /resource se reescribe como /different-resource, el servidor la obtiene internamente y devuelve el recurso en /different-resource.

Aunque es posible que el cliente pueda recuperar el recurso en la dirección URL reescrita, no se le informa de que el recurso existe en la dirección URL reescrita cuando realiza su solicitud y recibe la respuesta.

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Aplicación de ejemplo de reescritura de URL

Puede explorar las características del middleware de reescritura de URL con la aplicación de ejemplo. La aplicación emplea las reglas de redireccionamiento y reescritura, y muestra la URL redirigida o reescrita para varios escenarios.

Cuándo usar el middleware de reescritura de URL

Use el middleware de reescritura de URL cuando no pueda usar los enfoques siguientes:

Además, use el middleware cuando la aplicación se hospede en el servidor HTTP.sys (anteriormente denominado WebListener).

Las principales razones para usar la tecnologías de reescritura de URL basadas en servidor en IIS, Apache y Nginx son las siguientes:

  • El middleware no es compatible con todas las características de estos módulos.

    Algunas de las características de los módulos de servidor no funcionan con proyectos de ASP.NET Core, como las restricciones IsFile y IsDirectory del módulo de reescritura de IIS. En estos casos, es mejor usar el middleware.

  • El rendimiento del middleware probablemente no coincida con el de los módulos.

    La única manera de saber con certeza con qué enfoque el rendimiento disminuye más, o si la disminución de este es insignificante, es mediante una comparación.

Package

Para incluir el middleware en el proyecto, agregue una referencia de paquete al metapaquete Microsoft.AspNetCore.App del archivo de proyecto, que contiene el paquete Microsoft.AspNetCore.Rewrite.

Si no se usa el metapaquete Microsoft.AspNetCore.App, agregue una referencia de proyecto al paquete Microsoft.AspNetCore.Rewrite.

Extensión y opciones

Establezca las reglas de reescritura y redirección de URL mediante la creación de una instancia de la clase RewriteOptions con métodos de extensión para cada una de las reglas de reescritura. Encadene varias reglas en el orden que quiera procesarlas. Las RewriteOptions se pasan al middleware de reescritura de URL cuando se agrega a la canalización de solicitudes con UseRewriter:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Redirigir solicitudes que distintas de www a www

Hay tres opciones que permiten a la aplicación redirigir solicitudes distintas de www a www:

Redirección de URL

Use AddRedirect para redirigir las solicitudes. El primer parámetro contiene la expresión regular para hacer coincidir la ruta de acceso de la URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, si está presente, especifica el código de estado. Si no se especifica el código de estado, el valor predeterminado es 302 - Encontrado, lo que indica que el recurso se ha movido o reemplazado temporalmente.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

En un explorador con herramientas de desarrollo habilitadas, realice una solicitud a la aplicación de ejemplo con la ruta de acceso /redirect-rule/1234/5678. La expresión regular coincide con la ruta de acceso de la solicitud en redirect-rule/(.*) y la ruta de acceso se reemplaza con /redirected/1234/5678. La dirección URL de redireccionamiento se devuelve al cliente con un código de estado 302 - Encontrado. El explorador realiza una solicitud nueva en la URL de redireccionamiento, que aparece en la barra de direcciones del explorador. Como ninguna de las reglas de la aplicación de ejemplo coincide con la dirección URL de redireccionamiento:

  • La segunda solicitud recibe una respuesta 200: correcto de la aplicación.
  • En el cuerpo de la respuesta se muestra la dirección URL de redireccionamiento.

Se realiza un recorrido de ida y vuelta al servidor cuando se redirige una dirección URL.

Advertencia

Tenga cuidado al establecer las reglas de redirección. Las reglas de redirección se evalúan en cada solicitud enviada a la aplicación, incluso después de una redirección. Es fácil crear por error un bucle de redirecciones infinitas.

Solicitud original: /redirect-rule/1234/5678

Add redirect: Browser window with developer tools tracking the requests and responses

La parte de la expresión incluida entre paréntesis se denomina grupo de capturas. El punto (.) de la expresión significa buscar cualquier carácter. El asterisco (*) indica buscar el carácter precedente cero o más veces. Por tanto, el grupo de capturas (.*) busca los dos últimos segmentos de la ruta de acceso de la URL, 1234/5678. Cualquier valor que se proporcione en la URL de la solicitud después de redirect-rule/ es capturado por este grupo de capturas único.

En la cadena de reemplazo, se insertan los grupos capturados en la cadena con el signo de dólar ($) seguido del número de secuencia de la captura. Se obtiene el valor del primer grupo de capturas con $1, el segundo con $2, y así siguen en secuencia para los grupos de capturas de la expresión regular. Solo hay un grupo capturado en la expresión regular de la regla de redirección de la aplicación de ejemplo, por lo que solo hay un grupo insertado en la cadena de reemplazo, que es $1. Cuando se aplica la regla, la URL se convierte en /redirected/1234/5678.

Redirección de URL a un punto de conexión segura

Use AddRedirectToHttps para redirigir las solicitudes HTTP al mismo host y ruta de acceso mediante el protocolo HTTPS. Si no se proporciona el código de estado, el middleware muestra de forma predeterminada 302 - Encontrado. Si no se proporciona el puerto:

  • El middleware tiene como valor predeterminado null.
  • El esquema cambia a https (protocolo HTTPS), y el cliente accede al recurso en el puerto 443.

En el ejemplo siguiente se muestra cómo establecer el código de estado en 301 - Moved Permanently y cambiar el puerto a 5001.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

Use AddRedirectToHttpsPermanent para redirigir las solicitudes no seguras al mismo host y ruta de acceso mediante el protocolo HTTPS seguro en el puerto 443. El middleware establece el código de estado en 301 - Moved Permanently.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

Nota

Al redirigir a un punto de conexión seguro sin la necesidad de reglas de redirección adicionales, se recomienda usar el Middleware de redirección de HTTPS. Para más información, vea el tema Aplicación de HTTPS.

La aplicación de ejemplo es capaz de mostrar cómo usar AddRedirectToHttps o AddRedirectToHttpsPermanent. Agregue el método de extensión a RewriteOptions. Realice una solicitud poco segura a la aplicación en cualquier URL. Descarte la advertencia de seguridad del explorador que indica que el certificado autofirmado no es de confianza o cree una excepción para confiar en el certificado en cuestión.

Solicitud original mediante AddRedirectToHttps(301, 5001): http://localhost:5000/secure

Add redirect to HTTPS: Browser window with developer tools tracking the requests and responses

Solicitud original mediante AddRedirectToHttpsPermanent: http://localhost:5000/secure

Add redirect to HTTPS permanent: Browser window with developer tools tracking the requests and responses

Reescritura de URL

Use AddRewrite para crear una regla para reescribir URL. El primer parámetro contiene la expresión regular para buscar coincidencias en la ruta de dirección de URL entrante. El segundo parámetro es la cadena de reemplazo. El tercer parámetro, skipRemainingRules: {true|false}, indica al middleware si al aplicar la regla actual tiene que omitir o no alguna regla de redirección adicional.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Solicitud original: /rewrite-rule/1234/5678

Add rewrite: Browser window with developer tools tracking the request and response

El acento circunflejo (^) al principio de la expresión significa que la búsqueda de coincidencias empieza al principio de la ruta de dirección URL.

En el ejemplo anterior de la regla de redireccionamiento (redirect-rule/(.*)), no hay ningún acento circunflejo (^) al principio de la expresión regular. Por tanto, cualquier carácter puede preceder a redirect-rule/ en la ruta de acceso para que la coincidencia sea correcta.

Ruta de acceso Coincidir con
/redirect-rule/1234/5678
/my-cool-redirect-rule/1234/5678
/anotherredirect-rule/1234/5678

La regla de reescritura, ^rewrite-rule/(\d+)/(\d+), solo encuentra rutas de acceso que empiezan con rewrite-rule/. En la tabla siguiente, observe la diferencia de coincidencia.

Ruta de acceso Coincidir con
/rewrite-rule/1234/5678
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

Después de la parte ^rewrite-rule/ de la expresión, hay dos grupos de captura, (\d+)/(\d+). \d significa buscar un dígito (número) . El signo más (+) significa buscar uno o más de los caracteres anteriores. Por tanto, la URL debe contener un número seguido de una barra diagonal, seguida de otro número. Estos grupos de capturas se insertan en la URL de reescritura como $1 y $2. La cadena de reemplazo de la regla de reescritura coloca los grupos capturados en la cadena de consulta. La ruta de acceso solicitada de /rewrite-rule/1234/5678 se reescribe para obtener el recurso en /rewritten?var1=1234&var2=5678. Si una cadena de consulta está presente en la solicitud original, se conserva cuando se reescribe la dirección URL.

No hay ningún recorrido de ida y vuelta al servidor para obtener el recurso. Si el recurso existe, se captura y se devuelve al cliente con un código de estado 200 - Correcto. Como el cliente no se redirige, la dirección URL no cambia en la barra de direcciones del explorador. Los clientes no pueden detectar que se ha producido una operación de reescritura de URL en el servidor.

Nota

Use skipRemainingRules: true siempre que sea posible, ya que las reglas de coincidencia consumen muchos recursos y aumentan el tiempo de respuesta de aplicación. Para obtener la respuesta más rápida de la aplicación:

  • Ordene las reglas de reescritura desde la que coincida con más frecuencia a la que coincida con menos frecuencia.
  • Omita el procesamiento de las reglas restantes cuando se produzca una coincidencia; no es necesario ningún procesamiento de reglas adicional.

mod_rewrite de Apache

Aplique reglas mod_rewrite de Apache con AddApacheModRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. Para obtener más información y ejemplos de reglas mod_rewrite, vea Apache mod_rewrite (mod_rewrite de Apache).

Se usa StreamReader para leer las reglas del archivo de reglas ApacheModRewrite.txt:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo redirige las solicitudes de /apache-mod-rules-redirect/(.\*) a /redirected?id=$1. El código de estado de la respuesta es 302: Encontrado.

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Solicitud original: /apache-mod-rules-redirect/1234

Add Apache mod redirect: Browser window with developer tools tracking the requests and responses

El middleware admite las siguientes variables de servidor mod_rewrite de Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Reglas del Módulo URL Rewrite para IIS

Para usar el mismo conjunto de reglas que se aplica al módulo URL Rewrite para IIS, use AddIISUrlRewrite. Asegúrese de que el archivo de reglas se implementa con la aplicación. No dirija el middleware para que use el archivo web.config de la aplicación cuando se ejecute en Windows Server IIS. Con IIS, estas reglas se deben almacenar fuera del archivo web.config de la aplicación para evitar conflictos con el Módulo URL Rewrite para IIS. Para obtener más información y ejemplos de reglas del Módulo URL Rewrite para IIS, vea Using Url Rewrite Module 2.0 (Uso del Módulo URL Rewrite 2.0) y URL Rewrite Module Configuration Reference (Referencia de configuración del Módulo URL Rewrite).

Se usa StreamReader para leer las reglas del archivo de reglas IISUrlRewrite.xml:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo reescribe las solicitudes de /iis-rules-rewrite/(.*) a /rewritten?id=$1. La respuesta se envía al cliente con un código de estado 200 - Correcto.

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Solicitud original: /iis-rules-rewrite/1234

Add IIS URL rewrite: Browser window with developer tools tracking the request and response

Si tiene un Módulo URL Rewrite para IIS activo con reglas configuradas en el nivel de servidor que podrían afectar a la aplicación de manera no deseada, puede deshabilitar el Módulo URL Rewrite para IIS para una aplicación. Para más información, vea Disabling IIS modules (Deshabilitación de módulos de IIS).

Características no admitidas

El middleware publicado con ASP.NET Core 2.x no admite las siguientes características de Módulo URL Rewrite para IIS:

  • Reglas de salida
  • Variables de servidor personalizadas
  • Caracteres comodín
  • LogRewrittenUrl

Variables de servidor compatibles

El middleware admite las siguientes variables de servidor del Módulo URL Rewrite para IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

Nota

También puede obtener IFileProvider a través de PhysicalFileProvider. Con este enfoque logrará mayor flexibilidad para la ubicación de los archivos de reglas de reescritura. Asegúrese de que los archivos de reglas de reescritura se implementan en el servidor en la ruta de acceso que proporcione.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Regla basada en métodos

Use Add para implementar su propia lógica de la regla en un método. Add expone el elemento RewriteContext, lo que hace que HttpContext esté disponible para usarlo en el método. RewriteContext.Result determina cómo se administra el procesamiento adicional en la canalización. Establezca el valor en uno de los campos RuleResult que se describen en la tabla siguiente.

Volver a escribir el resultado del contexto Acción
RuleResult.ContinueRules (valor predeterminado) Continuar aplicando las reglas.
RuleResult.EndResponse Dejar de aplicar las reglas y enviar la respuesta.
RuleResult.SkipRemainingRules Dejar de aplicar las reglas y enviar el contexto al middleware siguiente.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

La aplicación de ejemplo muestra un método que redirige las solicitudes para las rutas de acceso que terminen con .xml. Si se realiza una solicitud de /file.xml, la solicitud se redirige a /xmlfiles/file.xml. El código de estado se establece en 301 - Moved Permanently. Cuando el explorador realiza una solicitud nueva a /xmlfiles/file.xml, el middleware de archivos estáticos sirve el archivo al cliente desde la carpeta wwwroot/xmlfiles. Para un redireccionamiento, debe establecer de forma explícita el código de estado de la respuesta. En caso contrario, se devuelve un código de estado 200: correcto y no se produce el redireccionamiento en el cliente.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Este enfoque también permite volver a escribir las solicitudes. En la aplicación de ejemplo se muestra cómo volver a escribir la ruta de acceso para cualquier solicitud de archivo de texto para proporcionar el archivo de texto file.txt desde la carpeta wwwroot. El middleware de archivos estáticos proporciona el archivo en función de la ruta de acceso de la solicitud actualizada:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Regla basada en IRule

Utilice Add para usar la lógica de reglas en una clase que implemente la interfaz IRule. IRule proporciona mayor flexibilidad que si se usa el enfoque de reglas basadas en métodos. La clase de implementación puede incluir un constructor que permita pasar parámetros para el método ApplyRule.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Se comprueba que los valores de los parámetros en la aplicación de ejemplo para extension y newPath cumplen ciertas condiciones. extension debe contener un valor, que debe ser .png, .jpg o .gif. Si newPath no es válido, se genera ArgumentException . Si se realiza una solicitud de image.png, la solicitud se redirige a /png-images/image.png. Si se realiza una solicitud de image.jpg, la solicitud se redirige a /jpg-images/image.jpg. El código de estado se establece en 301 - Moved Permanently, y context.Result se establece para detener el procesamiento de las reglas y enviar la respuesta.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Solicitud original: /image.png

For image.png: Browser window with developer tools tracking the requests and responses

Solicitud original: /image.jpg

For image.jpg: Browser window with developer tools tracking the requests and responses

Ejemplos de expresiones regulares

Objetivo Cadena de expresión regular &
Ejemplo de coincidencia
Cadena de reemplazo &
Ejemplo de resultado
Ruta de acceso de reescritura en la cadena de consulta ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Quitar barra diagonal final (.*)/$
/path/
$1
/path
Exigir barra diagonal final (.*[^/])$
/path
$1/
/path/
Evitar reescritura de solicitudes específicas ^(.*)(?<!\.axd)$ o ^(?!.*\.axd$)(.*)$
Sí: /resource.htm
No: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Reorganizar los segmentos de URL path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
Reemplazar un segmento de URL ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

Recursos adicionales