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();
            });