Webapplikationen mit C# und JavaScript

Duett der Sprachen
Kommentare

Moderne Webapplikationen stellen hohe Ansprüche an den Entwickler: Neben der serverseitigen Programmierung werden auf dem Client inzwischen nicht nur rudimentäre Funktionen erwartet, sondern es sind gleich ganze Programmteile zu realisieren. Die Herausforderung liegt in den völlig verschiedenen Umgebungen, Browser und Server, die optimal kooperieren müssen. Dieser Artikel zeigt, welche Komponenten einer Webapplikation jeweils auf Server und Client verteilt sind und was dabei zu beachten ist.

Der Trend der letzten Jahre, komplexe Applikationen im Client zu platzieren, führt eine Entwicklung fort, die vor vielen Jahren mit einfachen Skripten angefangen hat. Der Eindruck, den solche Applikationen auf den ersten Blick hinterlassen, täuscht aber meist. Denn tatsächlich sind es keine komplexen Programme, die völlig losgelöst im Browser agieren und ab und an mal einen kleinen AJAX-Call absetzen, um Daten nachzuladen oder ein Formular zu senden. Anwendungen wie Google Docs [1], Microsoft Office 365 [2] oder Zoho [3] verarbeiten die Daten zu einem Großteil nicht im Client, sondern auf dem Server. Es ist ein raffiniertes Spiel zwischen beiden Enden, bei dem es darum geht, die Bandbreite effektiv zu nutzen, die Leistung von Client und Server zu beachten und die inhärente Skalierbarkeit solcher Systeme auch zu erhalten.

Über die Kunst der Verteilung solcher Anwendung gibt es mehrere Philosophien. JavaScript-Frameworks wie Senchas Ext JS [4] verfolgen einen stark clientorientierten Ansatz: Bis hin zu Datenbindung und MVC-Strukturen wird alles im Client aufgebaut. Der Server liefert lediglich Rohdaten über meist traditionell ausgelegte Webdienste. Der Vorteil dieses Prinzips liegt in der Konsistenz des Ansatzes. Die Servertechnologie rückt in den Hintergrund und kann leicht ausgetauscht werden. Dafür sind die Ansprüche an den Client enorm. Manche Browser in Smartphones und Tablets verweigern sich solchen Applikationen gern. Darüber hinaus sind die Herausforderungen an die Entwickler des Clients groß, und gerade hier hat JavaScript doch einige Schwächen. Andere Frameworks, wie jQuery [5], verfolgen einen eher schlanken Ansatz. Hier wird nur die unmittelbare Interaktion mit dem Nutzer clientseitig abgebildet, z. B. die erste Validierungsstufe, Pop-up-Dialoge und AJAX-Aufrufe. Die Bibliothek vereinfacht das Kodieren für den Entwickler jedoch signifikant. Im Kern läuft die eigentliche Verarbeitung dagegen ganz klassisch auf dem Server ab.

Die bereits erwähnten Office-Applikationen folgen letztlich alle mehr oder weniger direkt dem zweiten Ansatz. Ein guter Grund, der Technologie etwas weiter auf den Grund zu gehen und sie für eigene Projekte nutzbar zu machen.

Struktur und Verteilung

In der ASP.NET-Welt bietet sich inzwischen fast immer MVC als Pattern für die Anwendungsentwicklung an. Das heißt aber nicht, dass das Prinzip nicht auch auf das klassische ASP.NET übertragbar wäre. Netterweise hat Microsoft in den Vorlagen für Visual Studio bereits eine (etwas veraltete) jQuery-Version und ein paar eigene Ergänzungen für die Validierung mit eingebaut. Von einer echten Applikation ist man damit aber noch relativ weit entfernt. Vor allem die konkrete Aufteilung von Funktionen auf Client und Server fällt in der Regel schwer. Es gilt schließlich eine ganze Reihe von Ansprüchen unter einen Hut zu bringen, die sich durchaus widersprechen. Eine systematische Betrachtung kann hier helfen. Zu beachten sind die folgenden Punkte:

  • Leistungsfähigkeit und Spektrum der Clients (PC, Tablet, Smartphone)
  • Verfügbare oder erwartete Bandbreite (LAN, DSL, UMTS, EDGE usw.)
  • Komplexität und Testansprüche
  • Designphilosophie (klassische, einfache WebApp oder Ablösung einer WinForms-Applikation)

Die größte Schwachstelle aller verteilten Systeme ist der Weg zwischen den Endpunkten. Ist die Bandbreite unzureichend, die Verbindung instabil oder zeitweilig nicht verfügbar, kann die Nutzung im Desaster enden. Die Lösung sollte unter diesen Umständen nicht permanent auf eine Serververbindung angewiesen sein. Die Ansprüche an den Client sind daher hoch, letztlich müssen Daten eine gewisse Zeit vorgehalten, bereitgestellt und gespeichert werden. Die Übertragung erfolgt in kleineren Portionen, mit automatischer Wiederholung und sorgfältigem Überwachen des Erfolgs. Sauber aufgebaute Systeme belästigen den Nutzer nicht mit diesen Problemen, lassen ihn aber auch nicht im Unklaren. Ein schönes Beispiel dafür ist Google Docs [1] (Abb. 1). Trotz des stark serverbasierten Ansatzes ist der Client leistungsfähig genug, um auch bei Verbindungsproblemen stabil zu laufen. Allerdings schummelt Google ein wenig, denn einige Funktionen sind auf den hauseigenen Browser Chrome beschränkt.

Ist der Client sehr schwach, was vor allem mobile Geräte betrifft, kann ein solcher clientseitiger Anspruch kritisch sein. Die Kunst besteht darin, das Applikationsdesgin insgesamt hinreichend einfach zu halten und dennoch alle Störfälle mit einzukalkulieren.

Abb. 1: Google Docs als Referenzbeispiel (wenn auch garantiert ohne C#)
Abb. 1: Google Docs als Referenzbeispiel (wenn auch garantiert ohne C#)
Unvereinbares Vereinen

Die Einleitung deutete schon an, was die Lieblingssprachen der Endpunkte manifestieren: Hier sollen zwei Welten zusammenarbeiten, die nicht zusammenpassen. Aber was nicht passt, wird passend gemacht, und als Entwickler steht man im Zentrum der Macht.

Der erste Ansatz besteht im Auflösen der Abhängigkeiten. Das ist einerseits alles, was sich in Richtung Datenbank und Geschäftslogik abspielt. Dies sollte immer auf dem Server stattfinden, dort komplett verarbeitet und gegebenenfalls vorgehalten werden. REST-basierte Aufrufe direkt vom JavaScript-Client auf die Datenbank sind schnell, aber eigentlich tabu. Geht etwas schief, hat keine Seite eine echte Chance, adäquat darauf zu reagieren. Deshalb verbleibt das (Data-)Model gekapselt auf dem Server und enthält die nötige Logik, während das View Model den passenden clientseitigen Code erzeugt. So bekommt man auf der Serverseite den Client in den Griff. Dieses Gesamtmodell zeigt Abbildung 2. Es setzt voraus, dass alle Seiten konsequent demselben Prinzip folgen, was generell eine gute Idee ist. Die View liefert den Rahmen, Skripte und Struktur der Seite. Alle dynamischen Inhalte werden konsequent über Partial Views abgerufen, die sich auf das eigentliche Datenmodell stützen.

Abb. 2: Ablauf der Skripterzeugung und -ausführung
Abb. 2: Ablauf der Skripterzeugung und -ausführung

Die Views, die ASP.NET MVC bietet, sind letztlich ein Kompromiss, wie so ziemlich alles in der Webwelt. Produzieren müssen sie Folgendes:

  • Einen kleinen statischen HTML-Rahmen, gegebenenfalls mit statischen Daten
  • Die passenden CSS-Anweisungen
  • Präparierte JavaScript-Bibliotheksaufrufe

Der Controller bildet das Backend der AJAX-Aufrufe, wobei immer JSON zum Einsatz kommen sollte. Damit steht alles bereit, um eine Applikation schnell aufzubauen. Der Controller produziert ein View Model, dass alles enthält, was erforderlich ist, um clientseitig weitere Daten vom selben Controller nachzufordern. Er produziert also quasi seinen eigenen Aufruf.

Listing 1 zeigt einen nach diesem Prinzip entworfenen Controller. Der Controller selbst ist eher unspektakulär, die Arbeit erfolgt in der Klasse ScriptModel, die hier benutzt wird, um die Skriptsequenzen vorzubereiten (zu finden in Listing 2; Abbildung 3 zeigt das gerenderte Ergebnis). Der Nutzer des Modells entscheidet durch ein paar Parameter, wie das finale Skript aussieht. Dazu gehört das Cache-Verhalten (Cache), die Action-Methode (Url), die ID des Ziels des AJAX-Aufrufs (SuccessTarget) und nicht zuletzt die HTTP-Methode (Method), die mit dem Attribut [HttpPost] oder [HttpGet] auf der Ziel-Action übereinstimmen muss. Die durch den erzeugten AJAX-Aufruf benutzte Methode – hier ist dies SelectData() – führt nun die Datenoperationen aus und liefert den Teil der HTML-Seite, der ausgetauscht werden soll, zurück. Generiert wird er aus einer Partial View mit klassischer Übergabe des Models (hier ganz primitiv eine Liste, in der Praxis dürfte es öfter eine Datenbankabfrage sein).

Listing 1: Der Controller, der mit sich selbst redet

public class HomeController : Controller {

  public ActionResult Index() {
    ViewBag.Message = "Welcome to ASP.NET MVC!";
    var sm = new ScriptModel {
      Select = new AjaxCall { Cache = false, 
                              Url = Url.Action("SelectData"), 
                              SuccessTarget = "ajaxzone", 
                              Method = "POST" }
    };
    return View(sm);
  }

  [HttpPost]
  public ActionResult SelectData(string filter) {
    var data = new List(
        new string[] { "Element1", "Element2", 
                       "Element3", "Element4" });
    var result = data
       .Where(d => String.IsNullOrEmpty(filter) 
                || !d.EndsWith(filter)).ToList();
    return PartialView("Data/_Select", result);
  }

  public ActionResult About() {
    return View();
  }
}  
Listing 2: Das ScriptModel, statt Daten gibt’s erst einmal JavaScript

public class ScriptModel {
  public AjaxCall Select { get; set; }
  public AjaxCall Delete { get; set; }
  public AjaxCall Update { get; set; }
}

public class AjaxCall {

  public MvcHtmlString GetCall(string callback) {
    return new MvcHtmlString(
      String.Format(@"
      var value = $('#{1}').val();
      $.ajax({{
        url : '{0}',
        data : JSON.stringify({{ {1} : value }}),
        contentType: 'application/json; charset=utf-8',
        type: '{2}',
        cache: {3},
        dataType: 'html',
        success: function (data) {{
          $('#{4}').html(data);
          {5};
        }},
        error: function (data) {{
          alert(data);
        }} 
      }});
    ", Url, "filter", Method, Cache.ToString().ToLower(), SuccessTarget, callback));
  }

  public string Url { get; set; }
  public string Method { get; set; }
  public bool Cache { get; set; }
  public string SuccessTarget { get; set; }
  public string ErrorMethod { get; set; }
}  

Abb. 3: Der erzeugte Skriptcode in der Quellcodeansicht des Browsers
Abb. 3: Der erzeugte Skriptcode in der Quellcodeansicht des Browsers
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -