How to: Minify require.js Scripts in ASP.net

Ohne Frage, das Bundling in ASP.net ist eine feine Sache – so lange Scripts zusammengefasst werden können, die immer benötigt werden und nicht dynamisch per require.js bei Bedarf nachgeladen werden.

Für diese Scripts gibt es 2 Möglichkeiten.

  1. Die Scripts werden entweder direkt durch einen Script-Minifier gejagt und ausgeliefert – dafür gibt es diverse Tools, die entweder auf die Änderung eines Scripts reagieren oder bei einem Build ausgeführt werden
  2. Die Scripts werden zur Laufzeit optimiert

Ich möchte hier auf die 2. Möglichkeit eingehen und zeige, wie Scripts zur Laufzeit – unter Verwendung von WebGrease – minimiert werden können.

WebGrease installieren

WebGrease kann über nuget eingebunden werden: Install-Package WebGrease

Der Minifier

Um die Ausgabe an den Client verändern zu können, muss der „Filter“ der Response auf ein Objekt gesetzt werden, dass die Arbeit anstelle des Default-Filters übernimmt.

Response.Filter:
Summary: 	Gets or sets a wrapping filter object that is used to modify the 
		HTTP entity body before transmission.
Returns:	The System.IO.Stream object that acts as the output filter.

Konkret bedeutete das, dass ein Objekt benötigt wird, das von Stream ableitet, um es dem Filter zuzuweisen. Diesem Objekt wird der Output-Stream, auf den das minimierte Script geschrieben werden soll:

public class JavascriptRequestMinifier : MemoryStream {
	private readonly Stream _stream;
	
	///<summary>Erzeugt eine Instanz des Objekts</summary>
	///<param name="stream">Der Output-Stream</param>
	public JavascriptRequestMinifier(Stream stream) {
		this._stream = stream;
	}
	//TODO: Write + Flush implementieren
}

Als nächstes muss die Methode ‚Write‘ überschrieben werden, um das Schreiben auf den Output-Stream abzufangen.
Das Framework beschreibt den Stream blockweise, was anfangs dazu führte, dass bei größeren Scripts nicht das Ganze optimiert und ausgegeben wurde, sondern nur ein Teil davon. Abhilfe schafft ein StringBuilder, um den Output zu sammeln und ihn komplett in die Ausgabe zu schreiben, wenn der Stream geschlossen wird: 

readonly StringBuilder _codeBuilder = new StringBuilder();

public override void Write(byte[] buffer, int offset, int count) {
	// string aus buffer extrahieren und _codeBuilder zuweisen, falls das script 
	// länger ist
	_codeBuilder.Append(Encoding.UTF8.GetString(buffer));
}

Abschließend muss die Flush-Methode überschrieben werden, um die gesammelten Daten minimiert an den Client zu senden:

public override void Flush() {
	Minifier m = new Minifier();
	string code = m.MinifyJavaScript(
		this._codeBuilder.ToString(), 
		new CodeSettings {
			PreserveImportantComments = false,
			PreserveFunctionNames = true,
			MinifyCode = true
		}
	);
	this._stream.Write(
		Encoding.UTF8.GetBytes(code), 0, Encoding.UTF8.GetByteCount(code));
	this._stream.Flush();
}

Den Filter setzen

Der letzte Schritt, um das Script minimiert auszugeben ist, den Filter zuzuweisen. Die richtige Stelle ist hier in der Global.asax der Event ‚PostReleaseRequestState‘

// Occurs when ASP.NET has completed executing all request event handlers and the
// request state data has been stored.

An dieser Stelle muss geprüft werden, ob es sich bei dem ContentType um ein Javascript handelt, dass optimiert werden soll.

Ich habe require.js dahingehend konfiguriert, dass dem Script ein QueryString-Parameter ‚min-require=1‘ angefügt wird. Enthält die Query der Url des Request diesen String, wird eine Instanz des Minifiers erzeugt und dem den Response.Filter zugewiesen:

protected void Application_PostReleaseRequestState(Object sender, EventArgs e){
	if (this.Request.Url.Query.Contains("min-require=1")) {
		this.Response.Filter = 
			new JavascriptRequestMinifier(this.Response.Filter);
	}
}

Damit werden alle Scripts, die dynamisch geladen werden, minimiert an den Client übertragen.

Advertisements

C# Code während Laufzeit kompilieren mit Roslyn

Dieser Artikel wird bis zur Fertigstellung regelmäßig aktualisiert. Aktuell dient er als Gedankenstütze und „Brain Backup“ für mich…

Gelegentlich kommt es vor, dass man vor die Aufgabe gestellt wird, Code erst zur Laufzeit zu kompilieren und auszuführen. Dabei gilt es, sich neben der gestellten Aufgabe, auch um Sicherheit bzw. Berechtigungen zu kümmern. Dazu sollte man den Code in einer eigenen AppDomain mit entsprechenden Einstellungen ausführen – darauf möchte ich hier allerdings erstmal nicht eingehen, hier geht es erstmal um das Ausführen dynamischen Codes.

Was ist Roslyn?

Roslyn ist eine Scripting-API die es ermöglicht, Code zur Laufzeit auszuführen. Mit Roslyn ist es nicht mehr notwendig, Assemblies zu kompilieren und diese im Speicher oder in einem Verzeichnis abzulegen.

Die Roslyn-Assemblies

Roslyn benötigt natürlich einen Satz von Assemblies. Diese selbst zusammenzusuchen stellte sich als recht aufwendig heraus. Glücklicherweise kann man alles, was man für die Ausführung benötigt, über den PackageManager installieren: Install-Package Roslyn

Damit sind alle benötigten Assemblies im Projekt und es kann los gehen!

Javascript-Minifier beim Response – ohne Bundles

ASP.net bringt von Haus aus ja bekanntlich die Funktionalität mit, viele Javascripts zu einem Bundle zusammenzufassen und diese minimiert an den Client zu übertragen.

Was jedoch, wenn man Scripts hat, die dynamisch – z.B. mit require.js nach – nachgeladen werden?

Die eine Möglichkeit ist, (1)diese nach der Erstellung direkt zu minimieren – eine andere wäre, (2)das Script dynamisch zur Laufzeit zu optimieren und an den Client zu geben.

Zweiteres möchte ich in diesem Post näher Beleuchten.

Um Javascript zur Laufzeit zu minimieren, bedarf es einem Eingriff in der global.asax. Hier kann man sich den PostReleaseRequestState-Event zunutze machen.

Die Beschreibung zu diesem Event lautet:

Occurs when ASP.NET has completed executing all request event handlers and the request state data has been stored.

Und einer Helfer-Klasse, die das minimieren des Scripts übernimmt. Diese nimmt den OutputStream entgegen und minimiert mit Hilfe des Minifier-Objekts aus dem Namespace Microsoft.Ajax.Utilities die Ausgabe:

public class JavascriptRequestMinifier : MemoryStream{
private readonly Stream _stream;

public JavascriptRequestMinifier(Stream stream){
this._stream = stream;
}

public override void Write(byte[] buffer, int offset, int count){
string code = Encoding.UTF8.GetString(buffer);
Minifier m = new Minifier();
code = m.MinifyJavaScript(code, new CodeSettings {
EvalTreatment = EvalTreatment.MakeImmediateSafe,
PreserveImportantComments = false
});

this._stream.Write(
Encoding.UTF8.GetBytes(code),
offset,
Encoding.UTF8.GetByteCount(code));
}
}

In der Global.asax wird nun noch der PostReleaseRequestState-Event behandelt und geprüft, ob es sich um ein Javascript handelt – ist das der Fall, tritt der JavascriptRequestMinifier in Aktion:

protected void Application_PostReleaseRequestState(
Object sender, EventArgs e) {

if (this.Response.ContentType.Equals(„application/javascript“)) {
this.Response.Filter = new
JavascriptRequestMinifier(this.Response.Filter);
}
}

ACHTUNG: An der Stelle wird der Minifier immer dann verwendet, wenn es sich um eine Response handelt, dessen ContentType „application/javascript“ ist.

Das heißt, auch bereits minimierte Scripts werden noch einmal versucht, zu minimieren – was natürlich gelegentlich auch klappt, und das ein oder andere Byte zusätzlich gespart werden kann!

Um das zu verhindern, könnte man ggfs. prüfen, ob der angefragte Dateiname auf .min.js endet oder zu minimierende Scripts immer mit QueryString-Argumenten aufrufen und diese dann zu prüfen…

 

Komplexe Objekte & Arrays mit jquery $.post an einen ASP.net MVC-Controller senden

Das Szenario

Ich wollte ein komplexes Javascript-Objekt auf dem Client erzeugen und per $.post an den Server schicken. Ein einfaches Aufbauen des Objekts hat so lange funktioniert, bis ich ein Array mit weiteren Objekten erzeugt habe.

So bin ich gestartet

Mein Model auf der Serverseite sieht ungefähr so aus:

public class DataModel {
    public string Id { get; set; }
    public List<Data> Items { get; set; }
}
public class Data{
    public string[] Key { get; set; }
    public int Value { get; set; }
}

Auf dem Client habe ich dafür folgendes Aufgebaut

var data = {
    id: 'foo',
    Items: [{ Key: [ 'a', 'b' ], Value: 666 },
            { Key: [ 'c', 'd' ], Value: 999 }]
}

Nachdem ich das losgeschickt hatte, war DataModel.Id gesetzt, Items blieb NULL…

Die Lösung

jQuery ist manchmal ein…nicht ganz so nettes Ding! Ich habe viel hin und her probiert, wie ich das Model auf dem Client zusammenbauen muss, damit etwas vernünftiges auf dem Server ankommt. Dabei habe ich mich an die Notation erinnert die notwendig ist, wenn man die Daten per Form-Serialisierung versenden möchte.

Hier erhalten die Felder im name-Attribute den ‚Pfad‘, an den der Wert am Ende landen soll. Konkret heißt das, dass ein Feld, dessen Wert in DataModel.Items[0].Value stehen soll, den „name“ Items[0].Value besitzen muss.Für den Key wäre der „name“ Items[0].Key[0], Items[0].Key[1], …

Und genau SO muss dann auch der Name des Wertes im ViewModel aussehen:

var data = {
    id: 'foo' 
}
// soweit alles beim alten, jetzt das NEUE
data["Items[0].Value"] = 666;
data["Items[0].Key[0]"] = 'a';
data["Items[0].Key[1]"] = 'b';

data["Items[1].Value"] = 999;
data["Items[1].Key[0]"] = 'c';
data["Items[1].Key[1]"] = 'd';

Damit kann ich dann komplexe Daten wegschicken…

 

Wenn jemand einen besseren Weg kennt, bitte Posten! Ansonsten: Viel Spass und Erfolg damit!

 

ASP.net MVC – ActionResult mundgerecht geliefert / ViewResult to JsonResult

Problemstellung:

Eine Action auf einem MVC-Controller soll je nach Art des Request entweder ein ViewResult oder ein JsonResult zurückliefern. Dabei soll im Accepts-Header geprüft werden, ob ‚application/json‘ explizit angefordert wurde.

Per Default liefert die Action ein ViewResult zurück, bei einem Json-Request soll das Result-Objekt in ein JsonResult-Objekt umgewandelt und das Model aus ViewResult genutzt werden.

Lösung:

Die richtige Stelle, um die Anfrage zu überprüfen, ist der Einstiegspunkt ‚OnActionExecuted‘. Diese kann – und wird für dieses Beispiel – im Controller direkt überschrieben werden.

Natürlich kann man das Ganze auch in ein ActionFilterAttribute auslagern und hier OnActionExecuted überschreiben.

Zunächst wird überprüft, ob der Client im Request angegeben hat, dass er Json erwartet. Diese Information findet man hier:

filterContext.RequestContext.HttpContext.Request.AcceptTypes -> string[]

Für meine Anforderung hat es gereicht, um zu überprüfen, ob der erste Eintrag ‚application/json‘ entspricht und ob das Ergebnis ein ViewResult ist. Ist das der Fall, wird ein neues JsonResult-Objekt erzeugt und das Data-Property auf das Model aus dem ViewResult gesetzt.

Abschließend wird dem filterContext als Result das neu erzeugte Objekt zugewiesen.

string[] acceptTypes = filterContext.RequestContext.HttpContext.Request.AcceptTypes;
System.Web.Mvc.ViewResultBase vr = filterContext.Result as System.Web.Mvc.ViewResultBase;
if (acceptTypes.Length > 0 
    && acceptTypes[0].Equals("application/json") 
    && vr != null){
        filterContext.Result = newJsonResult() { Data = vr.Model };
}

Um es zu testen, habe ich einen Link erstellt, bei dem der click-Event abgefangen wird:

$("a[data-getJson]").live("click", function (evt) {
    $.ajax({  type: "POST",
             url: this.href,
             headers: { 
                 Accept:  "application/json", 
},
              complete: function (data) {
                        alert(data.responseText);
                    }
                });
                evt.preventDefault();
            });

ASP.net SignalR

Live, Live, Live… Etwas, was viele von uns im Web vermissen, ist die Möglichkeit, die Kommunikation vom Server zum Client zu ermöglichen. Zum Beispiel dann, wenn ich die Besucher meiner Webseite oder die User innerhalb einer WebApplication immer aktuell mit Informationen versorgen will, ohne Ihnen zuzumuten einen Refresh der Seite zu machen.

Es gibt eine nicht allzu große Anzahl von möglichen Lösungen zu finden, diese unterscheiden sich aber – natürlich – von Browser zu Browser.

Eine wunderbare und vor allem einfach zu implementierende Lösung – auf die ich schon vor einiger Zeit aufmerksam wurde – ist die Komponente SignalR – ein „Freizeitprojekt“ zweier Entwickler aus dem Hause Microsoft.

Auf der Client-Seite brauche ich wenige Zeilen Javascript, auf dem Server, für eine einfache Chat-Funktion, ebenfallt nur einige Zeilen.

Ich will das Ganze kurz im Umfeld einer ASP.net MVC4-Anwendung aufzeigen und ein kleines Chatprogramm schreiben.

Nachdem die Anwendung angelegt wurde, sollten die Referenzen für hinzugefügt werden. Ich mache das über NuGet…

Die Schnittstelle zwischen Server und Client stellt eine Klasse bereit, die von SignalR.Hubs.Hub ableitet. Diese Klasse nenne ich MessageHub

Weiter implementiere ich die Interfaces IConnected und IDisconnect um auf Connect-, Disconnect- und Reconnect-Ereignisse reagieren zu können.

Zusätlich stelle ich 2 Funktionen bereit:

  • JoinGroup: Fügt den Client zu einer Gruppe hinzu
  • SendMessage: Sendet eine Nachricht an alle verbundenen Clients innerhalb der Gruppe

Das Ergebnis sieht dann so aus:

public class MessageHub : Hub, IConnected, IDisconnect
{
// Liste aller verbundenen Clients und der zugeordneten Gruppen
private static Dictionary<string, string> _GroupMembers = new Dictionary<string, string>();

/// <summary>
/// Methode, um einer Gruppe beizutreten
/// </summary>
/// <param name=“group“>Name der Gruppe</param>
public void JoinGroup(string group) {
Groups.Add(Context.ConnectionId, group);
_GroupMembers[Context.ConnectionId] = group;
}

/// <summary>
/// Sendet eine Nachricht innerhalb der Gruppe, in der der Client
/// registriert ist
/// </summary>
/// <param name=“message“>Nachricht, die verteilt werden soll</param>
public void SendMessage(string message)
{
Clients[_GroupMembers[Context.ConnectionId]].receive(message);
}

/// <summary>
/// Wird aufgerufen, wenn der Client getrennt wird
/// </summary>
/// <returns></returns>
public System.Threading.Tasks.Task Disconnect()
{
_GroupMembers.Remove(Context.ConnectionId);
return Clients.leave(Context.ConnectionId);
}

/// <summary>
/// Wird aufgerufen, wenn ein Client verbunden wurde
/// </summary>
/// <returns></returns>
public System.Threading.Tasks.Task Connect()
{
return Clients.joined(Context.ConnectionId, DateTime.Now.ToString());
}

/// <summary>
/// Wird aufgerufen, wenn ein Client erneut verbunden wird
/// </summary>
/// <param name=“groups“></param>
/// <returns></returns>
public System.Threading.Tasks.Task Reconnect(IEnumerable<string> groups)
{
return Clients.rejoined(Context.ConnectionId, DateTime.Now.ToString());
}
}

Auf der Clientseite wird es dann noch viel einfacher. Das SignalR-Framework erzeugt Javascript-Code, der über die URL „~/signalr/hubs“ eingebunden werden muss. Zusätzlich braucht man das JS-Framework, das über den Pfad „~/Scripts/jquery.signalR-0.5.3.js“ verfügbar ist.

Hier kann dann auf die Methoden (joinGroup und sendMessage – ACHTUNG, die Methoden beginnen mit kleinen Buchstaben) zugegriffen werden.

Über jQuery ist SignalR über $.connection erreichbar. Der erstellte Hub fügt sich nahtlos mit seinem Namen in diesen Kontext ein: $.connection.messageHub.

$(function () {
var messageHub = $.connection.messageHub;
messageHub.name = „room1“;
/* Wird aufgerufen, wenn eine Nachricht vom Server gesendet wird */
messageHub.receive = function (message) {
$(„#messages“).append(„<br />“ + message);
}

$(„#send-button“).click(function () {

/* sendet eine Nachricht an den Server*/
messageHub.sendMessage($(„#text-input“).val());
});
/* starten der Connection */
$.connection.hub.start({
/* Callback, wenn die Verbindung aufgebaut wurde */
callback: function (){
/* Einer Gruppe beitreten */
messageHub.joinGroup(messageHub.name);
}
});
}); /* end $(function) */

Und das war es im Prinzip…

C#: Operationen asynchron ausführen

Nachdem ich lange nicht’s mehr geschrieben habe dachte ich mir heute, es wird mal wieder Zeit eine Lösung zu einem Problem zu posten. Was mich heute beschäftigt ist, wie ich es schnell und einfach schaffe einen oder mehrer Operationen asynchron auszuführen, ohne mich jedesmal um das Erstellen eines neuen Thread kümmern zu müssen.

Meine Anforderung

war es ein Objekt zu schaffen, dass einen neuen Thread erzeugt und eine Funktion aufruft, die mit dem übergebenen Objekt arbeitet – das Ganze soll dann natürlich auch noch TypeSafe sein und ich möchte mitbekommen, ob die Operation erfolgreich gewesen ist, oder eben nicht.

Der Ansatz

ist denkbar einfach. Eine Klasse – AsyncProcess – stellt eine Funktion Start und einen Event DoWork bereit.

  • Start nimmt das Objekt, mit dem gearbeitet werden soll, entgegen nimmt, plus ein EventWaitHandle – Objekt, um die Fertigstellung der Verarbeitung zu signalisieren.
  • DoWork wird innerhalb eines neuen Threads gestartet und erhält allen notwendigen Parametern, also das Objekt, das für die Verarbeitung benötigt wird, und ein AsyncProcessContext-Objekt, das EventWaitHandle bereit stellt (Mehr zu dem AsyncProcessContext-Objekt nach dem Code-Beispiel).

Das Ganze schaut dann erstmal so aus:

public class AsyncProcess<T> {
    public void Start(T data, EventWaitHandle waitHandle) {
        AsyncProcessContext ctx = new AsyncProcessContext(waitHandle);
        ctx.OnError += new Action<Exception>(handleError); /* Dazu später mehr */
        Thread thread = new Thread(() => DoWork(data, ctx));
        thread.Start();
    }
    public event Action<T, AsyncProcessContext> DoWork;
}

Das AsyncProcessContext ist zum einen dafür gedacht, Objekte, die im Kontext der Verarbeitung benötigt werden, wie das EventWaitHandle, an die aufzurufenden Methode zu übergeben und verfügbar zu haben, zum anderen ist es, für denn Fall, dass später noch das ein oder andere Objekte, z.B. eine Transaction, hinzukommst, schnell erweiterbar.

Der AsyncProcessContext stellt 2 Methoden bereit um die Verarbeitung abzuschließen:

  • Abort(Exception reason) wird aufgerufen wenn eine Exception während der Ausführung aufgetreten ist
  • Complete() signalisiert, dass die Operation fehlerfrei ausgeführt wurde

Diese Methoden machen natürlich nur dann Sinn, wenn man damit auch etwas anfangen kann. Deshalb habe ich dem Kontext noch 2 Events spendiert, die entsprechend ausgeführt und von dem AsyncProcess-Objekt behandelt werden:

  • event Action OnSuccess: Wird ausgelöst wenn Complete() aufgerufen wurde
  • event Action<Exception> OnError: Wird ausgelöst wenn Abort(ex) aufgerufen wurde

Das AsyncProcessContext-Objekt sieht demnach so aus:

public class AsyncProcessContext {
    public event Action OnSuccess;
    public event Action<Exception> OnError;

    private EventWaitHandle WaitHandle;

    public AsyncProcessContext(EventWaitHandle waitHandle) {
        this.WaitHandle = waitHandle;
    }

    public void Abort(Exception reason) {
        if (this.OnError != null) { this.OnError(reason); }
        this.complete(true);
    }

    public void Complete() {
        if (this.OnSuccess != null) { this.OnSuccess(); }
        this.complete(false);
    }

    private void complete(bool hasErrors) {
        this.WaitHandle.Set();
    }
}

Damit erklärt sich dann auch die Zeile ctx.OnError += new Action<Exception>(handleError); in der Start-Methode, welche allerdings wenig Sinn macht, da der Event noch nicht behandelt wird:

void handleError(Exception ex) {
    this.Successful = false;
    this.Exception = ex;
}

Die Properties Successful und Exception dürfen dem AsyncProcess-Objekt natürlich nicht fehlen.

Über diese kann abschließend sehr einfach überprüft werden ob eine Operation erfolgreich gewesen ist, oder nicht – und für den Oder-Fall kommt man auch schnell an die zugrunde ligende Exception.

Der Einsatz

So sieht es dann Live aus:

    EventWaitHandle[] h = new EventWaitHandle[] {
        new EventWaitHandle(false, EventResetMode.ManualReset),
        new EventWaitHandle(false, EventResetMode.ManualReset)
    };

    AsyncProcess<string> a1 = new AsyncProcess<string>();
    AsyncProcess<string> a2 = new AsyncProcess<string>();

    a1.DoWork += (a, ctx) => {
        ctx.Complete();
    };
    a2.DoWork += (a, ctx) => {
        Thread.Sleep(1000);
        ctx.Abort(new Exception("Einfach so..."));
    };

    a1.Start("test", h[0]);
    a2.Start("test", h[1]);

    WaitHandle.WaitAll(h);

    Debug.WriteLine("a1: " + a1.Successful.ToString());
    Debug.WriteLine("a2: " + a2.Successful.ToString());