Włączanie obsługi operacji CRUD (tworzenia, odczytu, aktualizacji i usuwania) w formularzach danych

autor: Microsoft

Pobierz plik PDF

Jest to krok 5 bezpłatnego samouczka aplikacji "NerdDinner" , który zawiera instrukcje dotyczące tworzenia małej, ale kompletnej aplikacji internetowej przy użyciu ASP.NET MVC 1.

Krok 5 pokazuje, jak kontynuować naszą klasę DinnersController przez włączenie obsługi edytowania, tworzenia i usuwania kolacji.

Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store.

NerdDinner — krok 5. Tworzenie, aktualizowanie, usuwanie scenariuszy formularzy

Wprowadziliśmy kontrolery i widoki oraz omówiliśmy sposób ich używania do implementowania środowiska list/szczegółów dla kolacji na miejscu. Następnym krokiem będzie dalsze przejście do klasy DinnersController i włączenie obsługi edytowania, tworzenia i usuwania kolacji.

Adresy URL obsługiwane przez dinnersController

Wcześniej dodaliśmy metody akcji do elementu DinnersController, które zaimplementowały obsługę dwóch adresów URL: /Dinners i /Dinners/Details/[id].

Adres URL CZASOWNIK Cel
/Kolacje/ GET Wyświetl listę HTML nadchodzących kolacji.
/Dinners/Details/[id] GET Wyświetl szczegółowe informacje o określonej kolacji.

Teraz dodamy metody akcji, aby zaimplementować trzy dodatkowe adresy URL: /Dinners/Edit/[id], /Dinners/Create i /Dinners/Delete/[id]. Te adresy URL umożliwią edytowanie istniejących kolacji, tworzenie nowych kolacji i usuwanie kolacji.

Będziemy obsługiwać interakcje czasowników HTTP GET i HTTP POST z tymi nowymi adresami URL. Żądania HTTP GET do tych adresów URL będą wyświetlać początkowy widok HTML danych (formularz wypełniony danymi kolacji w przypadku "edycji", pusty formularz w przypadku "tworzenia" i ekran potwierdzenia usuwania w przypadku usunięcia). Żądania HTTP POST do tych adresów URL spowodują zapisanie/zaktualizowanie/usunięcie danych kolacji w naszym repozytorium DinnerRepository (i stamtąd do bazy danych).

Adres URL CZASOWNIK Cel
/Dinners/Edit/[id] GET Wyświetl edytowalny formularz HTML wypełniony danymi kolacji.
POST Zapisz zmiany formularza dla określonej kolacji w bazie danych.
/Dinners/Create GET Wyświetl pusty formularz HTML, który umożliwia użytkownikom definiowanie nowych kolacji.
POST Utwórz nową kolację i zapisz ją w bazie danych.
/Dinners/Delete/[id] GET Wyświetl ekran potwierdzenia usuwania.
POST Usuwa określoną kolację z bazy danych.

Edytowanie obsługi

Zacznijmy od zaimplementowania scenariusza "edytuj".

Metoda akcji Http-GET Edit

Zaczniemy od zaimplementowania zachowania HTTP "GET" naszej metody akcji edycji. Ta metoda zostanie wywołana po zażądaniu adresu URL /Dinners/Edit/[id]. Nasza implementacja będzie wyglądać następująco:

//
// GET: /Dinners/Edit/2

public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(dinner);
}

Powyższy kod używa repozytorium DinnerRepository do pobrania obiektu Dinner. Następnie renderuje szablon Widok przy użyciu obiektu Dinner. Ponieważ nie przekazaliśmy jawnie nazwy szablonu do metody pomocniczej View(), użyjemy domyślnej ścieżki opartej na konwencji, aby rozpoznać szablon widoku: /Views/Dinners/Edit.aspx.

Teraz utwórzmy ten szablon widoku. Zrobimy to, klikając prawym przyciskiem myszy metodę Edit i wybierając polecenie menu kontekstowego "Dodaj widok":

Zrzut ekranu przedstawiający tworzenie szablonu widoku w celu dodania widoku w programie Visual Studio.

W oknie dialogowym "Dodawanie widoku" wskażemy, że przekazujemy obiekt Dinner do szablonu widoku jako jego model i wybieramy automatyczne tworzenie szkieletu szablonu "Edytuj":

Zrzut ekranu przedstawiający dodawanie widoku do automatycznego tworzenia szkieletu szablonu Edytuj.

Po kliknięciu przycisku "Dodaj" program Visual Studio doda nowy plik szablonu "Edit.aspx" dla nas w katalogu "\Views\Dinners". Spowoduje to również otwarcie nowego szablonu widoku "Edit.aspx" w edytorze kodu — wypełnionego początkową implementacją szkieletu "Edytuj", jak pokazano poniżej:

Zrzut ekranu przedstawiający nowy szablon Widoku edycji w edytorze kodu.

Wprowadźmy kilka zmian w domyślnym wygenerowanym szkieletie "edytuj" i zaktualizujmy szablon widoku edycji, aby miał poniższą zawartość (co spowoduje usunięcie kilku właściwości, których nie chcemy uwidocznić):

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Edit: <%=Html.Encode(Model.Title)%>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit Dinner</h2>

    <%=Html.ValidationSummary("Please correct the errors and try again.") %>  
    
    <% using (Html.BeginForm()) { %>

        <fieldset>
            <p>
                <label for="Title">Dinner Title:</label>
                <%=Html.TextBox("Title") %>
                <%=Html.ValidationMessage("Title", "*") %>
            </p>
            <p>
                <label for="EventDate">EventDate:</label>
                <%=Html.TextBox("EventDate", String.Format("{0:g}", Model.EventDate))%>
                <%=Html.ValidationMessage("EventDate", "*") %>
            </p>
            <p>
                <label for="Description">Description:</label>
                <%=Html.TextArea("Description") %>
                <%=Html.ValidationMessage("Description", "*")%>
            </p>
            <p>
                <label for="Address">Address:</label>
                <%=Html.TextBox("Address") %>
                <%=Html.ValidationMessage("Address", "*") %>
            </p>
            <p>
                <label for="Country">Country:</label>
                <%=Html.TextBox("Country") %>               
                <%=Html.ValidationMessage("Country", "*") %>
            </p>
            <p>
                <label for="ContactPhone">ContactPhone #:</label>
                <%=Html.TextBox("ContactPhone") %>
                <%=Html.ValidationMessage("ContactPhone", "*") %>
            </p>
            <p>
                <label for="Latitude">Latitude:</label>
                <%=Html.TextBox("Latitude") %>
                <%=Html.ValidationMessage("Latitude", "*") %>
            </p>
            <p>
                <label for="Longitude">Longitude:</label>
                <%=Html.TextBox("Longitude") %>
                <%=Html.ValidationMessage("Longitude", "*") %>
            </p>
            <p>
                <input type="submit" value="Save"/>
            </p>
        </fieldset>
        
    <% } %>
    
</asp:Content>

Po uruchomieniu aplikacji i żądaniu adresu URL "/Dinners/Edit/1" zostanie wyświetlona następująca strona:

Zrzut ekranu przedstawiający stronę My M V C Application (Moja aplikacja języka C W języku M).

Znacznik HTML wygenerowany przez nasz widok wygląda następująco. Jest to standardowy kod HTML — z elementem <formularza> , który wykonuje żądanie HTTP POST do adresu URL /Dinners/Edit/1 po naciśnięciu przycisku "Save" <input type="submit"/> (Zapisz). Typ danych wejściowych HTML <="text"/> element został wyprowadzony dla każdej właściwości edytowalnej:

Zrzut ekranu przedstawiający wygenerowany znacznik H T M L.

Metody pomocnika HTML.BeginForm() i Html.TextBox()

Nasz szablon widoku "Edit.aspx" używa kilku metod "Pomocnika HTML": Html.ValidationSummary(), Html.BeginForm(), Html.TextBox() i Html.ValidationMessage(). Oprócz generowania znaczników HTML te metody pomocnika zapewniają wbudowaną obsługę błędów i obsługę walidacji.

Html.BeginForm(), metoda pomocnika

Metoda pomocnika Html.BeginForm() zwraca element formularza> HTML <w adiustacji. W naszym szablonie widoku Edit.aspx zauważysz, że podczas korzystania z tej metody stosujemy instrukcję "using" języka C#. Otwarty nawias klamrowy wskazuje początek <zawartości formularza> , a zamykający nawias klamrowy wskazuje koniec </form> elementu:

<% using (Html.BeginForm()) { %>

   <fieldset>
   
      <!-- Fields Omitted for Brevity -->
   
      <p>
         <input type="submit" value="Save"/>
      </p>
   </fieldset>
   
<% } %>

Alternatywnie, jeśli znajdziesz metodę instrukcji "using" nienaturalną dla takiego scenariusza, możesz użyć kombinacji Html.BeginForm() i Html.EndForm() (co robi to samo):

<% Html.BeginForm();  %>

   <fieldset>
   
      <!-- Fields Omitted for Brevity -->
   
      <p>
          <input type="submit" value="Save"/>
      </p>
   </fieldset>
   
<% Html.EndForm(); %>

Wywołanie metody Html.BeginForm() bez żadnych parametrów spowoduje, że wyprowadzi element formularza, który wykonuje żądanie HTTP-POST pod adresem URL bieżącego żądania. Dlatego nasz widok Edycji generuje <element form action="/Dinners/Edit/1" method="post".> Alternatywnie można przekazać jawne parametry do metody Html.BeginForm(), jeśli chcemy opublikować adres URL.

Html.TextBox(), metoda pomocnika

Nasz widok Edit.aspx używa metody pomocnika Html.TextBox() do danych wyjściowych <type="text"/> elementów:

<%= Html.TextBox("Title") %>

Powyższa metoda Html.TextBox() przyjmuje pojedynczy parametr — który jest używany do określania atrybutów identyfikatora/nazwy typu wejściowego <="text"/> elementu do danych wyjściowych, a także właściwości modelu w celu wypełnienia wartości pola tekstowego. Na przykład obiekt Dinner przekazany do widoku Edycja miał wartość właściwości "Title" ".NET Futures", a więc nasze dane wyjściowe metody Html.TextBox("Title"): <input id="Title" name="Title" type="text" value=".NET Futures" />.

Alternatywnie możemy użyć pierwszego parametru Html.TextBox(), aby określić identyfikator/nazwę elementu, a następnie jawnie przekazać wartość do użycia jako drugi parametr:

<%= Html.TextBox("Title", Model.Title)%>

Często należy wykonać formatowanie niestandardowe dla wartości, która jest wynikiem wyjściowym. Metoda statyczna String.Format() wbudowana w platformę .NET jest przydatna w tych scenariuszach. Nasz szablon widoku Edit.aspx używa go do formatowania wartości EventDate (która jest typu DateTime), aby nie pokazywała sekund dla tego czasu:

<%= Html.TextBox("EventDate", String.Format("{0:g}", Model.EventDate)) %>

Trzeci parametr html.TextBox() może być opcjonalnie używany do wyprowadzania dodatkowych atrybutów HTML. Poniższy fragment kodu pokazuje, jak renderować dodatkowy atrybut size="30" i atrybut class="mycssclass" w typie <wejściowym="text"/> element. Zwróć uwagę, jak uciekamy od nazwy atrybutu klasy przy użyciu znaku "@", ponieważ "class" jest zastrzeżonym słowem kluczowym w języku C#:

<%= Html.TextBox("Title", Model.Title, new { size=30, @class="myclass" } )%>

Implementowanie metody akcji edycji HTTP-POST

Mamy teraz zaimplementowaną wersję HTTP-GET metody akcji Edit. Gdy użytkownik zażąda adresu URL /Dinners/Edit/1 , otrzyma stronę HTML podobną do następującej:

Zrzut ekranu przedstawiający dane wyjściowe H T M L, gdy użytkownik zażąda edycji kolacji.

Naciśnięcie przycisku "Zapisz" powoduje opublikowanie formularza w adresie URL /Dinners/Edit/1 i przesłanie wartości formularza wejściowego> HTML <przy użyciu czasownika HTTP POST. Teraz zaimplementujmy zachowanie HTTP POST naszej metody akcji edycji — która będzie obsługiwać zapisywanie kolacji.

Zaczniemy od dodania przeciążonej metody akcji "Edytuj" do elementu DinnersController z atrybutem "AcceptVerbs", który wskazuje, że obsługuje scenariusze HTTP POST:

//
// POST: /Dinners/Edit/2

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
   ...
}

Gdy atrybut [AcceptVerbs] jest stosowany do metod akcji przeciążonych, ASP.NET MVC automatycznie obsługuje wysyłanie żądań do odpowiedniej metody akcji w zależności od przychodzącego zlecenia HTTP. Żądania HTTP POST do adresów URL /Dinners/Edit/[id] zostaną przekierowane do powyższej metody Edit, podczas gdy wszystkie pozostałe żądania zlecenia HTTP do adresów URL /Dinners/Edit/[id] trafią do pierwszej wdrożonej metody Edit (która nie ma [AcceptVerbs] atrybutu).

Temat boczny: Dlaczego warto rozróżniać czasowniki HTTP?
Możesz zadać pytanie — dlaczego używamy pojedynczego adresu URL i różnimy jego zachowanie za pośrednictwem czasownika HTTP? Dlaczego nie tylko mają dwa oddzielne adresy URL do obsługi ładowania i zapisywania zmian edycji? Na przykład: /Dinners/Edit/[id] w celu wyświetlenia formularza początkowego i /Dinners/Save/[id] do obsługi wpisu formularza w celu zapisania go? Wadą publikowania dwóch oddzielnych adresów URL jest to, że w przypadkach, w których publikujemy adres /Dinners/Save/2, a następnie należy ponownie odtworzyć formularz HTML z powodu błędu wejściowego, użytkownik końcowy zakończy się adresem URL /Dinners/Save/2 na pasku adresu przeglądarki (ponieważ był to adres URL, do którego wysłano formularz). Jeśli zakładki użytkownika końcowego zostaną dodane do listy ulubionych przeglądarki lub skopiować/wkleić adres URL i wkleić go do znajomego, spowoduje to zapisanie adresu URL, który nie będzie działać w przyszłości (ponieważ ten adres URL zależy od wartości post). Uwidaczniając pojedynczy adres URL (np. /Dinners/Edit/[id]) i różnicując przetwarzanie go przez czasownik HTTP, użytkownicy końcowi mogą bezpiecznie oznaczyć stronę edycji i/lub wysłać adres URL do innych użytkowników końcowych.

Pobieranie wartości po wpisie formularza

Istnieje wiele sposobów uzyskiwania dostępu do opublikowanych parametrów formularza w ramach metody "Edit" HTTP POST. Jedną z prostych metod jest użycie właściwości Request w klasie bazowej Kontroler w celu uzyskania dostępu do kolekcji formularzy i bezpośredniego pobrania opublikowanych wartości:

//
// POST: /Dinners/Edit/2

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    // Retrieve existing dinner
    Dinner dinner = dinnerRepository.GetDinner(id);

    // Update dinner with form posted values
    dinner.Title = Request.Form["Title"];
    dinner.Description = Request.Form["Description"];
    dinner.EventDate = DateTime.Parse(Request.Form["EventDate"]);
    dinner.Address = Request.Form["Address"];
    dinner.Country = Request.Form["Country"];
    dinner.ContactPhone = Request.Form["ContactPhone"];

    // Persist changes back to database
    dinnerRepository.Save();

    // Perform HTTP redirect to details page for the saved Dinner
    return RedirectToAction("Details", new { id = dinner.DinnerID });
}

Powyższe podejście jest jednak trochę pełne, zwłaszcza po dodaniu logiki obsługi błędów.

Lepszym rozwiązaniem dla tego scenariusza jest wykorzystanie wbudowanej metody pomocnika UpdateModel() w klasie bazowej Kontroler. Obsługuje on aktualizowanie właściwości obiektu, który przekazujemy przy użyciu parametrów formularza przychodzącego. Używa odbicia w celu określenia nazw właściwości obiektu, a następnie automatycznie konwertuje i przypisuje do nich wartości na podstawie wartości wejściowych przesłanych przez klienta.

Możemy użyć metody UpdateModel(), aby uprościć naszą akcję edycji HTTP-POST przy użyciu tego kodu:

//
// POST: /Dinners/Edit/2

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    UpdateModel(dinner);

    dinnerRepository.Save();

    return RedirectToAction("Details", new { id = dinner.DinnerID });
}

Teraz możemy odwiedzić adres URL /Dinners/Edit/1 i zmienić tytuł naszej kolacji:

Zrzut ekranu przedstawiający stronę Edytuj kolację.

Po kliknięciu przycisku "Zapisz" wykonamy wpis formularza do naszej akcji Edytuj, a zaktualizowane wartości zostaną utrwalone w bazie danych. Następnie nastąpi przekierowanie do adresu URL szczegółów kolacji (który wyświetli nowo zapisane wartości):

Zrzut ekranu przedstawiający adres URL szczegółów kolacji.

Obsługa błędów edycji

Nasza bieżąca implementacja HTTP-POST działa prawidłowo — z wyjątkiem błędów.

Gdy użytkownik popełni błąd podczas edytowania formularza, musimy upewnić się, że formularz jest odtwarzany ponownie za pomocą komunikatu o błędzie informacyjnego, który prowadzi ich do jego naprawienia. Obejmuje to przypadki, w których użytkownik końcowy publikuje nieprawidłowe dane wejściowe (na przykład źle sformułowany ciąg daty), a także przypadki, w których format danych wejściowych jest prawidłowy, ale istnieje naruszenie reguły biznesowej. Gdy wystąpią błędy, formularz powinien zachować dane wejściowe wprowadzone przez użytkownika, aby nie musieli ręcznie wypełniać zmian. Ten proces powinien powtarzać się tyle razy, ile jest to konieczne, dopóki formularz nie zostanie pomyślnie ukończony.

ASP.NET MVC zawiera kilka ładnych wbudowanych funkcji, które ułatwiają obsługę błędów i redisplay formularzy. Aby wyświetlić te funkcje w akcji, zaktualizujmy metodę akcji Edytuj przy użyciu następującego kodu:

//
// POST: /Dinners/Edit/2

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {

        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {

        foreach (var issue in dinner.GetRuleViolations()) {
            ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
        }

        return View(dinner);
    }
}

Powyższy kod jest podobny do poprzedniej implementacji — z tą różnicą, że teraz opakowujemy blok obsługi błędów try/catch wokół naszej pracy. Jeśli wystąpi wyjątek podczas wywoływania metody UpdateModel() lub podczas próby zapisania repozytorium Obiad (co spowoduje wystąpienie wyjątku, jeśli obiekt kolacja, który próbujemy zapisać, jest nieprawidłowy z powodu naruszenia reguły w naszym modelu), zostanie wykonany nasz blok obsługi błędów catch. W ramach tej pętli przejrzymy wszystkie naruszenia reguł, które istnieją w obiekcie Dinner i dodamy je do obiektu ModelState (który omówimy wkrótce). Następnie ponownie odtworzymy widok.

Aby zobaczyć, jak działamy ponownie uruchomimy aplikację, zmodyfikujmy kolację i zmieńmy ją tak, aby miała pusty tytuł, wartość EventDate "BOGUS" i użyjmy numeru telefonu w Wielkiej Brytanii z wartością kraju/regionu USA. Po naciśnięciu przycisku "Zapisz" nie będzie można zapisać kolacji (ponieważ występują błędy) i ponownie wyświetlić formularz:

Zrzut ekranu przedstawiający redisplay formularza z powodu błędów przy użyciu metody edycji T T T S S T S T.

Nasza aplikacja ma przyzwoite środowisko błędów. Elementy tekstowe z nieprawidłowymi danymi wejściowymi są wyróżnione na czerwono, a komunikaty o błędach walidacji są wyświetlane użytkownikowi końcowemu o nich. Formularz zachowuje również dane wejściowe wprowadzone przez użytkownika — dzięki czemu nie trzeba ich wypełniać.

Jak, możesz zapytać, czy tak się stało? W jaki sposób pola tekstowe Tytuł, EventDate i ContactPhone zostały wyróżnione na czerwono i wiedziały, aby dane wyjściowe pierwotnie wprowadzone wartości użytkownika były wyświetlane? Jak komunikaty o błędach zostały wyświetlone na liście u góry? Dobrą wiadomością jest to, że nie wystąpiło to przez magię - raczej dlatego, że użyliśmy niektórych wbudowanych funkcji ASP.NET MVC, które ułatwiają walidację danych wejściowych i obsługę błędów.

Opis modelu ModelState i metod pomocnika HTML walidacji

Klasy kontrolera mają kolekcję właściwości "ModelState", która umożliwia wskazanie, że błędy istnieją z obiektem modelu przekazywanym do widoku. Wpisy błędów w kolekcji ModelState identyfikują nazwę właściwości modelu z problemem (na przykład: "Tytuł", "EventDate" lub "ContactPhone") i umożliwiają określenie przyjaznego dla człowieka komunikatu o błędzie (na przykład: "Tytuł jest wymagany").

Metoda pomocnika UpdateModel() automatycznie wypełnia kolekcję ModelState, gdy wystąpią błędy podczas próby przypisania wartości formularza do właściwości obiektu modelu. Na przykład właściwość EventDate obiektu Kolacja jest typu DateTime. Gdy metoda UpdateModel() nie może przypisać do niej wartości ciągu "BOGUS" w powyższym scenariuszu, metoda UpdateModel() dodała wpis do kolekcji ModelState wskazujący, że wystąpił błąd przypisania z tą właściwością.

Deweloperzy mogą również napisać kod, aby jawnie dodać wpisy błędów do kolekcji ModelState, tak jak poniżej w bloku obsługi błędów "catch", który wypełnia kolekcję ModelState z wpisami opartymi na aktywnych naruszeniach reguł w obiekcie Kolacja:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        foreach (var issue in dinner.GetRuleViolations()) {
            ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
        }

        return View(dinner);
    }
}

Integracja pomocnika HTML z modelem ModelState

Metody pomocnika HTML — takie jak Html.TextBox() — sprawdź kolekcję ModelState podczas renderowania danych wyjściowych. Jeśli istnieje błąd elementu, renderuje wartość wprowadzoną przez użytkownika i klasę błędów CSS.

Na przykład w widoku "Edytuj" używamy metody pomocnika Html.TextBox() do renderowania obiektu EventDate obiektu Kolacja:

<%= Html.TextBox("EventDate", String.Format("{0:g}", Model.EventDate)) %>

Gdy widok został renderowany w scenariuszu błędu, metoda Html.TextBox() sprawdziła kolekcję ModelState, aby sprawdzić, czy wystąpiły jakiekolwiek błędy związane z właściwością "EventDate" obiektu Kolacja. Po ustaleniu, że wystąpił błąd, który renderował przesłane dane wejściowe użytkownika ("BOGUS") jako wartość, i dodał klasę błędów css do typu wejściowego <="pole tekstowe"/> wygenerowano znaczniki:

<input class="input-validation-error"id="EventDate" name="EventDate" type="text" value="BOGUS"/>

Wygląd klasy błędów css można dostosować tak, aby wyglądał jak chcesz. Domyślna klasa błędów CSS — "input-validation-error" — jest zdefiniowana w arkuszu stylów \content\site.css i wygląda następująco:

.input-validation-error
{
    border: 1px solid #ff0000;
    background-color: #ffeeee;
}

Ta reguła CSS jest przyczyną wyróżnienia nieprawidłowych elementów wejściowych, takich jak poniżej:

Zrzut ekranu przedstawiający wyróżnione nieprawidłowe elementy wejściowe.

Html.ValidationMessage() — Metoda pomocnika

Metoda pomocnika Html.ValidationMessage() może służyć do wyprowadzania komunikatu o błędzie ModelState skojarzonego z określoną właściwością modelu:

<%= Html.ValidationMessage("EventDate")%>

Powyższe dane wyjściowe kodu: <span class="field-validation-error" Wartość "BOGUS"> jest nieprawidłowa</span>

Metoda pomocnika Html.ValidationMessage() obsługuje również drugi parametr, który umożliwia deweloperom zastąpienie wyświetlanego komunikatu tekstowego o błędzie:

<%= Html.ValidationMessage("EventDate","*") %>

Powyższe dane wyjściowe kodu: <span class="field-validation-error">*</span> zamiast domyślnego tekstu błędu, gdy występuje błąd dla właściwości EventDate.

Html.ValidationSummary() — Metoda pomocnika

Metoda pomocnika Html.ValidationSummary() może służyć do renderowania komunikatu o błędzie podsumowania wraz z listą <ul><li//><ul> wszystkich szczegółowych komunikatów o błędach w kolekcji ModelState:

Zrzut ekranu przedstawiający listę wszystkich szczegółowych komunikatów o błędach w kolekcji ModelState.

Metoda pomocnika Html.ValidationSummary() przyjmuje opcjonalny parametr ciągu — który definiuje komunikat o błędzie podsumowania wyświetlany powyżej listy szczegółowych błędów:

<%= Html.ValidationSummary("Please correct the errors and try again.") %>

Opcjonalnie możesz użyć css, aby zastąpić wygląd listy błędów.

Korzystanie z metody Pomocnika AddRuleViolations

Nasza początkowa implementacja edycji HTTP-POST użyła instrukcji foreach w bloku catch w celu sprzężenia w pętli naruszeń reguł obiektu Kolacja i dodania ich do kolekcji ModelState kontrolera:

catch {
        foreach (var issue in dinner.GetRuleViolations()) {
            ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
        }

        return View(dinner);
    }

Ten kod może być nieco czystszy, dodając klasę "ControllerHelpers" do projektu NerdDinner i implementując w nim metodę rozszerzenia "AddRuleViolations", która dodaje metodę pomocnika do klasy ASP.NET MVC ModelStateDictionary. Ta metoda rozszerzenia może hermetyzować logikę niezbędną do wypełnienia elementu ModelStateDictionary listą błędów RuleViolation:

public static class ControllerHelpers {

   public static void AddRuleViolations(this ModelStateDictionary modelState, IEnumerable<RuleViolation> errors) {
   
       foreach (RuleViolation issue in errors) {
           modelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
       }
   }
}

Następnie możemy zaktualizować metodę akcji edycji HTTP-POST, aby użyć tej metody rozszerzenia, aby wypełnić kolekcję ModelState naszymi naruszeniami reguł kolacji.

Ukończ implementacje metody akcji Edycji

Poniższy kod implementuje całą logikę kontrolera niezbędną dla naszego scenariusza edycji:

//
// GET: /Dinners/Edit/2

public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(dinner);
}

//
// POST: /Dinners/Edit/2

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        ModelState.AddRuleViolations(dinner.GetRuleViolations());

        return View(dinner);
    }
}

Dobrą rzeczą w naszej implementacji Edit jest to, że nasza klasa Kontrolera ani nasz szablon Widok nie musi wiedzieć nic o określonej weryfikacji lub regułach biznesowych wymuszanych przez nasz model kolacji. W przyszłości możemy dodać dodatkowe reguły do naszego modelu i nie trzeba wprowadzać żadnych zmian kodu w kontrolerze lub widoku, aby mogły być obsługiwane. Zapewnia to elastyczność łatwego rozwoju wymagań aplikacji w przyszłości z minimalnymi zmianami kodu.

Tworzenie pomocy technicznej

Zakończono implementowanie zachowania "Edytuj" klasy DinnersController. Teraz przejdźmy do wdrożenia na nim obsługi "Utwórz", co umożliwi użytkownikom dodawanie nowych kolacji.

Metoda akcji HTTP-GET Create

Zaczniemy od zaimplementowania zachowania HTTP "GET" metody tworzenia akcji. Ta metoda zostanie wywołana, gdy ktoś odwiedza adres URL /Dinners/Create . Nasza implementacja wygląda następująco:

//
// GET: /Dinners/Create

public ActionResult Create() {

    Dinner dinner = new Dinner() {
        EventDate = DateTime.Now.AddDays(7)
    };

    return View(dinner);
}

Powyższy kod tworzy nowy obiekt Dinner i przypisuje jej właściwość EventDate do jednego tygodnia w przyszłości. Następnie renderuje widok oparty na nowym obiekcie Kolacja. Ponieważ nie przekazaliśmy jawnie nazwy metodzie pomocnika View(), użyjemy domyślnej ścieżki opartej na konwencji, aby rozpoznać szablon widoku: /Views/Dinners/Create.aspx.

Teraz utwórzmy ten szablon widoku. Możemy to zrobić, klikając prawym przyciskiem myszy metodę Tworzenia akcji i wybierając polecenie menu kontekstowego "Dodaj widok". W oknie dialogowym "Dodaj widok" wskażemy, że przekazujemy obiekt Kolacja do szablonu widoku i wybieramy automatyczne tworzenie szablonu:

Zrzut ekranu przedstawiający dodawanie widoku w celu utworzenia szablonu widoku.

Po kliknięciu przycisku "Dodaj" program Visual Studio zapisze nowy widok "Create.aspx" oparty na szkieletach do katalogu "\Views\Dinners" i otworzy go w środowisku IDE:

Zrzut ekranu przedstawiający nazwę I D E, aby edytować kod.

Wprowadźmy kilka zmian w domyślnym pliku szkieletu "create", który został wygenerowany dla nas, i zmodyfikujmy go tak, aby wyglądał następująco:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
     Host a Dinner
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Host a Dinner</h2>

    <%=Html.ValidationSummary("Please correct the errors and try again.") %>
 
    <% using (Html.BeginForm()) {%>
  
        <fieldset>
            <p>
                <label for="Title">Title:</label>
                <%= Html.TextBox("Title") %>
                <%= Html.ValidationMessage("Title", "*") %>
            </p>
            <p>
                <label for="EventDate">EventDate:</label>
                <%=Html.TextBox("EventDate") %>
                <%=Html.ValidationMessage("EventDate", "*") %>
            </p>
            <p>
                <label for="Description">Description:</label>
                <%=Html.TextArea("Description") %>
                <%=Html.ValidationMessage("Description", "*") %>
            </p>
            <p>
                <label for="Address">Address:</label>
                <%=Html.TextBox("Address") %>
                <%=Html.ValidationMessage("Address", "*") %>
            </p>
            <p>
                <label for="Country">Country:</label>
                <%=Html.TextBox("Country") %>
                <%=Html.ValidationMessage("Country", "*") %>
            </p>
            <p>
                <label for="ContactPhone">ContactPhone:</label>
                <%=Html.TextBox("ContactPhone") %>
                <%=Html.ValidationMessage("ContactPhone", "*") %>
            </p>            
            <p>
                <label for="Latitude">Latitude:</label>
                <%=Html.TextBox("Latitude") %>
                <%=Html.ValidationMessage("Latitude", "*") %>
            </p>
            <p>
                <label for="Longitude">Longitude:</label>
                <%=Html.TextBox("Longitude") %>
                <%=Html.ValidationMessage("Longitude", "*") %>
            </p>
            <p>
                <input type="submit" value="Save"/>
            </p>
        </fieldset>
    <% } 
%>
</asp:Content>

A teraz, gdy uruchomimy naszą aplikację i uzyskamy dostęp do adresu URL "/Dinners/Create" w przeglądarce, renderuje interfejs użytkownika podobny do poniższego z implementacji akcji Utwórz:

Zrzut ekranu przedstawiający implementację tworzenia akcji podczas uruchamiania aplikacji i uzyskiwania dostępu do kolacji U R L.

Implementowanie metody akcji TWORZENIA HTTP-POST

Mamy zaimplementowaną wersję HTTP-GET metody akcji Create. Gdy użytkownik kliknie przycisk "Zapisz", wykonuje wpis formularza w adresie URL /Dinners/Create i przesyła wartości formularza wejściowego> HTML <przy użyciu czasownika HTTP POST.

Teraz zaimplementujmy zachowanie POST protokołu HTTP metody tworzenia akcji. Zaczniemy od dodania przeciążonej metody akcji "Utwórz" do elementu DinnersController z atrybutem "AcceptVerbs", który wskazuje, że obsługuje scenariusze HTTP POST:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create() {
    ...
}

Istnieje wiele sposobów uzyskiwania dostępu do opublikowanych parametrów formularza w ramach naszej metody "Create" z włączoną obsługą protokołu HTTP-POST.

Jednym z podejść jest utworzenie nowego obiektu Dinner, a następnie użycie metody pomocnika UpdateModel() (tak jak w przypadku akcji Edytuj), aby wypełnić go wartościami opublikowanych formularzy. Następnie możemy dodać go do naszego repozytorium DinnerRepository, utrwały go w bazie danych i przekierować użytkownika do naszej akcji Szczegóły, aby wyświetlić nowo utworzoną kolację przy użyciu poniższego kodu:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create() {

    Dinner dinner = new Dinner();

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Add(dinner);
        dinnerRepository.Save();

        return RedirectToAction("Details", new {id=dinner.DinnerID});
    }
    catch {
    
        ModelState.AddRuleViolations(dinner.GetRuleViolations());

        return View(dinner);
    }
}

Alternatywnie możemy użyć metody, w której mamy metodę akcji Create() jako parametr metody, użyjemy metody . ASP.NET MVC automatycznie utworzy dla nas wystąpienie nowego obiektu Dinner, wypełni jego właściwości przy użyciu danych wejściowych formularza i przekaże go do naszej metody akcji:

//
//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new {id = dinner.DinnerID });
        }
        catch {        
            ModelState.AddRuleViolations(dinner.GetRuleViolations());
        }
    }
    
    return View(dinner);
}

Nasza metoda akcji powyżej sprawdza, czy obiekt Dinner został pomyślnie wypełniony wartościami post formularza, sprawdzając właściwość ModelState.IsValid. Spowoduje to zwrócenie wartości false, jeśli występują problemy z konwersją danych wejściowych (na przykład ciąg "BOGUS" dla właściwości EventDate) i jeśli wystąpią problemy, nasza metoda akcji ponownie rozdziela formularz.

Jeśli wartości wejściowe są prawidłowe, metoda akcji próbuje dodać i zapisać nową kolację w repozytorium DinnerRepository. Opakowuje tę pracę w bloku try/catch i redisplays formularza, jeśli istnieją jakiekolwiek naruszenia reguł biznesowych (co spowodowałoby, że metoda dinnerRepository.Save() zgłosi wyjątek.

Aby zobaczyć, jak działa ta obsługa błędów, możemy zażądać adresu URL /Dinners/Create i wypełnić szczegóły dotyczące nowej kolacji. Niepoprawne dane wejściowe lub wartości spowodują ponowne ponownedisredisplayed formularza tworzenia z błędami wyróżnionymi w następujący sposób:

Zrzut ekranu przedstawiający formularz odtwarzany z wyróżnionymi błędami.

Zwróć uwagę, że formularz Tworzenia jest honorujący dokładnie te same reguły weryfikacji i biznesowe, co formularz edycji. Wynika to z tego, że nasze reguły walidacji i biznesowe zostały zdefiniowane w modelu i nie zostały osadzone w interfejsie użytkownika lub kontrolerze aplikacji. Oznacza to, że możemy później zmienić/rozwinąć nasze reguły weryfikacji lub biznesowe w jednym miejscu i zastosować je w całej naszej aplikacji. Nie będziemy musieli zmieniać żadnego kodu w naszych metodach akcji Edytuj lub Utwórz, aby automatycznie przestrzegać wszelkich nowych reguł ani modyfikacji istniejących.

Po naprawieniu wartości wejściowych i ponownym kliknięciu przycisku "Zapisz" nasz dodatek do repozytorium DinnerRepository zakończy się pomyślnie, a nowa kolacja zostanie dodana do bazy danych. Następnie nastąpi przekierowanie do adresu URL /Dinners/Details/[id] — gdzie zostaną wyświetlone szczegółowe informacje o nowo utworzonej kolacji:

Zrzut ekranu przedstawiający nowo utworzoną kolację.

Usuwanie pomocy technicznej

Teraz dodajmy obsługę "Usuń" do naszego kontrolera DinnersController.

Metoda akcji usuwania HTTP-GET

Zaczniemy od zaimplementowania zachowania HTTP GET naszej metody akcji usuwania. Ta metoda zostanie wywołana, gdy ktoś odwiedzi adres URL /Dinners/Delete/[id]. Poniżej przedstawiono implementację:

//
// HTTP GET: /Dinners/Delete/1

public ActionResult Delete(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (dinner == null)
         return View("NotFound");
    else
        return View(dinner);
}

Metoda akcji próbuje pobrać kolację do usunięcia. Jeśli obiad istnieje, renderuje widok na podstawie obiektu Dinner. Jeśli obiekt nie istnieje (lub został już usunięty), zwraca widok renderujący utworzony wcześniej szablon widoku "NotFound" dla metody akcji "Szczegóły".

Możemy utworzyć szablon widoku "Usuń", klikając prawym przyciskiem myszy metodę akcji Usuń i wybierając polecenie menu kontekstowego "Dodaj widok". W oknie dialogowym "Dodaj widok" wskażemy, że przekazujemy obiekt Dinner do szablonu widoku jako jego model, a następnie wybierzemy utworzenie pustego szablonu:

Zrzut ekranu przedstawiający tworzenie szablonu usuń widok jako pusty szablon.

Po kliknięciu przycisku "Dodaj" program Visual Studio doda nowy plik szablonu "Delete.aspx" dla nas w katalogu "\Views\Dinners". Dodamy kod HTML i kod do szablonu, aby zaimplementować ekran potwierdzenia usuwania, jak pokazano poniżej:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Delete Confirmation:  <%=Html.Encode(Model.Title) %>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>
        Delete Confirmation
    </h2>

    <div>
        <p>Please confirm you want to cancel the dinner titled: 
           <i> <%=Html.Encode(Model.Title) %>? </i> 
        </p>
    </div>
    
    <% using (Html.BeginForm()) {  %>
        <input name="confirmButton" type="submit" value="Delete" />        
    <% } %>
     
</asp:Content>

Powyższy kod wyświetla tytuł kolacji do usunięcia i zwraca <element formularza> , który wykonuje post do adresu URL /Dinners/Delete/[id], jeśli użytkownik końcowy kliknie w nim przycisk "Usuń".

Po uruchomieniu aplikacji i korzystaniu z adresu URL "/Dinners/Delete/[id]" dla prawidłowego obiektu Dinner renderuje interfejs użytkownika, jak pokazano poniżej:

Zrzut ekranu przedstawiający metodę akcji Usuwania kolacji U I w metodzie akcji Usuń T T G E.

Temat boczny: Dlaczego robimy post?
Możesz zapytać — dlaczego wykonaliśmy prace nad utworzeniem <formularza> na ekranie potwierdzenia Usuń? Dlaczego nie wystarczy użyć standardowego hiperlinku, aby połączyć się z metodą akcji, która wykonuje rzeczywistą operację usuwania? Przyczyną jest to, że chcemy zachować ostrożność przed przeszukiwarkami internetowymi i aparatami wyszukiwania, odnajdując nasze adresy URL i przypadkowo powodując usunięcie danych, gdy są one zgodne z linkami. Adresy URL oparte na protokole HTTP-GET są uznawane za "bezpieczne" w celu uzyskania dostępu do/przeszukiwania i nie powinny być zgodne z adresami HTTP-POST. Dobrą regułą jest upewnienie się, że zawsze umieszczasz operacje destrukcyjne lub modyfikujące dane za żądaniami HTTP-POST.

Implementowanie metody akcji usuwania HTTP-POST

Mamy teraz zaimplementowaną wersję HTTP-GET metody akcji Usuń, która wyświetla ekran potwierdzenia usunięcia. Gdy użytkownik końcowy kliknie przycisk "Usuń", wykona wpis formularza pod adresem URL /Dinners/Dinner/[id].

Teraz zaimplementujmy zachowanie HTTP "POST" metody akcji usuwania przy użyciu poniższego kodu:

// 
// HTTP POST: /Dinners/Delete/1

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int id, string confirmButton) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    if (dinner == null)
        return View("NotFound");

    dinnerRepository.Delete(dinner);
    dinnerRepository.Save();

    return View("Deleted");
}

Wersja HTTP-POST naszej metody akcji Usuń próbuje pobrać obiekt kolacji do usunięcia. Jeśli nie można go znaleźć (ponieważ został on już usunięty), renderuje nasz szablon "NotFound". Jeśli znajdzie kolację, usunie ją z repozytorium DinnerRepository. Następnie renderuje szablon "Usunięte".

Aby zaimplementować szablon "Usunięte", kliknij prawym przyciskiem myszy metodę akcji i wybierz menu kontekstowe "Dodaj widok". Nadamy widokowi nazwę "Usunięto" i będziemy mieć pusty szablon (a nie będziemy używać silnie typizowanego obiektu modelu). Następnie dodamy do niego zawartość HTML:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Dinner Deleted
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Dinner Deleted</h2>

    <div>
        <p>Your dinner was successfully deleted.</p>
    </div>
    
    <div>
        <p><a href="/dinners">Click for Upcoming Dinners</a></p>
    </div>
    
</asp:Content>

A teraz, gdy uruchomimy aplikację i uzyskamy dostęp do adresu URL "/Dinners/Delete/[id]" dla prawidłowego obiektu obiad, spowoduje to wyświetlenie ekranu potwierdzenia usunięcia kolacji, jak pokazano poniżej:

Zrzut ekranu przedstawiający ekran potwierdzenia usunięcia kolacji w metodzie akcji Usuń T T T P P O S.

Kliknięcie przycisku "Usuń" spowoduje wykonanie żądania HTTP-POST pod adresem URL /Dinners/Delete/[id], co spowoduje usunięcie kolacji z bazy danych i wyświetlenie szablonu widoku "Usunięte":

Zrzut ekranu przedstawiający szablon Usunięty widok.

Zabezpieczenia powiązań modelu

Omówiliśmy dwa różne sposoby korzystania z wbudowanych funkcji powiązania modelu ASP.NET MVC. Pierwsza metoda UpdateModel() do aktualizowania właściwości istniejącego obiektu modelu, a druga przy użyciu obsługi ASP.NET MVC przekazywania obiektów modelu jako parametrów metody akcji. Obie te techniki są bardzo wydajne i bardzo przydatne.

Ta moc wiąże się również z odpowiedzialnością. Ważne jest, aby zawsze być paranoicznie na temat zabezpieczeń podczas akceptowania jakichkolwiek danych wejściowych użytkownika i jest to również prawdziwe w przypadku tworzenia powiązań obiektów w celu utworzenia danych wejściowych. Należy zachować ostrożność, aby zawsze kodować wszystkie wartości wprowadzone przez użytkownika, aby uniknąć ataków polegających na wstrzyknięciu kodu HTML i JavaScript oraz zachować ostrożność podczas ataków polegających na wstrzyknięciu kodu SQL (uwaga: używamy LINQ to SQL dla naszej aplikacji, która automatycznie koduje parametry, aby zapobiec atakom tego typu). Nigdy nie należy polegać tylko na weryfikacji po stronie klienta i zawsze używać weryfikacji po stronie serwera, aby chronić się przed hakerami próbującymi wysłać fałszywe wartości.

Jednym z dodatkowych elementów zabezpieczeń, które należy wziąć pod uwagę podczas korzystania z funkcji powiązania ASP.NET MVC, jest zakresem obiektów, które są tworzone. W szczególności chcesz mieć pewność, że rozumiesz konsekwencje bezpieczeństwa właściwości, które zezwalasz na powiązanie, i upewnij się, że zezwalasz tylko na te właściwości, które naprawdę powinny być aktualizowane przez użytkownika końcowego.

Domyślnie metoda UpdateModel() podejmie próbę zaktualizowania wszystkich właściwości obiektu modelu zgodnego z przychodzącymi wartościami parametrów formularza. Podobnie obiekty przekazywane jako parametry metody akcji również domyślnie mogą mieć wszystkie ich właściwości ustawione za pomocą parametrów formularza.

Blokowanie powiązania na podstawie użycia

Zasady powiązań można zablokować na podstawie użycia, podając jawną "listę dołączaną" właściwości, które można zaktualizować. Można to zrobić, przekazując dodatkowy parametr tablicy ciągów do metody UpdateModel(), jak pokazano poniżej:

string[] allowedProperties = new[]{ "Title","Description", 
                                    "ContactPhone", "Address",
                                    "EventDate", "Latitude", 
                                    "Longitude"};
                                    
UpdateModel(dinner, allowedProperties);

Obiekty przekazywane jako parametry metody akcji obsługują również atrybut [Bind], który umożliwia określenie "listy dołączanej" dozwolonych właściwości, jak pokazano poniżej:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create( [Bind(Include="Title,Address")] Dinner dinner ) {
    ...
}

Blokowanie powiązania na podstawie typu

Reguły powiązań można również zablokować dla poszczególnych typów. Dzięki temu można raz określić reguły powiązań, a następnie zastosować je we wszystkich scenariuszach (w tym scenariuszach parametrów UpdateModel i metody akcji) we wszystkich kontrolerach i metodach akcji.

Reguły powiązań poszczególnych typów można dostosować, dodając atrybut [Bind] do typu lub rejestrując go w pliku Global.asax aplikacji (przydatne w scenariuszach, w których nie jesteś właścicielem typu). Następnie można użyć właściwości Dołączanie i wykluczanie atrybutu powiązania, aby kontrolować, które właściwości można powiązać dla określonej klasy lub interfejsu.

Użyjemy tej techniki dla klasy Dinner w aplikacji NerdDinner i dodamy do niej atrybut [Bind], który ogranicza listę powiązanych właściwości do następujących:

[Bind(Include="Title,Description,EventDate,Address,Country,ContactPhone,Latitude,Longitude")]
public partial class Dinner {
   ...
}

Zwróć uwagę, że nie zezwalamy na manipulowanie kolekcją RSVPs za pośrednictwem powiązania ani nie zezwalamy na ustawianie właściwości DinnerID lub HostedBy za pośrednictwem powiązania. Ze względów bezpieczeństwa będziemy zamiast tego manipulować tylko tymi konkretnymi właściwościami przy użyciu jawnego kodu w naszych metodach akcji.

Wrap-Up CRUD

ASP.NET MVC zawiera szereg wbudowanych funkcji, które ułatwiają implementowanie scenariuszy publikowania formularzy. Użyliśmy różnych funkcji, aby zapewnić obsługę interfejsu użytkownika CRUD na podstawie naszego repozytorium DinnerRepository.

Używamy podejścia ukierunkowanego na model w celu zaimplementowania naszej aplikacji. Oznacza to, że cała nasza logika walidacji i reguł biznesowych jest definiowana w warstwie modelu — a nie w naszych kontrolerach lub widokach. Ani nasza klasa Kontroler, ani szablony widoków nie wiedzą nic o konkretnych regułach biznesowych wymuszanych przez naszą klasę modelu Obiad.

Dzięki temu nasza architektura aplikacji będzie czysta i łatwiejsza do testowania. W przyszłości możemy dodać dodatkowe reguły biznesowe do naszej warstwy modelu i nie trzeba wprowadzać żadnych zmian w kodzie kontrolera lub widoku, aby mogły być obsługiwane. Zapewni to nam dużą elastyczność w zakresie rozwoju i zmiany naszej aplikacji w przyszłości.

Nasza funkcja DinnersController umożliwia teraz wyświetlanie/szczegóły kolacji, a także tworzenie, edytowanie i usuwanie pomocy technicznej. Kompletny kod dla klasy można znaleźć poniżej:

public class DinnersController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // GET: /Dinners/

    public ActionResult Index() {

        var dinners = dinnerRepository.FindUpcomingDinners().ToList();
        return View(dinners);
    }

    //
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View(dinner);
    }

    //
    // GET: /Dinners/Edit/2

    public ActionResult Edit(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);
        return View(dinner);
    }

    //
    // POST: /Dinners/Edit/2

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection formValues) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        try {
            UpdateModel(dinner);

            dinnerRepository.Save();

            return RedirectToAction("Details", new { id= dinner.DinnerID });
        }
        catch {
            ModelState.AddRuleViolations(dinner.GetRuleViolations());

            return View(dinner);
        }
    }

    //
    // GET: /Dinners/Create

    public ActionResult Create() {

        Dinner dinner = new Dinner() {
            EventDate = DateTime.Now.AddDays(7)
        };
        return View(dinner);
    }

    //
    // POST: /Dinners/Create

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Dinner dinner) {

        if (ModelState.IsValid) {

            try {
                dinner.HostedBy = "SomeUser";

                dinnerRepository.Add(dinner);
                dinnerRepository.Save();

                return RedirectToAction("Details", new{id=dinner.DinnerID});
            }
            catch {
                ModelState.AddRuleViolations(dinner.GetRuleViolations());
            }
        }

        return View(dinner);
    }

    //
    // HTTP GET: /Dinners/Delete/1

    public ActionResult Delete(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View(dinner);
    }

    // 
    // HTTP POST: /Dinners/Delete/1

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Delete(int id, string confirmButton) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");

        dinnerRepository.Delete(dinner);
        dinnerRepository.Save();

        return View("Deleted");
    }
}

Następny krok

Mamy teraz podstawową obsługę operacji CRUD (Tworzenie, odczyt, aktualizowanie i usuwanie) w ramach naszej klasy DinnersController.

Przyjrzyjmy się teraz, jak możemy użyć klas ViewData i ViewModel, aby włączyć jeszcze bogatszy interfejs użytkownika w formularzach.