3 Stimmen

Was sind die Verantwortlichkeiten der Komponenten in einem MVC-Muster für einen einfachen Login

Ich versuche, das MVC-Muster zu verstehen, und ich habe die allgemeine Vorstellung, dass das Model dafür verantwortlich ist, den Zustand zu erhalten, die Ansicht dafür verantwortlich ist, das Model darzustellen und der Controller dafür verantwortlich ist, das Model zu modifizieren und die entsprechenden Ansichten aufzurufen. Ich wollte versuchen, eine einfache ASP.NET MVC-Loginseite zu implementieren, die OpenID verwendet, um ein Verständnis dafür zu bekommen, wie das alles funktioniert.

Ich habe DotNetOpenAuth-3.4.6 heruntergeladen und mir die Beispiele angesehen, insbesondere deren MVC-Beispiel. Leider hat das Beispiel tatsächlich kein Modell, sondern nur einen Controller:

namespace OpenIdRelyingPartyMvc.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Security;
    using DotNetOpenAuth.Messaging;
    using DotNetOpenAuth.OpenId;
    using DotNetOpenAuth.OpenId.RelyingParty;

    public class UserController : Controller
    {
        private static OpenIdRelyingParty openid = new OpenIdRelyingParty();

        public ActionResult Index()
        {
            if (!User.Identity.IsAuthenticated)
            {
                Response.Redirect("~/User/Login?ReturnUrl=Index");
            }

            return View("Index");
        }

        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            return Redirect("~/Home");
        }

        public ActionResult Login()
        {
            // Stage 1: display login form to user
            return View("Login");
        }

        [ValidateInput(false)]
        public ActionResult Authenticate(string returnUrl)
        {
            var response = openid.GetResponse();
            if (response == null)
            {
                // Stage 2: user submitting Identifier
                Identifier id;
                if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
                {
                    try
                    {
                        return openid.CreateRequest(Request.Form["openid_identifier"]).RedirectingResponse.AsActionResult();
                    }
                    catch (ProtocolException ex)
                    {
                        ViewData["Message"] = ex.Message;
                        return View("Login");
                    }
                }
                else
                {
                    ViewData["Message"] = "Invalid identifier";
                    return View("Login");
                }
            }
            else
            {
                // Stage 3: OpenID Provider sending assertion response
                switch (response.Status)
                {
                    case AuthenticationStatus.Authenticated:
                        Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
                        FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
                        if (!string.IsNullOrEmpty(returnUrl))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("Index", "Home");
                        }
                    case AuthenticationStatus.Canceled:
                        ViewData["Message"] = "Canceled at provider";
                        return View("Login");
                    case AuthenticationStatus.Failed:
                        ViewData["Message"] = response.Exception.Message;
                        return View("Login");
                }
            }
            return new EmptyResult();
        }
    }
}

Vielleicht ist das Beispiel zu einfach, um tatsächlich ein Modell einzubeziehen, da alle Zustandsinformationen im Cookie enthalten sind. Daraufhin habe ich eine einfache Datenbank implementiert, die eine einzige Benutzertabelle hat:

Users
+ user_id
+ open_id
+ last_login

Ich nehme an, dass ich ein LoginModel benötigen würde:

public class LogInModel
{
    [Required]
    [DisplayName("OpenID")]
    public string OpenID { get; set; }
}

Ein DisplayModel:

public class DisplayModel
{
    [DisplayName("User ID")]
    public string UserID{ get; set; }

    [DisplayName("OpenID")]
    public string OpenID { get; set; }

    [DisplayName("Last Login")]
    public DateTime LastLogin{ get; set; }
}

Zusätzlich könnte ich ein ModifyModel benötigen, aber das DisplayModel kann wiederverwendet und möglicherweise in UserModel umbenannt werden (um die Verwendung des Modells ordnungsgemäß widerzuspiegeln).

Jetzt habe ich mehrere Fragen:

  1. Ist der Controller dafür verantwortlich, die Benutzereingabe zu überprüfen, oder wird dies durchgeführt, wenn die Eingabe an das Modell übergeben wird (d.h. Aufruf von Identifier.TryParse auf dem openid_identifier)?
  2. Ich möchte dem Benutzer erlauben, sich einzuloggen, ihre Informationen zu ändern (z.B. die user_id) und ihre Informationen anzuzeigen (z.B. user_id, open_id und last_login). Wie viele Modelle benötige ich (ModifyModel, DisplayModel und LogInModel)?
  3. Was ist die Lebensdauer eines Modells? Existiert das Modell statisch an einem Ort oder wird es einfach vom Controller erstellt und an die Ansicht übergeben?
  4. Wäre es besser, die Datenbank als Modell hinzuzufügen, anstatt die obigen Modelle zu erstellen?

2voto

RPM1984 Punkte 70972

1) Es könnte ja sein, aber ein besserer Ansatz wäre es, Datenannotationen auf dem ViewModel zu verwenden.

2) Ein Modell reicht aus. Ein Modell sollte ein Gesamtobjekt repräsentieren, in diesem Fall ein "Benutzer". Wenn die für jede Ansicht benötigten Informationen stark voneinander abweichen, trennen Sie sie in View-Modelle auf.

3) Ich bin mir nicht sicher, was du meinst. MVC (und ASP.NET im Allgemeinen) basiert auf dem HTTP-Protokoll und ist somit zustandslos. Wenn Sie also eine URL aufrufen, wird ein Controller zugewiesen, dann werden Objekte neu erstellt, wie der Code es erfordert - dazu gehört auch eine Datenbankverbindung. Dann, wenn die Anfrage beendet ist, ist alles weg (zumindest die verwalteten Ressourcen). Versuchen Sie nicht, das Wort "Modell" mit einer physischen Entität zu verwechseln, sondern eher als einen Bereich Ihres Programmiermodells.

4) Im Allgemeinen ist Ihr "Modell" Ihr Repository/DAL/ORM, das Ihre zugrunde liegende Datenbank umschließt und Ihr Domänenmodell darstellt. Ihre Ansichten sollten sich nicht mit der Domäne befassen. Das ist die Aufgabe Ihres Controllers/Modells. Ihre Ansicht sollte mit dem arbeiten, was sie braucht, und nicht mehr. Deshalb sollten Sie als Faustregel niemals direkt an ein ORM-Modell binden, sondern ein ViewModel verwenden.

Bearbeitung - als Antwort auf Fragen in den Kommentaren:

Es tut mir leid... für 1 schlagen Datenannotationen vor, dass die Validierung am Modell durchgeführt wird, aber OpenId.Identifier bietet auch einige Funktionen zur Überprüfung von Eingaben (z.B. TryParse). Wäre es also konsequenter, die gesamte Validierung am Modell durchzuführen, oder ist der "Ort" der Validierung normalerweise nicht so streng?

Fügen Sie die Datenannotationen dem ViewModel hinzu, das Repräsentationen des Modells sind, die erstellt wurden, um das Leben für die Ansicht einfacher zu machen. Datenannotationen sollten nicht auf Ihren tatsächlichen Modellentitäten (Entity Framework, L2SQL usw.) platziert werden. Datenannotationen sollten für Eingabevalidierung (Vergleich von Passwörtern, Länge von Zeichen, Telefonnummern, E-Mail-Adressen usw.) verwendet werden. Geschäftsvalidierung sollte in der Domäne durchgeführt werden. Ich würde sagen, dass OpenId ein Dienst ist und nicht Teil der Domäne. Wenn sie einige Validierungsfunktionen haben, könnten Sie diese Aufrufe in benutzerdefinierte Datenannotationen einwickeln und sie auf Ihr ViewModel legen. Das wäre ein sauberer und konsistenter Ansatz.

In Bezug auf 3 bin ich einverstanden, dass das HTTP-Protokoll zustandslos ist, aber wenn ich richtig verstehe, sind Cookies eine Möglichkeit, einen Zustand aufrechtzuerhalten, und das Modell bietet einen alternativen Weg.

Sie haben recht, Cookies sind eine Möglichkeit, den Zustand aufrechtzuerhalten. Ein häufiger Einsatzzweck dafür ist das Forms-Authentifizierungs-Ticket. Ein anderer ist die Sitzung. TempData verwendet die Sitzung (auf clevere Weise - automatische Auslagerung). Das Modell ist kein "alternativer Weg" - es hält keinen Zustand aufrecht. Werde unten mehr darüber sprechen.

In der Anwendungsprogrammierung ist ein Zustand (d.h. ein Objekt) normalerweise während der gesamten Lebensdauer der Anwendung persistent, also wenn ich ein Modell in einer Konsolenanwendung erstellen würde, würde es existieren solange es eine Referenz dazu gibt. Wenn Sie also sagen, dass die "Objekte neu erstellt" werden, bedeutet das, dass das Modellobjekt bereits instanziiert ist (wie in einer Konsolenanwendung) und ich es nur "aktualisiere", oder heißt das, dass ich ein neues Modell konstruiere?

In einer Konsolenanwendung - da haben Sie recht. Solange die Konsolenanwendung läuft, sind die Objekte lebendig. Aber in einer ASP.NET MVC-Webanwendung ist der "Hauptthread" ein ASP.NET-Worker-Thread, der zugewiesen wird, wenn eine Anfrage hereinkommt. Alle erforderlichen Objekte (Controller, Datenbankverbindung, Repository, Domänenobjekte) sind "Kinder" dieses Threads, wenn das Sinn macht. Sobald dieser Thread verschwunden ist, sind auch alle zugehörigen Objekte weg.

Es gibt keine "magische Instanziierung" des Modells, noch einmal - das ist das Modell ein Gesamtüberblick/Darstellung Ihrer Domäne, die normalerweise aus dem Domänenmodell (Entitäten, Geschäftslogik) und dem Repository besteht.

Die eine Ausnahme ist "Model Binding". Wenn Sie ein Formular an eine mit [HttpPost] markierte Aktion senden, die stark typisiert ist zu einem "Modell" (sollte ein ViewModel sein), wird ASP.NET MVC (über Reflexion) dieses "Modell" auf Basis der Felder im HTTP-POST erstellen.

Wenn Sie Dinge wie "UpdateModel" machen, aktualisieren Sie lediglich ein Objekt, das Sie mit dem aktualisieren, was in der Aktion über das Modellbinding hereinkommt. Es wird keine tatsächliche Datenbank aktualisiert.

Ich weiß nicht, was ich noch dazu sagen kann. Es scheint, als würden Sie Verwirrung über das "Modell" haben. Darf ich Ihnen vorschlagen, sich ein Exemplar von Steven Sandersons Pro ASP.NET MVC 2 Framework Buch zu besorgen. Es ist fantastisch, erklärt alles von Grund auf - in einfachen Worten, dann erhöht es das Tempo, damit Sie am Ende des Buches ein Experte sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X