Freitag, 4. Mai 2012

Mehrere Sites im IIS 7 ohne Wildcard Zertifikat an https binden

Bei der Verwendung eines Nicht-Wildcard SSL Zertifikats im IIS 7, hat man leider keine Möglichkeit das Bindung von einem Hostheader abhängig zu machen. Der Konfigurationsdialog lässt keine entsprechende Eingabe zu:
Man hat aber die Möglichkeit mit einem kleinen Trick, die Eingabe des Hostnamens zuzulassen. Dazu wie folgt vorgehen:
Launch the Microsoft Management Console (MMC)
Select Start –> Run
Type in “MMC” and hit enter
From the console, select File –> Add / Remove Snap-in
Select Certificates from the Add / Remove dialog
Select Computer Account when prompt for which certificates the snap-in will manager.
Select Local Computer when prompted
Click OK to add the Snap-in to the MMC
Locate your SSL certificate
For self-signed (SELFSSL), look in Personal
For installed / purchased, look in the appropriate folder the certificate was originally installed in
Right click on the certificate and select properties
Edit the Friendly Name field so the name starts with an *
Der Dialog in der Management Konsole sieht folgendermaßen aus:


Wenn man anschließend im IIS die https Bindung hinzufügt und das entsprechende Zertifikat auswählt hat man auch die Möglichkeit den Hostnamen einzutragen. Der IIS nimmt durch den * am Anfang des Anzeigenamens des Zertifikats an, dass es sich um ein Wildcard Zertifikat handelt.

Zwar bekommt man im Browser natürlich eine Fehlermeldung, da der Zertifikatname nicht mit dem Servernamen übereinstimmt, aber trotzdem kann man auf diese Weise ein Nicht-Wildcard Zertifikat dazu verwenden, mehrere Sites im IIS 7 an eine IP über https zu binden.

Freitag, 20. April 2012

ASP.NET MVC: Content bei 4xx Antworten zurückgeben

Wenn man in einer Action einen HTTP-Fehler generiert, dann setzt der IIS als Content immer seine Standardmeldung.

Beispiel:
public ActionResult MyAction()
{
    Response.StatusCode = 409; // Conflict
    return Content("A steht mit B im Konflikt!", "text/plain")
}

Die Antwort die man beim Abruf der Action kriegt ist nicht wie zu erwarten "A steht mit B im Konflikt!", sondern "Seite kann aufgrund eines Konfliktes nicht angezeigt werden", wobei die Sprache der Meldung abhängig Ländereinstellung des Servers ist.

Die Lösung: Response.TrySkipIisCustomErrors = true;
public ActionResult MyAction()
{
    Response.StatusCode = 409; // Conflict
    Response.TrySkipIisCustomErrors = true;
    return Content("A steht mit B im Konflikt!", "text/plain")
}

Freitag, 16. März 2012

git+putty: fatal: The remote end hung up unexpectedly

Wenn git beim Zugriff auf ein Repository via SSH (mittels plink) den Fehler "fatal: The remote end hung up unexpectedly" meldet kann dies auch daran liegen, dass man eine Putty-Session hat die wie die URL des Repositories heißt.

Ist die URL z.B. ssh://git.mydomain.com/reponame.git, dann darf man keine Putty-Session mit dem Namen git.mydomain.com haben, da sonst obiger Fehler auftritt.

Mittwoch, 8. Februar 2012

Virtual Server und SQL Server (Express)

Gerne hosten wir auf einem externen Virtual Server mehrere Anwendungen im IIS sowie eine SQL Server Express Instanz. Eigentlich kein großes Problem, auch wenn der vServer nur 2GB Speicher hat.
Allerdings holt sich der SQL Server auch gerne mal fast den gesamten freien Speicher. Für den Fall der Fälle - man weiß ja nie. Ungünstig wenn dieser Speicher aber auch nicht rechtzeitig wieder freigegeben wird, wenn andere Anwendungen ihn benötigen. So verabschiedet sich dann der IIS regelmäßig und lieferrt höchstens noch ein "503 - Service unavailable" an seine Außenwelt.
Dieses Verhalten kann man dem SQL Server abgewöhnen, indem man ihm vorschreibt, wieviel Speicher er sich maximal holen soll. Folgende Statements sorgen dafür, das Maximum auf 512 MB zu beschränken:
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'max server memory', 512;
GO
RECONFIGURE;
GO
Anschließend den SQL Server (SQLEXPRESS) Dienst neu starten, und schon ist er nicht mehr so hungrig.

Dienstag, 31. Januar 2012

Error 405 bei HttpDelete Requests

Wenn in einer Web-Anwendung auf einem IIS 7.5 bei HttpDelete Requests der Fehler Code 405 auftritt ist das meist ein Indiz dafür, dass das standardmäßig beim IIS 7.5 installierte WebDav Modul dazwischen funkt.
Dieses sollte dann in der web.config wie folgt ausgesachaltet werden:
<modules runAllManagedModulesForAllRequests="true">
    <remove name="WebDAVModule" />
</modules>
Siehe auch:  http://stackoverflow.com/questions/1760607/asp-net-mvc-got-405-error-on-http-delete-request

Donnerstag, 1. Dezember 2011

Spaß mit QueryString

Einen QueryString zu ändern kann gar nicht so leicht sein. Beispielsweise ist Request.QueryString readonly. Daher kann man nicht einfach Request.QueryString.Remove("parameterDerMichStoert") aufrufen.

Eine Option ist es Request.QueryString.ToString() zu verwenden und dann dem ganzen mit regulären Ausdrücken zu Leibe zu rücken. Es gibt aber eine IMHO schönere Möglichkeit. Man kopiert sich einfach die Werte in eine NameValueCollection
var nameValueCollection = new NameValueCollection(Request.QueryString);
nameValueCollection.Remove("page");
Falls man den QueryString schon in string-Form vorliegt, kann mit HttpUtility.ParseQueryString(...) eine entsprechende NameValueCollection erstellt werden.

Bleibt allerdings noch ein Problem. Die ToString()-Methode von NameValueCollection liefert uns keinen QueryString. Im Netz finden sich viele Extension-Methods die behaupten dies zu können, allerdings waren alle die ich gefunden habe fehlerhaft. Das Problem waren immer mehrfache Werte für einen Parameter. Anstatt z.B. "customerId=1&customerId=2" haben die Methoden "customerId=1,2" ausgegeben.

Genug gelabert, hier die Extension-Method:
public static string ToQueryString(this NameValueCollection nameValueCollection)
{
 var parts = new Collection();
 foreach (string name in nameValueCollection)
 {
     var values = nameValueCollection.GetValues(name);
     if (values == null) continue;
     foreach(var value in values)
     {
         parts.Add(string.Format("{0}={1}", name, HttpUtility.UrlEncode(value)));
     }
 }
 return String.Join("&", parts);
}

Server.MapPath() mal anders

Server.MapPath(...) ist bekanntlich eine praktische Methode, um in Controllern und Views physische Pfade für Assets zu bestimmen.

Und wenn man kein Server-Objekt hat?

Sofern der Code im Rahmen eines Requests ausgeführt wird kann man einfach über den HttpContext gehen:

HttpContext.Current.Server.MapPath(...)

Was aber, wenn man z.B. in einem statischen Konstruktor oder sonst wie außerhalb eines Requests einen Pfad bestimmen muss. Auch hier gibt es eine Lösung:

HostingEnvironment.MapPath(...)

Prinzipiell könnte man wohl an allen Stellen an denen man Server.MapPath(...) verwendet auch HostingEnvironment.MapPath(...) nehmen.