Tipps, Tricks und weitere nützliche Dinge zu ASP.NET MVC3

Die neuste Version des MVC Frameworks aus dem Hause Microsoft ist nun schon ein paar Tage alt. Zeit, ein paar interessante Dinge zusammenzufassen. Ich werde auf die neue View Engine ‚Razor‘ eingehen, etwas zu neuen Möglichkeiten in Controller und Model sagen und die Vereinfachung der Verwendung von DI/IoC kurz anreißen.

ViewEngine Razor

Neue Verwendung von Codeblöcken und Variablen

In Razor werden alle Variablen, oder allgemeiner, alle Codeblöcke mit einem @ eingeleitet. In der ASP.NET Engine hat man dies mit den spitzen Klammern und einem Prozentzeichen gemacht.

Razor: @meineVariable.Eigenschaft
ASP.NET: 

Soll nun ein @ als Zeichen im Text ausgegeben werden, führt dies zu einem Compilerfehler, da dieser nach einer Variable mit dem Namen sucht.

[html]

Der Twitterlink lautet @myTwitter.

[/html]

Um dies zu umgehen, verwendet man das Escapezeichen @@, um ein @ als Zeichen auszugeben.

[html light=“true“]

Der Twitterlink lautet @@myTwitter.

[/html]

Kommentare gibt man in Razor mit @* ... *@ an. Ein using-Statement wird mit @using pro Zeile eingeleitet.

Expliziter Codeausdruck

Bei der Verwendung von Codesnippets in der HTML-Seite (.cshtml), kann es zu Fehldeutungen kommen. Möchte man beispielsweise eine Quelle einem img-Tags mittels Variable angeben, den typ jedoch fest vorgeben, erkennt der Compiler nicht die Grenze zwischen C# und HTML. Hier ein kurzes Beispiel.
[html]

[/html]
Hier versucht der Compiler in der Instanz personid die Eigenschaft png aufzurufen. Diese gibt es natürlich nicht und somit wird ein Fehler geworfen. Um dies zu umgehen, setzt man den Variablennamen in Klammern, um die Grenze des C#-ausdrucks zu verdeutlichen. Der folgende Codeblock zeigt die richtige Verwendung.
[html]

[/html]

Textausgaben in einem Codeblock

Schreibt man einen größeren Codeblock als einen Einzeiler in der HTML-Datei verwendet man hierzu einen Klammerausdruck.
[csharp]
@{
if (true) {
DoSomething();
Bin fertig.
}
}
[/csharp]
Soll nun Text in diesem Codeblock ausgegeben werden, wie oben mit der Textzeile unter DoSomething() dargestellt, erkennt der Compiler nicht den Unterschied von C# und reinem Ausgabetext.
Hierfür gibt es zwei Möglichkeiten.
1. Man schreibt den Text in HTML-Tags wie p, span oder div. Hierbei wird der Text erkennt und die View rendert die Ausgabe entsprechend. Ein kleiner Nachteil ist, dass man keinen reinen Text, sondern immer Text in speziellen Tags ausgibt.
2. Schreibt man den Text in das Tag …, wird der text ohne Angabe von Tags gerendet und als reines Literal ausgegeben.

Neue Html-Helper

Html-Raw()
Mit Raw() kann man die Html-Codierung des Textes umgehen. Der Text wird also wie gespeichert ausgegeben. Aber Vorsicht, hier kann XSS ganz schnell zuschlagen!
Html.Partial()
Im Gegensatz zu Html.RenderPartial() wird hierbei nichts in den Ausgabestrom der Renderengine geschrieben, sondern ein String zurückgegeben, welcher an der Stelle in das Dokument eingefügt wird.

Verwenden von Modellobjekten in Razor

Standardmäßig verwendet Razor ein dynamic als Modeltemplate. Das bedeutet, die Auswertung des Modellobjektes findet zur Laufzeit statt. Dies vereinfacht die Verwendung deutlich, erhöht aber auch das Risiko von fehlerhaften Aufrufen. Man kann aber auch ein strongly-typed model object angeben. dazu gibt man mit dem Ausdruck @model TYPE den Typ des Objektes an. Dann hat man auch Intellisense beim Schreiben zur Verfügung.

Sections in Razor

Mit der Methode @RenderSection() kann man einzelne Bereiche in eine Seite rendern lassen. Dies wird meist in die Layoutpage eingebunden, um Sections aus den Contentpages einzubinden. Dies erinnert stark an das ContentPlaceholder-Control in der ASP.NET Viewengine.
Um eine Section in die Layoutpage einzubinden, wird die Methode RenderSection mit dem Namen der Section aufgerufen. Zusätzlich kann man angeben, ob die Section optional ist.
[html]

@RenderSection(„inner_menu“, required: false)

[/html]
Alternativ kann man auch mit der Methode IsSectionDefinded() prüfen, ob die Section vorhanden ist. Die Verwendung würde dann folgendermaßen aussehen.
[html]

@if (IsSectionDefined(„inner_menu“)) {
@RenderSection(„inner_menu“)
}

[/html]

In der Contentpage wird die Section dann definiert.
[html]
@section inner_menu{

}
[/html]

Konfigurationseinstellungen in der web.config

Für Razor gibt es einen neuen Bereich, in dem die Einstellungen gesetzt werden, system.web.webPages.razor. Hier kann man die Standardnamespaces für alle Seiten aufnehmen, wie man das auch schon aus der ASP.NET Engine her kennt. Die Voreinstellungen findet man in der web.config, welche im Views-Ordner abgelegt ist. Hier sollte man auch die Einstellungen vornehmen.

Controller

Auch bei den Controllern gab es ein paar Neuerungen und Verbesserungen.

Action Filters

Mit den Actionfiltern kann man Code vor und nach einer Aktion ausführen lassen. Diese Filter können auf Controller und Aktionen angewendet werden. Möchte man einen eigenen Filter schreiben, so muss man von der Klasse ActionFilterAttribute ableiten. In MVC3 kann man die Filter nun auch global setzen. Somit sind diese für alle Controller gültig. Dies ermöglicht man, indem man den Filter der globalen Filterliste hinzufügt. Dies wird am besten in der global.asax.cs gemacht. Hier befindet sich in den MVC3-Projekten auch schon eine statische Methode namens RegisterGlobalFilters.
[csharp]
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyFilterAttribute());
}
[/csharp]

Neue ActionResults und Methoden zur Verwendung

RedirectPermanent
Mit der Methode RedirectPermanent(url) wird ein HTTP-301-Code an den Client gesendet, der angibt, dass beim Aufruf der Ressource an die in der Methode angegeben URL verwiesen werden soll.

HttpStatusCodeResult
Möchte man etwas mehr Freiheit bei der Angabe von Statuscodes haben, kann man die Methode HttpStatusCodeResult() verwenden, die einen Statuscode und eine Beschreibung aufnehmen kann. Dieser wird dann an den Browser gesendet.

HTML an Server senden

Ein kritisches Thema ist das Senden von HTML-Code an den Server. Dies öffnet immer eine Tür für XSS-Attacken. Wenn es aber mal nicht anders geht, kann man mit dem Attribut [ValidateInput(false)] die Überwachung für eine Aktion ausschalten. Aber Vorsicht, hier wird gar keine Überprüfung durch den Server durchgeführt. Jeglicher Code wird einfach entgegengenommen.
Möchte man etwas mehr Kontrolle über die Werte und deren Validierung haben, kann man mit dem Attribut [AllowHtml] einzelne Eigenschaften eines Modellobjektes aus der Überwachung ausschließen. Hier kurz die Verwendung der Attribute.
[csharp]
// — Controller —
[ValidateInput(false)]
public ActionResult Create(Person person)
{
//content in person.Description could be a XSS-Attack like
//…

//save person in database
}

// — Model —
class Person
{
public int Id {get; set;}
public string Name {get; set;}
[AllowHtml]
public string Description {get; set;}
}
[/csharp]

Modell

Bei der Validierung von Objekten gab es auch ein paar sehr nützliche Neuerungen. Besonders die Annotationen sind hier erweitert wurden.

Data Annotations

Mit der Annotation [Compare] kann man zwei Eigenschaften in einem Objekt miteinander vergleichen. Dies könnte man z.B. bei Passwörtern anwenden, um sicherzustellen, dass die Eingabe identisch ist.
[csharp]
class Person
{
public int Id {get; set;}
public string Password {get; set;}
[System.Web.Mvc.Compare(Password“)]
public string ConfirmPassword {get; set;}
}
[/csharp]

Reichen die vorhandenen Annotationen (Validierungsklassen) nicht aus, kann man sich einen eigenen Validator erstellen. Hierzu leitet man von ValidationAttribute ab und überschreibt die Methode IsValid(object).

Selbstvalidierende Modellobjekte

Wenn sich das Modellobjekt validieren soll, muss es das Interface IValidatableObject implementieren. Dieses beinhaltet die Methode Validate(ValidationContext), welche eine Auflistung von ValidationResults liefert. Wird kein Fehler gefunden, wird NULL zurückgegeben.

Remote Validation

Werden komplexe Validierungen notwendig, die auch das Abfragen eines externen Repositories erfordern, kann man sich mit einer RemoteValidation helfen. Diese wird wieder an die Eigenschaft des Modells geheftet. Dabei muss man den Controller und die Action zur Validierung angeben. Die Aktion muss ein JsonResult als Ergebnis liefern. Hierbei muss man auf die Möglichkeit zur Abfrage per GET achten. Dies wird durch das JsonRequestBehavior AllowGet ermöglicht.
[csharp]
// — Model —
class Person
{
public int Id {get; set;}
[System.Web.Mvc.Remote(„ValidateNumber“, „Person“, ErrorMessage=“Die Nummer ist nicht zulässig.“)]
public int Number {get; set;}
public string Password {get; set;}
[System.Web.Mvc.Compare(Password“)]
public string ConfirmPassword {get; set;}
}

// — Controller —
public JsonResult ValidateNumber(int number)
{
bool result = number % 2 == 0;
return Json(result, JsonRequestBehavior.AllowGet);
}
[/csharp]

Verwendung von DI/IoC

In MVC3 wurde die Schnittstelle IDependencyResolver eingeführt. Wenn man einen IoC Container einsetzten möchte, und die Komponente keinen eigenen Resolver mitbringt, was in naher Zukunft bestimmt überall der Fall sein wird, kann man selbst eine Resolverklasse schreiben, die die Schnittstelle implementiert. Ich setzte Ninject ein, welches mit der Assembly Ninject.MVC3 (über NuGet verfügbar) einen solchen Resolver schon mitbringt.
Dann ist die Verwendung noch einfacher.
[csharp]
protected void Application_Start()
{
// Area-, Filter- und Routes-Registration
//initialize DI-Container
NinjectServiceLocator sl = new NinjectServiceLocator(MyKernelFactory.GetKernel());
DependencyResolver.SetResolver(sl);
}

public static class KernelFactory
{
public static IKernel GetKernel()
{
bool isStaging = bool.Parse(ConfigurationManager.AppSettings[„staging“]);
NinjectModule module = isStaging ? new TestModule() as NinjectModule : new ProductionModule() as NinjectModule;
return new StandardKernel(module);
}
}

public class ProductionModule : NinjectModule
{
public override void Load()
{
Bind().To().InRequestScope();
}
}
[/csharp]

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s