Cómo hacer funcionar el router de Angular en IIS sin utilizar el hash en las urls

angular-web-app

El problema

Es habitual que, en tiempo de desarrollo, las rutas definidas para el router de Angular funcionen perfectamente en nuestro navegador pero, ¿Qué pasa cuando subimos nuestra app a un servidor IIS? Si estás pensando en un error 404 ¡Estás en lo cierto!

La realidad es que cuando se carga la app desde su ruta base y el router se pone a trabajar, todo va como la seda. No obstante, si intentamos acceder directamente a una url, pongamos https://nuestro-dominio.com/panel-de-control, nos encontraremos con que es el servidor el que intenta resolver esa ruta y, como no existe físicamente, nos va a devolver un 404.

A menudo, este efecto no deseado se resuelve indicando al router que aplique un sufijo a la url base que no es más que un hash. Esto se indica en la configuración del router de la siguiente forma:

RouterModule.forRoot(routes, {useHash: true})

Si aplicamos esta configuración veremos que la navegación se realiza mediante urls del estilo https://nuestro-dominio.com/#/panel-de-control

Lo que también es muy probable es que no sea la forma más elegante de mostrar las rutas de nuestra app o, lo que es más relevante aún, que las especificaciones de nuestro producto requieran de urls semánticas y libres del horrible hash.

La solución

En el caso de IIS la medida a tomar para resolver este asunto es bien sencilla y no requiere de acceso a su configuración ya que, le vamos a dar las indicaciones mediante el archivo web.config.

Esta configuración pasa por indicarle al IIS que aplique una sencilla regla de reescritura que va a obligar al servidor a pasar todas las peticiones por la ruta base de manera que, en todos los casos, será el router de Angular el que obre su magia.

<configuration>
    <system.webServer>
		<rewrite>
		  <rules>
			<rule name="Angular Routes" stopProcessing="true">
			  <match url=".*" />
			  <conditions logicalGrouping="MatchAll">
				<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
				<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
			  </conditions>
			  <action type="Rewrite" url="/" />
			</rule>
		  </rules>
		</rewrite>
    </system.webServer>
</configuration>