Laden...

ASP.NET MVC - Neues Model durch Benutzer bestätigen lassen

Erstellt von mygil vor 8 Jahren Letzter Beitrag vor 8 Jahren 3.921 Views
M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren
ASP.NET MVC - Neues Model durch Benutzer bestätigen lassen

Hallo!

Mittels folgendem Code erstellt ein Benutzer einen neuen Datensatz:


        // GET: Jobs/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Jobs/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "ID,Firma,Datum,Stellenbezeichnung,Qualifikationen,Aufgaben")] Jobs jobs)
        {
            if (ModelState.IsValid)
            {
                db.Jobs.Add(jobs);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(jobs);
        }

Ich möchte jetzt, dass der Benutzer bevor er diesen Datensatz erstellt nochmals seine Eingaben auf den Bildschirm bekommt, bestätigt und erst wenn alles passt dann in die Datenbank gespeichert wird.

Probiert habe ich das mittels z.b.:


        // POST: Jobs/Create
         [HttpPost]
         [ValidateAntiForgeryToken]
         public ActionResult Create([Bind(Include = "ID,Firma,Datum,Stellenbezeichnung,Qualifikationen,Aufgaben")] Jobs job)
         {
             if (ModelState.IsValid)
             {
                 db.Jobs.Add(job);
                 db.SaveChanges();
                 return RedirectToAction("JobUeberpruefung", new { job= job}); // <<< NEU
             }  
         } 


public ActionResult JobUeberpruefung(Job job)
{
          ...
}

Aber wenn ich das so mache, dann ist job = null.

Lt. Google Recherche könnte ich das mit TempData lösen:


                ...
                TempData["j"] = job;
                return RedirectToAction("JobUeberpruefung"
       }

        public ActionResult JobUeberpruefung()
        {
            Job job = (Job)TempData["j"];
            return View(job);
        }

Das scheint zu klappen aber wie wird das üblicherweise gemacht?
Wie ist der übliche/moderne/empfohlene Weg hierfür?

Danke & lg myGil

P
1.090 Beiträge seit 2011
vor 8 Jahren

Ich bin da jetzt kein Experte, aber ich würde es wohl Client seitig mit Javascript lösen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

742 Beiträge seit 2005
vor 8 Jahren

Ich würde es auch mit Javascript lösen, aber TempData ist für sowas gedacht, warum sich also den Kopf zerbrechen, es funktioniert ja erstmal 😃

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Hallo!

So schaut das jetzt bei mir aus:


        // 1. Bewerber möchte neue Bewerbung ausfüllen:       
        public ActionResult BewerbungErstellen(string Stellenbezeichnung)
        {
            Bewerbung b = new Bewerbung();
            b.Stellenbezeichnung = ViewBag.Stellenbezeichnung;
            b.Datum = DateTime.Now;
            return View(b);
        }

        // 2. Serverseitige Validierung der Benutzereingaben:
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult BewerbungErstellen([Bind(Include = "ID,Datum,Stellenbezeichnung,Anrede,Nachname,Vorname...")] Bewerbung bewerbung)
        {
            if (ModelState.IsValid)
            {
                TempData["b"] = bewerbung;
                return RedirectToAction("BewerbungUeberpruefung");
            }
            return View(bewerbung);
        }
      
        // 3. Bewerber muss jetzt seine Eingaben nochmals überprüfen & bestätigen:
        public ActionResult BewerbungUeberpruefung()
        {
            Bewerbung bewerbung = (Bewerbung)TempData["b"];
            TempData["b"] = bewerbung; // Tatsächlich nochmals notwendig :)
            return View(bewerbung);
        }
        
        // 4. Bewerber hat jetzt alles nochmals bestätigt:
        public ActionResult BewerbungUerberperuft()
        {
            Bewerbung bewerbung = (Bewerbung)TempData["b"];

            // Bewerbung erstellen:
            db.Bewerbung.Add(bewerbung);
            db.SaveChanges();

            return RedirectToAction("BewerbungErstellt");
        }
       
        // 5. Bewerber bekommt Meldung auf den Bildschirm "Vielen Dank für Ihre Bewerbung!"
        public ActionResult BewerbungErstellt()
        {
            return View();
        }

Irgendwie kommt mir das noch komisch vor.
Wahrscheinlich weil mir so eine Bestätigung vor dem speichern eher etwas alltäglich vorkommt und ich von diesem TempData einfach noch nicht viel untergekommen ist. (In der Regel hab ich dann etwas komplett missverstanden oder was anders übersehen ^^)

Wenn ein Profi hier noch einen Tipp für mich hat dann wäre ich natürlich dankbar aber ansonsten belasse ich das jetzt mal so 😃

lg myGil

P
1.090 Beiträge seit 2011
vor 8 Jahren

Das grundlegende Problem ist, das man einen Zusätzlichen Dialog vor dem Speichern meist gar nicht mehr verwendet. Wenn du z.B. den Harken an den AGBs gesetzt hast, wird meist ohne zusätzlichen Dialog gespeichert.

Der Dialog ist in den meisten Fällen nur nervend. 😉

Mit dem drücken von "Speichern" oder ähnlichen, hat der Benutzer ja schon signalisiert, dass die Daten aus seine Sicht Valide sind. Einen groß teil solltest du auch schon Client seitig geprüft haben und wenn nach Serverseitigerprüfung herauskommt, das Daten nicht Valide sind, gibt es ein Feedback welche.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

5.658 Beiträge seit 2006
vor 8 Jahren

Hi mygil,

in dem Controller hast du doch bereits das vollständige Modell. Das brauchst du eigentlich nur noch an eine View zu übergeben, welche die Daten dem Benutzer nocheinmal anzeigt:


return View("Verification", jobs);

Ansonsten schließe ich mich meinen Vorrednern an, was die clientseitige Validierung betrifft.

Christian

Weeks of programming can save you hours of planning

2.207 Beiträge seit 2011
vor 8 Jahren

Http ist ein statusloses Protokoll. Was du mit dem TempData machst: Du hältst einen Status --> nicht gut. ViewBag und ViewData sind übrigens auch Mist.

Entweder machst dus Clientseitig, ansonsten: Benutze ViewModels.

Poste deine Daten an die Überprüfung, mach ein ViewModel wo der Job nochmal drauf ist. Gib das ViewModel einfach nochmal zurück und beim Bestätigen ist es wieder ein ganz normaler Post.

Oder habe ich da was falsch verstanden? Ich meine: Du machst ja schon einen Post hoch an den Server. Das musst du einfach 2x machen.

Gruss

Coffeebean

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Hallo!

Bevor meine Serverseitige validiertung stattfindet wurde sowieso schon Clientseitig validiert:


        // 1. Bewerber möchte neue Bewerbung ausfüllen:       
         public ActionResult BewerbungErstellen(string Stellenbezeichnung)
         ...

        // 2. Serverseitige Validierung der Benutzereingaben nachdem Clientseitige Validierung OK waren:
         [HttpPost]
         public ActionResult BewerbungErstellen([Bind(Include = "ID,Datum,Stellenbezeichnung,Anrede,Nachname,Vorname...")] Bewerbung bewerbung)
        ...

Im Prinzip versuche ich genau das nachzubauen:
Link

Füllt man dort eine Bewerbung aus und klickt anschließend auf [Absenden], dann wird alles nochmals zusammengefasst (als purer Text) dargestellt und man muss nochmals auf [Absenden] klicken.
Genau so wäre das die Anforderung und ich find das auch sehr angenehm und sinnvoll.

@Coffeebean:
Du hast Recht! Mit ViewModels kann ich tatsächlich mittels return RedirectToAction("BewerbungUeberpruefung", bewerbungVM); das Model an eine andere Action übergeben. Danach hab ich schon mal gesucht.

Jetzt habe ich aber noch eine Frage:
So wie in diesem Link muss die Anwendung 3 Dokumente hochladen können.

Falls ich das richtig verstanden habe dann werde ich hier auf ein Problem stoßen weil wenn (a) der Benutzer die Eingaben macht (b) Clientseitig validiert wird (c) Serverseitig validiert wird (d) der Benutzer alles nochmals zur Kontrolle via eigener Action mittels ViewModel übergeben bekommt ... Hab ich dann gerade die Dokumente hin und her geschickt?! Das wäre natürlich nicht so gut.

(Sorry! Ich alter Windows Anwendungen Entwickler tue mich mit diesem HTTP Get und Post noch ein wenig schwer ^^)

Wäre es doch cleverer, wenn ich schon nach dem Client und Serverseitigen validieren (noch bevor der Benutzer es ein zweites mal bestätigt) alles in eine Datenbank speichere.
Anschließend kann ich dem Benutzer nochmals alles sehr einfach anzeigen und ihn alles nochmals bestätigen lassen.
In der Datenbank speichere ich dann halt einfach ein "IsBestätigt"=1 ab und das wars.

Hmm...

Also wenn ich das ganze von Anfang in eine DB speichere und dann wie folgt den Benutzer nochmals bestätigen lassen und in der DB diese Bestätigung nochmals kennzeichne würde das wunderbar klappen:


        // 3. Der Bewerber muss nochmals bestätigen:
        public ActionResult BewerbungUeberpruefung(int id)
        {
            Bewerbung bewerbung = db.Bewerbung.Where(p => p.ID == id).FirstOrDefault();
            return View(bewerbung);
        }

Aber so kann jetzt jeder alle Bewerbungen anschauen indem er einfach die ID im in der Url ändert:

https://www.webseite.at/Home/BewerbungUeberpruefung/47
https://www.webseite.at/Home/BewerbungUeberpruefung/46
https://www.webseite.at/Home/BewerbungUeberpruefung/45

Bin ich auf dem Holzweg?
Bin ich auf dem richtigen Weg und man muss hier jetzt mit Guid.NewGuid() arbeiten und diese ebenso von Anfang an in der DB speichern?

lg myGil

P
1.090 Beiträge seit 2011
vor 8 Jahren

Also deinen 2. Ansatz.
Benutzer erstellt Bewerbung, sie wird gespeichert. Und der Benutzer, kann sie veröffentlichen, wenn er möcht. Finde ich deutlich besser.

Das daraus Resultierende Problem solltest du über eine Rechteverwaltung lösen. Einen Einstieg kannst du hier finden: MSDN:ASP.NET: Authentifizierung und mehr

Wenn du vor hast eine Internetseite zu Programmieren, solltest du die sowieso grundlegend mit dem Thema Internetsicherheit (Benutzerrechte) auseinandersetzen (Gerade wenn es um Bewegungen und andere Persönliche Daten geht.).

Ich bin ja jetzt kein Jurist, aber um an andere Benutzerdaten kommen zu können, die ID um 1 hoch zu zählen, halte ich für grob fahrlässig. Und so weit ich weiß, kann das dann für dich privat richtig teuer werden.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Hallo Palin!

Mit Rechteverwaltung kenne ich mich schon aus aber danke für den Tipp und das mit der ID wäre natürlich völliger quatsch so 😃

lg myGil

2.207 Beiträge seit 2011
vor 8 Jahren

@Coffeebean:
Du hast Recht! Mit ViewModels kann ich tatsächlich mittels return RedirectToAction("BewerbungUeberpruefung", bewerbungVM); das Model an eine andere Action übergeben. Danach hab ich schon mal gesucht.

Das meinte ich nicht. Gib das Ding zum Client und wieder hoch. Mit Viewmodels.

     
         public ActionResult BewerbungErstellen(string Stellenbezeichnung)
         { return View(viewModel); }


         [HttpPost]
         public ActionResult BewerbungUeberprüfen(Bewerbung bewerbung)
         {
             //vm mit allen Daten abfuellen und zurückgeben
        }

         [HttpPost]
         public ActionResult BewerbungAbspeichern(Bewerbung bewerbung)
        {
           //abspeichern
        }


Aber so kann jetzt jeder alle Bewerbungen anschauen indem er einfach die ID im in der Url ändert

Du speicherst die Bewerbung natürlich mit einer UserId. Die baust du bei deinem Select wieder ein. Dann bekommt jeder User nur die Bewerbung, die auch zu ihm gehört.

Gruss

Coffeebean

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Jetzt klappt es perfekt!
Vielen Dank für die Tipps!

lg myGil

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Hallo Coffeebean!

Ich habe mir jetzt nochmals deine Lösung mit den "ViewModels" angeschaut.
In diesem Fall muss ich aber mittels "Reflection" oder alle Felder einzeln mittels @Html.HiddenFor(p => p.Vorname) wieder zum Controller übergeben oder?

Deine Lösung hab ich jetzt exakt wie folgt ausprobiert: (Deine Lösung bisschen verändert - hoffe ich hab's richtig verstanden 😃


          public ActionResult BewerbungErstellen(string Stellenbezeichnung)
          { return View(Model); } // Hier verwende ich das Entity Model


          [HttpPost]
          public ActionResult BewerbungUeberprüfen(Bewerbung bewerbung)
          {
              //vm mit allen Daten abfuellen und zurückgeben:
                BewerbungViewModel bewerbungVM = new BewerbungViewModel();
                bewerbungVM.AbgeleisteterDienst = bewerbung.AbgeleisteterDienst;
                bewerbungVM.AkademischerTitel = bewerbung.AkademischerTitel;
                bewerbungVM.Anrede = bewerbung.Anrede;
                bewerbungVM.Aufmerksam = bewerbung.Aufmerksam;
                bewerbungVM.Datum = bewerbung.Datum;
                ........
                return View(bewerbungVM)
         }

          [HttpPost]
          public ActionResult BewerbungAbspeichern(BewerbungViewModel bewerbung)
         {
                // Das ViewModel an der stelle wäre jetzt leer wenn ich nicht manuell @Html.HiddenFor(p => p.Vorname) ..... in den HTML Code schreiben würde
         } 

lg myGil

2.207 Beiträge seit 2011
vor 8 Jahren

Gib das Entity nicht runter an den Client. Man sieht das oft, aber wirklich sauber ist das nicht. Daher ja DTOs und View/SubmitModels.

Right now, our web API exposes the database entities to the client. The client receives data that maps directly to your database tables. However, that’s not always a good idea. Sometimes you want to change the shape of the data that you send to client. For example, you might want to:

Remove circular references (see previous section).  
Hide particular properties that clients are not supposed to view.  
Omit some properties in order to reduce payload size.  
Flatten object graphs that contain nested objects, to make them more convenient for clients.  
Avoid “over-posting” vulnerabilities. (See Model Validation for a discussion of over-posting.)  
Decouple your service layer from your database layer.

Aus Create Data Transfer Objects (DTOs)

Bei dir sinds halt keine DTOs, sondern ViewModels. Siehe Abts Link.

Gruss

Coffeebean

M
mygil Themenstarter:in
124 Beiträge seit 2009
vor 8 Jahren

Hallo!

@Abt: Danke für den erstklassigen Artikel! (Trifft bei mir 100% ins schwarze!!!)
@Coffeebean: Danke für die Hilfe!

Der Einsatz von ViewModel ist zwar mehr Tippaufwand aber dafür erhaltet man Kontrolle und "Macht" 😃

lg myGil