MVC4 WebAPI und knockoutjs – Teil 1 – Modelbinding


Um dynamische und flexible Oberflächen zu erstellen, lohnt es sich einen Blick auf die WebAPI in Verbindung mit knockoutjs zu werfen. Die Webseite von knockoutjs bietet hier bereits einen sehr guten Überblick und eine gute Einführung wie genau knockoutjs funktioniert, daher werde ich darauf nicht im Detail eingehen.

Mit Hilfe von MVC4, WebAPI und knockoutjs lassen sich MVC Oberflächen sehr einfach aktualisieren. Ich versuche es hier mit dem normalen knockoutjs + mapping Plugin, damit ist es möglich direkt das MVC Model in ein knockoutjs Model umzuwandeln.

Das folgende Beispiel zeigt, wie man ein Model erstellt und dann z.B. beim Speichern keinen Postback auslöst, sondern das ganze per Ajax Call abspeichern kann und die Oberfläche aktualisiert.

1. Erstellen eines passenden Models in C#, welches dann in ein JSON Objekt umgewandelt wird.

public class PersonModel
{
        [Display(Name = "ID")]
        public int Id { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Vorname")]
        public string Vorname { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Nachname")]
        public string Nachname { get; set; }
}

2. Wir nutzen einen normalen Controller und einen normalen View um unsere Daten in der Bearbeiten Ansicht darzustellen.

Wir passen den View aber bereits an unser knockoutjs Model an, d.h. wir Nutzen TextBoxFor, da wir hier zusätzliche HTML Attribute erstellen können. Wir erstellen hier “data_bind” welches dann im HTML als “data-bind” gerendert wird und setzten den passenden Value der aus unserem Model per knockoutjs angezeigt bzw. aktualisiert werden soll.

<div class="control-group">
     @Html.LabelFor(model => model.Vorname, new { @class = "control-label" })
     <div class="controls">
         @Html.TextBoxFor(model => model.Vorname, new { data_bind = "value: Vorname" })
         @Html.ValidationMessageFor(model => model.Vorname)
     </div>
</div>

3. Anlegen unseres API Controllers.

Hier gibt es zwei Funktionen, die beide das “gleiche” machen. Die Daten die wir mit dem Postback erhalten, werden in unser PersonModel “umgewandelt” und wir können können die Daten jetzt Speichern, …. Wir passen aber nur die ID an und geben unser Aktualisiertes Model wieder zurück, damit wir auch auf der Webseite sehen, das sich die ID geändert hat.

public class PersonEditApiController : ApiController
{
    /// <summary>
    /// POST api/PersonEditApi/SavePerson
    /// </summary>
    public HttpResponseMessage SavePerson(PersonModel value)
    {
        if (ModelState.IsValid)
        {
            if (value == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            value.Id = 5;
            //Wird automatisch in JSON Objekt umgewandelt und gibt den StatusCode 201 für Created zurück!
            HttpResponseMessage response = Request.CreateResponse<PersonModel>(HttpStatusCode.Created, value);
            response.Headers.Location = new Uri(Request.RequestUri, "/api/PersonEditApi/SavePerson/" + value.Id.ToString());
            return response;     
        }

        throw  new HttpResponseException(HttpStatusCode.BadRequest);
    }

    /// <summary>
    /// POST api/PersonEditApi/Save
    /// </summary>
    public PersonModel Save(PersonModel value)
    {
        if (ModelState.IsValid)
        {
            if (value == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            //Gibt den Statuscode 200 für OK zurück!
            value.Id = 5;
            return value;
        }

        throw new HttpResponseException(HttpStatusCode.BadRequest);
    }
}

4. Hinzufügen einer WebApi Route “AlternateWebApi”

Damit wir den PersonEditApiController auch so nutzen können wie wir es bisher von den MVC Controllern gewohnt sind, müssen wir noch eine weitere Route zu unserer WebApi hinzufügen. Denn mit der Standardroute für API aufrufe sind nur GET/POST/DELETE Abfragen möglich. Aber meist hat man mehr wie nur diese “paar” Abfragen die man für einen View benötigt, daher fügen wir noch eine neue Route vor der “DefaultApi” Route hinzu.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Alternatives Routing, hier ist es jetzt wieder möglich gezielte Actionen und Controller abzufragen
        //In der DefaultApi kann man nur Get/Post/Delete abfragen und ist damit sehr begrenzt was die Abfragen angeht.
        config.Routes.MapHttpRoute(
            name: "AlternateWebApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        //Standardroute  - hier lässt sich nur GET/POST/DELETE abfragen.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

5. Anpassen unseres JavaScript Codes im View ‘cshtml’, wo die Daten angezeigt werden sollen.

Das Aktuelle C# Model in ein JSON Model Serealisieren und mit dem knockoutjs Modelmapping direkt ein Model erstellen lassen und dieses an unser Formular binden.

Wenn der User den Submit Button klickt, wird unser knockoutjs Model wieder in JSON umgewandelt und vom JSON Format dann in einen String umgewandelt, welcher per API Aufruf übergeben wird. Aktuell werden hier zwei StatusCodes ausgewertet, da wir den Statuscode 200 bei der “Save” Abfrage zurückbekommen und die 201 bei “SavePerson”. Beide Aufrufe führen aber zum gleichen Ergebnis.

(Achtung es gibt auch eine ko.toJSON() Funktion, diese darf hier NICHT verwendet werden, da sonst ein Model erstellt wird welches nicht Valide mit unserem C# Objekt ist und unsere Funktion in der C# API als Model “null” übergeben bekommt.)

var mod12 = ko.mapping.fromJS(@(Html.Raw(JsonConvert.SerializeObject(Model))));
ko.applyBindings(mod12);

function CreatedSuccess(data) {
    ko.applyBindings(data);
}

$("form").submit(function (event) {
    event.preventDefault();
    var model = JSON.stringify(ko.toJS(mod12));

    $.ajax({
        url: "/api/personEditApi/Save",
        type: "POST",
        data: model,
        contentType: 'application/json; charset=utf-8',
        statusCode: {
            200 /* OK */: function (data) {
                CreatedSuccess(data);
            },
            201 /* Created */: function (data) {
                CreatedSuccess(data);
            }
        }
    });
});

 

Aktuell habe ich noch Probleme beim anzeigen und Konvertieren von Datumsangaben beim Modelbinding, daher verwende ich auch “JsonConvert” und nicht die Standard Json.Serialize Funktion von MS, denn dann werden nur Ticks angezeigt und mit JsonConvert wird das ISO Format für das Datum verwendet. Wenn ich diese Problematik in den Griff bekommen habe, wird es ebenfalls einen eigenen Post geben. Bzw. wenn jemand bereits Erfahrungen damit gesammelt hat freue ich mich gern über einen Kommentar :-).

Codeplex:

https://squadwuschel.codeplex.com/

Dann unter “Source Code” –> “Browse” –> “Testprojekte” –> “Mvc4WebApiKoTb” hier dann die Wichtigsten Dateien im Webprojekt unter “Views/Home/Person.cshtml”, “App_Start/WebApiConfig.cs” und “Controllsers/PersonEditApiController.cs”

Quellen:

http://www.west-wind.com/weblog/posts/2012/Aug/21/An-Introduction-to-ASPNET-Web-API

http://www.asp.net/web-api/overview

http://code.msdn.microsoft.com/ASPNET-Web-API-JavaScript-d0d64dd7/sourcecode?fileId=63598&pathId=212749278

http://knockoutjs.com/index.html

Ein Gedanke zu „MVC4 WebAPI und knockoutjs – Teil 1 – Modelbinding

  1. Pingback: MVC WebAPI und Serealisieren von Eingabedaten ohne Form Submit | SquadWuschel's Blog

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 )

Twitter-Bild

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

Facebook-Foto

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

Google+ Foto

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

Verbinde mit %s