Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 22 de julio de 2014
DebugHay veces que desde Javascript nos interesa ejecutar un código u otro en función de si la ejecución se está produciendo en un servidor de desarrollo o en uno de producción. Por ejemplo, en el primer caso suele ser interesante disponer de logs o herramientas de ayuda a la depuración, mientras que en el segundo lo normal es que queramos introducir código más eficiente y sin este tipo de condimentos.

En este post vamos a ver algunas técnicas muy básicas que nos permitirán ejecutar un código u otro en el lado cliente de aplicaciones ASP.NET MVC (o ASP.NET en general) en función del modo de compilación.

1. El lado servidor

En el lado servidor es bastante fácil de conseguir porque disponemos de directivas de compilación que nos permiten detectar si estamos generando los binarios en modo depuración:
// HomeController.cs
public ActionResult Index()
{
    #if DEBUG
        ViewBag.CompilationInDebugMode = true;
    #else
        ViewBag.CompilationInDebugMode = false;
    #endif
    return View();
}

@* Home/Index.cshtml *@
<p>Compilation in debug mode: @ViewBag.CompilationInDebugMode</p>
También podemos conocer en tiempo de ejecución si ASP.NET está compilando en dicho modo (compilation debug="true" en el web.config):
@* In a Razor view: *@
<p>ASP.NET debugging: @Context.IsDebuggingEnabled</p>
Normalmente esta última vía será la que utilicemos, pues es la que suele indicar si estamos ejecutando sobre un servidor de desarrollo o no, básicamente porque el cambio en el web.config del valor del parámetro debug a false es uno de esos pasos que nos han recomendado desde el principio de los tiempos al pasar una aplicación a producción. Y seguro que todos lo hacemos, ¿verdad? ;-)

2. ¿Y el lado cliente?

En el lado cliente, debido a la propia naturaleza dinámica del lenguaje Javascript, no existe una compilación en modo depuración; bueno, de hecho no existe ni siquiera compilación como tal ;-), por lo que tenemos que recurrir a algunos truquillos para conseguir emular este comportamiento.

2.1. Cargar scripts diferentes en función del modo de depuración de ASP.NET

Una posibilidad muy sencilla, y válida en algunos escenarios, es tener un archivo de script diferente para cada modo de compilación. De hecho, es empleada por muchas bibliotecas Javascript, que se distribuyen en forma de archivo .js para la versión de producción, normalmente ya compactada y minimizada, y .debug.js para la versión a utilizar durante el desarrollo.

Por ejemplo, supongamos que tenemos los dos siguientes archivos:
// File: Myscript.debug.js (development script)
function log(msg) {
    console.log(msg);
}

// =====================================

// File: Myscript.js (production script)
function log(msg) {
    // Nothing to do
}
Para cargar manualmente un archivo u otro en función del modo de compilación bastaría con hacer lo siguiente a la hora de referenciarlo:
<script src="~/scripts/myscript@(Context.IsDebuggingEnabled? ".debug": "").js">
</script>
Sin embargo, si utilizamos bundles es aún más sencillo: de serie, el sistema de bundling ya distinguirá entre las distintas versiones de forma automática basándose en la extensión de los archivos. Por ejemplo, continuando con el caso anterior, al añadir un bundle como el siguiente el mismo componente de optimización seleccionará el archivo myscript.debug.js cuando la depuración de ASP.NET esté activada, y myscript.js en caso contrario:
bundles.Add(new ScriptBundle("~/bundles/myscript").Include(
                "~/Scripts/myscript.*"
            ));
La referencia desde la vista en este caso sería así de simple:
@Scripts.Render("~/bundles/myscript")

2.2. Detección inline del modo de depuración

Pero no siempre el grano es tan grueso como en el apartado anterior. Muy frecuentemente no nos interesará sustituir completamente un script por otro en función del modo de compilación, sino conocer simplemente el modo en el que estamos para tomar una decisión a nivel de código. Esto nos permitiría escribir código como el siguiente:
function doSomething() {
    if (DEBUG_MODE) {
        log("Entering doSomething()");
    }
    // ... Code
    if (DEBUG_MODE) {
        log("Exiting doSomething()");
    }
}
Por tanto, nuestro problema se reduce a establecer esa constante DEBUG_MODE a un valor que evalúe a cierto cuando estemos ejecutando sobre una aplicación compilada en modo depuración y a falso en caso contrario. Y aplicando lo visto anteriormente, podríamos conseguirlo de varias formas.

Una, realmente sencilla de implementar, consistiría en introducir un conciso bloque de scripts antes de cargar las bibliotecas o scripts que usen este valor, por ejemplo en el encabezado del layout o página maestra del sitio web, más o menos con lo siguiente:

@* Razor Layout *@
...
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    <script>
        window.DEBUG_MODE = @(Context.IsDebuggingEnabled? "true": "false");
    </script>
</head>
...
Otra posibilidad sería establecer el valor de DEBUG_MODE en un script independiente del que tendríamos dos versiones, con extensiones .debug.js y .js, que respectivamente lo inicializarían a true y false, y usar cualquiera de las dos técnicas descritas anteriormente para cargar uno u otro en función del modo de compilación.

Publicado en Variable not found.

4 Comentarios:

Pedro Hurtado dijo...

Buenas Master:)

Tu crees que no sería mejor hacer una function que escriba el texto que le pasas como parametro y establecer una variable a nivel de modulo llamada "debug" debug=true o debug=false y el el inicio de la function poner

function log(messaje){

if(!debug) return;

//Escribir el mensaje

}

Después desde cada function llamas a este con el mensaje que quieres escribir y te ahorras dos js.

Como se que te gusta el SIGNALR y estoy intentando adaptarlo a Angularjs me he encontrado con este método que te puede inspirar.

log = function (msg, logging) {
if (logging === false) {
return;
}
var m;
if (typeof (window.console) === "undefined") {
return;
}
m = "[" + new Date().toTimeString() + "] SignalR: " + msg;
if (window.console.debug) {
window.console.debug(m);
} else if (window.console.log) {
window.console.log(m);
}
},

josé M. Aguilar dijo...

Hola, amigo! Gracias por comentar!

Claro, ambas opciones podrían valer también. Ideas para temas así de habituales creo hay tantas como desarrolladores :)

Lo interesante es tener alternativas y aplicar la que mejor encaje en el escenario concreto en el que nos encontremos.

Un abrazo!

Santy dijo...

Pues muy buen tip.
Me gusta más las opciones que planteas en el artículo sobre la que plantea Pedro por una sencilla razón, como a las funciones de JS les puedes pasar los parámetros que quieras... tener un parámetro como el "debug" en cada una de ellas puede inducir a algún error por despiste.

josé M. Aguilar dijo...

Hola, Santy.

Gracias por tu comentario :)

Bueno, el tema de recibirlo como parámetro tiene la ventaja de evitar la "polución" de espacios globales como window, y entiendo su utilidad. Aunque de todas formas, habría que aplicar algunas de las técnicas vistas para determinar si hay que pasar un "true" o un "false" a los métodos.

Por otra parte, como comentas, tampoco veo bien el enfoque del cliente SignalR de añadir un parámetro "logging" al método; en escenarios más amplios, el hacerlo así podría generar muchos problemas.

Pero bueno, en definitiva es lo que decía antes: escenarios hay miles, lo importante es tener varias ideas en la mochila, y aplicar la que más interese en cada caso.

Un saludo!