Dieser Artikel wurde maschinell übersetzt.

Innovation

Erweiterung der kontextbezogenen Statusanzeige für ASP.NET MVC

Dino Esposito

Dino EspositoIn der Web-Welt bedeutet der Begriff "Fortschritt-Stab" zu viele verschiedene Dinge für verschiedene Menschen.Manchmal bezieht sich es auf statischen Text, dass irgendwo platzieren Sie einfach zeigt, die eine Operation stattfindet.Der Text stellt grundlegende Feedback für den Benutzer bereit und im wesentlichen lädt sie einfach entspannen und warten.Manchmal, die Fortschrittleiste zeigt eine einfache Animation beim Senden der gleichen Nachricht an den Benutzer – bitte warten — die mindestens konzentriert sich die Aufmerksamkeit des Benutzers weil Benutzer dazu neigen, die beweglichen Elemente folgen.Eine typische Animation zeigt ein grafisches Element, das um einen kreisförmigen oder linearen Pfad auf unbestimmte Zeit verschoben.Als bewegte Element das Ende des Pfades erreicht, wird die Animation beginnt und hält ausgeführt, bis der zugrunde liegenden Vorgang beendet, und die Animation wird programmgesteuert beendet.Dies ist ein recht häufiges Muster auf, z. B. die meisten Fluggesellschaft Web-Sites.

Im vergangenen Monat habe ich eingeführt eine Basisversion einer ASP.NET MVC-Framework — SimpleProgress Rahmen — damit können Sie eine wirklich kontextbezogene Statusanzeige schnell und effektiv einzurichten (msdn.microsoft.com/magazine/hh580729).Kontextbezogene ist wahrscheinlich nur, was eine Statusanzeige werden sollte.Das heißt, sollte ein Element der Benutzeroberfläche, die den Status einer laufenden Betrieb schrittweise beschreibt.Die progressive Anzeige lässt sich so einfach wie eine Folge von Nachrichten oder kann es noch zwingender – beispielsweise einem Messgerät.

In diesem Artikel werde ich zeigen, wie die Statusanzeige zu verbessern, indem Abbrechen Funktionen hinzufügen.Mit anderen Worten, wenn der Benutzer mit der Schnittstelle interagiert und der Vorgang abgebrochen wird, wird Rahmen anweisen, den Manager von den laufenden Betrieb, Arbeit zu unterbrechen.

Abbrechen von laufende Aufgaben

Abbrechen einer laufenden serverseitige Aufgabe in einem Clientbrowser ist keine triviale Operation.Täuschen Sie lasst euch nicht durch die sehr einfache Beispiele, die Sie finden, die nur die Clientanforderung abbrechen und behaupten, dass alles auch auf dem Server deaktiviert ist.

Wenn Sie eine Server-Betrieb über AJAX auslösen, wird ein Socket geöffnet, die Ihr Browser eine mit einem Remoteendpunkt Verbindung.Diese Verbindung bleibt offen, Wartezeiten für die Anforderung abzuschließen und eine Antwort zurückgibt.Der Trick ich letzten Monat diskutiert für einrichten eine kontextbezogene Statusanzeige eine parallele Strömung von Anforderungen verwendet, um eine zweite Controller-Methode verantwortlich für die Rückgabe des Status des Vorgangs überprüfen — Art eines Postfachs, die Client und Server verwenden können, um zu kommunizieren.

Jetzt angenommen, es gibt eine Abbrechen-Schaltfläche, mit dem der Benutzer den aktuellen Servervorgang abbrechen.Welche Art von Code wird dieses verlangen?Zumindest möchten Sie die AJAX-Anforderung abbrechen.Wenn über die jQuery AJAX API der Servervorgang gestartet wurde, können Sie Folgendes tun:

xhr.abort();

Die Xhr Variable ist der Verweis auf das XmlHttpRequest-Objekt im Aufruf verwendet. In jQuery wird dieser Verweis direkt von der $Schnipsel Funktion zurückgegeben. Abbildung 1 zeigt ein Auszug aus das Progress-Framework (umbenannte ProgressBar) des letzten Monats im Code, die ein neues hinzufügt abort-Methode.

Abbildung 1 Hinzufügen von Funktionalität, um das Progress Framework abbrechen

var ProgressBar = function () {
  var that = {};
  // Store the XHR object being used.
that._xhr = null;// Get the user-defined callback that runs after
// aborting the call.
that._taskAbortedCallback = null;
...// Set progress callbacks.
that.callback = function (userCallback, completedCallback,
                          abortedCallback) {
  that._userDefinedProgressCallback = userCallback;
  that._taskCompletedCallback = completedCallback;
  that._taskAbortedCallback = abortedCallback;
  return this;
};
// Abort function.
that.abort = function () {
  if (_xhr !== null)
       xhr.abort();
};
...
// Invoke the URL and monitor its progress.
that.start = function (url, progressUrl) {
  that._taskId = that.createTaskId();
  that._progressUrl = progressUrl;
  // Place the AJAX call.
xhr = $.ajax({
      url: url,
      cache: false,
      headers: { 'X-ProgressBar-TaskId': that._taskId },
      complete: function () {
        if (_xhr.status != 0) return;
        if (that._taskAbortedCallback != null)
            that._taskAbortedCallback();
        that.end();
      },
      success: function (data) {
        if (that._taskCompletedCallback != null)
            that._taskCompletedCallback(data);
        that.end();
            }
  });
  // Start the progress callback (if any set).
if (that._userDefinedProgressCallback == null)
      return this;
  that._timerId = window.setTimeout(
    that._internalProgressCallback,
    that._interval);
};
  return that;
}

Wie Sie sehen können, die neue Abort-Methode nicht viel über aufrufenden Abbruch auf das interne XmlHttpRequest-Objekt. Abbrechen eines laufenden AJAX-Aufrufs löst noch das complete-Ereignis auf die AJAX-Manager jedoch, obwohl keine Funktionen, Erfolg oder Fehler. Um zu erkennen, ob ein Vorgang vom Benutzer abgebrochen wurde, fügen Sie einen Handler für füllen Sie und überprüfen Sie die Statuseigenschaft des XmlHttpRequest-Objekts — es wird 0 sein, wenn der Vorgang abgebrochen wurde. Der vollständige Handler führt dann was Bereinigungsvorgang erforderlich ist – Timer, z. B. beenden. Abbildung 2 zeigt eine nette Schnittstelle aus dem Benutzer können remote-Operationen zu beenden.

Cancelable AJAX Operations
Abbildung 2 abbrechbaren Vorgänge in AJAX

Benachrichtigt den Server

Die schöne Benutzeroberfläche in Abbildung 2 garantiert nicht zwangsläufig, dass die Server-seitige als der Benutzer angefordert beendet wurde und als die Anwendung Feedback scheint zu beweisen. Calling Abbruch auf XmlHttpRequest schließt einfach den Socket, der den Browser an den Server eine Verbindung herstellt. Anders ausgedrückt, durch aufrufenden Abbruch auf XmlHttpRequest, Sie einfach sagen, du bist nicht mehr jede Antwort, die Server-Methode generiert möglicherweise, interessiert. Nichts wirklich garantiert der Server empfangen und bestätigt die Abbruchanforderung; wahrscheinlicher ist, wird der Server weiterhin verarbeitet die Anforderung unabhängig davon, ob ein Browser auf eine Antwort wartet. Während diesen besondere Aspekt auf unterschiedlichen Plattformen und Servern variieren kann, gibt es einen anderen Aspekt zu betrachten, das streng von der Anwendung abhängt. Wenn die Anforderung einen asynchronen Vorgang oder ein lang andauernder Vorgang ausgelöst, können Sie es beenden? Die Wahrheit ist, es gibt keine zuverlässige und automatische Möglichkeit, die Verarbeitung einer Anforderung zu stoppen; Sie müssen bauen Ihre eigenen Rahmen und schreiben Ihre Servermethoden unterbrechbare sein. Lassen Sie uns das Progress-Framework, dann erweitern.

Das Progress Framework erweitern

Die Server-seitige Manager-Komponente ist der Teil des Rahmens, die Controller mit zu arbeiten. Controller Methoden rufen Methoden für die folgende Schnittstelle Nachrichten für die Client-Statusanzeige und Benachrichtigungen von der Benutzeroberfläche Verarbeitung anzuhalten:

public interface IProgressManager
{
    void SetCompleted(String taskId, String format, params Object[] args);
    void SetCompleted(String taskId, Int32 percentage);
    void SetCompleted(String taskId, String step);
    String GetStatus(String taskId);
    void RequestTermination(String taskId);
    Boolean ShouldTerminate(String taskId);
}

Verglichen mit der Code ich letzten Monat, gibt es ein paar zusätzliche Methoden — RequestTermination, dem Kunden auf Verlangen Terminierung aufgerufen werden, und ShouldTerminate, welche Aktion Methoden um zu sehen, wenn sie angeblich zu stoppen und Rollback aufgerufen werden.

Jeder Fortschritt Direktor Werk über einen Datenanbieter, der den Status der ausstehenden Aufgaben enthält, identifiziert jedes mit einem Client generierte ID. Die Standard-Systemdatenanbieters im Quellcode verwendet die ASP.NET-Cache zum Speichern von des Status der Aufgaben. Es wird ein Eintrag für jede Aufgabe erstellt und speichert den Eintrag in ein Objekt vom Typ TaskStatus, etwa so:

public class TaskStatus
{
  public TaskStatus(String status) : this (status, false)
  {
  }
  public TaskStatus(String status, Boolean aborted)
  {
    Aborted = aborted;
    Status = status;
  }
  public String Status { get; set; }
  public Boolean Aborted { get; set; }
}

Wenn die Domänencontroller-Methode SetCompleted aufgerufen wird, landet es eine Statusmeldung für die Aufgabe im zugrunde liegenden Speicher speichern. Bevor Sie mit dem nächsten Schritt fortfahren überprüft die Controller-Methode, ob es zu abgebrochen hat.

Setzen sie alle zusammen: Der Server

Mal sehen, was es braucht, um eine Controller-Methode für einen mehrstufigen, überwachbare und unterbrechbare Vorgang erstellen. Sie beginnen mit einer Beispiel-Controller-Klasse, die von ProgressBarController erbt:

public class TaskController : ProgressBarController
{  public String BookFlight(String from, String to)
  {
    ...
}
}

Die Sample-Methode gibt eine Zeichenfolge für Einfachheit; kann es eine Teilansicht oder JSON oder was auch immer Sie benötigen werden.

Die base-Klasse, in Abbildung 3, ist eine einfache Möglichkeit, den endgültigen Controller mit einer Reihe von gemeinsamen Methoden zu verleihen.

Beachten Sie insbesondere, die Status und Abort-Methode, die eine öffentliche und standard-API für jQuery-Clients Aufrufen zur Abfrage des aktuellen Status und verlangen Terminierung definieren. Durch die Verwendung einer Basisklasse, vermeiden Sie, dass Code immer und immer wieder schreiben.

Abbildung 3 die ProgressBar Super-Klasse

public class ProgressBarController : Controller
{
  protected readonly ProgressManager ProgressManager;
  public ProgressBarController()
  {
    ProgressManager = new ProgressManager();
  }
  public String GetTaskId()
  {
    // Get the header with the task ID.
var id = Request.Headers[ProgressManager.HeaderNameTaskId];
    return id ??
String.Empty;
  }
  public String Status()
  {
    var taskId = GetTaskId();
    return ProgressManager.GetStatus(taskId);
  }
  public void Abort()
  {
    var taskId = GetTaskId();
    ProgressManager.RequestTermination(taskId);
  }
}

Abbildung 4 veranschaulicht das Muster für eine Controller-Methode, die vom Client überwacht werden muss.

Abbildung 4 überwachbare Controller-Methode mit dem Fortschritt-Framework

public String BookFlight(String from, String to)
{
  var taskId = GetTaskId();
  // Book first leg
  ProgressManager.SetCompleted(taskId,
    "Booking flight: {0}-{1} ...", from, to);
  Thread.Sleep(4000);
  if (ProgressManager.ShouldTerminate(taskId))
  {
    // Compensate here
    //
    return String.Format("One flight booked and then canceled");
  }
  // Book return flight
  ProgressManager.SetCompleted(taskId,     "Booking flight: {0}-{1} ...", to, from));
  Thread.Sleep(4000);
  if (ProgressManager.ShouldTerminate(taskId))
  {
    // Compensate here
    //
    return String.Format("Two flights booked and then canceled");
  }
  // Book return
  ProgressManager.SetCompleted(taskId,     "Paying for the flight ...", taskId));
  Thread.Sleep(5000);
  if (ProgressManager.ShouldTerminate(taskId))
  {
    // Compensate here
    //
    return String.Format("Payment canceled.
No flights booked.");
  }
  // Some return value
  return "Flight booked successfully";
}

Die Task-ID auf dem Client generiert und über einen HTTP-Header an den Server übertragen. GetTaskId-Methode abschirmt Controller Entwickler müssen diese Details kennen. Die Controller-Methode erledigt seine Arbeit schrittweise und ruft SetCompleted jedes Mal, wenn es ein Großteil der Arbeit leistet. Es zeigt die Arbeit mit einer Zeichenfolge, die einen Prozentsatz als auch eine Statusmeldung sein könnte. In regelmäßigen Abständen überprüft die Controller-Methode, ob eine Anforderung für die Kündigung eingegangen ist. Wenn dies der Fall ist, ist es, was möglich ist zu Rollback oder auszugleichen, und dann gibt.

Setzen sie alle zusammen: Der Client

Auf der Clientseite müssen Sie den Fortschritt Framework Java­Script API- und die jQuery-Bibliothek:

<script src="@Url.Content("~/Scripts/progressbar-fx.js")"
        type="text/javascript"></script>

Jedes überwachbare Methode wird über AJAX aufgerufen werden und wird daher durch ein clientseitiges Ereignis ausgelöst werden — z. B. auf eine Schaltfläche, mit Markup erstellt, wie in gezeigt Abbildung 5.

Abbildung 5 Markup für das Auslösen einer überwachbaren und unterbrechbaren Aktion

<fieldset>
  <legend>Book a flight...</legend>
  <input id="buttonStart" type="button" value="Book a flight..." />
  <hr />
  <div id="progressbar_container">
    <span id="progressbar2"></span>
    <input id="buttonAbort" type="button"
      value="Abort flight booking"
      disabled="disabled" />
  </div>   
</fieldset>
Click handlers are attached unobtrusively when the page is loaded:
<script type="text/javascript">
  var progressbar;
  $(document).ready(function () {
    $("#buttonStart").bind("click", buttonStartHandler);
    $("#buttonAbort").bind("click", buttonAbortHandler);
  });
</script>

Abbildung 6 zeigt das JavaScript zum Starten und Abbrechen ein Remotevorgang und die Benutzeroberfläche entsprechend aktualisieren.

Abbildung 6 JavaScript-Code für die Sample-Ansicht

function buttonStartHandler() {
  updateStatusProgressBar ();
  progressbar = new ProgressBar();
  progressbar.setInterval(600)
             .callback(function (status) {
                             $("#progressbar").text(status); },
                       function (response) {
                             $("#progressbar").text(response);
                             updateStatusProgressBar(); },
                       function () {
                             $("#progressbar").text("");
                             updateStatusProgressBar(); })
             .start("/task/bookflight?from=Rome&to=NewYork",
                    "/task/status",
                    "/task/abort");
}
function buttonAbortHandler() {
    progressbar.abort();
}
function updateStatusProgressBar () {
    $("#buttonStart").toggleDisabled();
    $("#buttonAbort").toggleDisabled();
}

Das ProgressBar-JavaScript-Objekt besteht aus drei Hauptmethoden.Die SetInterval-Methode gibt das Intervall zwischen zwei aufeinander folgenden Prüfungen für Status-Updates.Der Wert wird in Millisekunden angegeben.Die Callback-Methode legt ein Bündel von Callback-Funktionen für die Aktualisierung des Status und Aktualisieren der Benutzeroberfläche, wenn der Vorgang erfolgreich abgeschlossen wird oder der Vorgang vom Benutzer abgebrochen wird.Und die Start-Methode beginnt mit dem Vorgang.Es dauert drei URLs: der Endpunkt der die Methode ausgeführt und Endpunkte für die Methoden aufgerufen werden, zurück zu Status Aktuelles zu fangen und zu den ausstehenden Vorgang abzubrechen.

Wie Sie sehen können, sind die URLs relativ und in der Form/Controller/Methode zum Ausdruck.Natürlich können Sie ändern die Namen der Methode Status und, was auch immer Sie mögen Abbrechen — solange solche Methoden als öffentliche Endpunkte vorhanden sind.Die Status und Abort Methoden sind vorhanden, wenn Sie die Controllerklasse von ProgressBarController erben garantiert.Abbildung 7 zeigt die Beispielanwendung in Aktion.Obwohl die UIs in Abbildung 2 und Abbildung 7 gleich aussehen, die zugrunde liegenden Code und das Verhalten sind wirklich anders.

The Framework in ActionAbbildung 7 Rahmen in Aktion

Fortschritte

AJAX liefert die Werkzeuge, um den Server abfragt und Fragen, was vor sich geht.Sie müssen Ihr eigenes Framework erstellen, falls Sie es wollt um überwachbare und unterbrechbare Methoden bereitzustellen.Es sei darauf hingewiesen, dass in ein echtes Beispiel die Aktion-Methode selbst andere asynchrone Dienste aufrufen kann.Dieser Code kann kompliziert sein.Zum Glück, sollten asynchrone Code Schreiben einfacher in die bevorstehenden ASP erhalten.NET MVC 4.In meinem nächsten Artikel, ich werde zeigen, einen anderen Ansatz zur Umsetzung und Überwachung von Remotetasks basierend auf eine neue Client-seitige-Bibliothek, die auch für die ASP erleichtern könnte.NET MVC 4 Bündel — der SignalR Bibliothek.

Dino Esposito ist der Verfasser von „Programming Microsoft ASP.NET MVC3“ (Microsoft Press, 2011) und Mitverfasser von „Microsoft .NET: Strukturieren Anwendungen für Unternehmen"(Microsoft Press, 2008). Mit Sitz in Italien, ist Esposito ein gefragter Referent bei Branchenveranstaltungen weltweit. Sie können folgen ihm auf Twitter bei twitter.com/despos.

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Phil Haack