이는 ASP.NET MVC 1을 사용하여 작지만 완전한 웹 애플리케이션을 빌드하는 방법을 안내하는 무료 "NerdDinner" 애플리케이션 자습서 의 5단계입니다.
5단계에서는 Dinners를 편집, 만들기 및 삭제할 수 있도록 지원하여 DinnersController 클래스를 더 자세히 수행하는 방법을 보여 줍니다.
ASP.NET MVC 3을 사용하는 경우 MVC 3 또는 MVC Music Store에서 시작 자습서를 따르는 것이 좋습니다.
NerdDinner 5단계: 양식 시나리오 만들기, 업데이트, 삭제
컨트롤러와 보기를 소개하고 이를 사용하여 사이트에서 Dinners에 대한 목록/세부 정보 환경을 구현하는 방법을 설명했습니다. 다음 단계는 DinnersController 클래스를 더 자세히 살펴보고 Dinners를 편집, 만들기 및 삭제할 수 있도록 지원하는 것입니다.
DinnersController에서 처리하는 URL
이전에 DinnersController에 /Dinners 및 /Dinners/Details/[id]의 두 URL에 대한 지원을 구현하는 작업 메서드를 추가했습니다.
URL | 동사 | 용도 |
---|---|---|
/저녁 식사/ | GET | 예정된 저녁 식사의 HTML 목록을 표시합니다. |
/Dinners/Details/[id] | GET | 특정 저녁 식사에 대한 세부 정보를 표시합니다. |
이제 /Dinners/Edit/[id], /Dinners/Create 및 /Dinners/Delete/[id]의 세 가지 추가 URL을 구현하는 작업 메서드를 추가합니다. 이러한 URL을 사용하면 기존 저녁 식사를 편집하고, 새 저녁 식사를 만들고, 저녁 식사를 삭제할 수 있습니다.
이러한 새 URL과의 HTTP GET 및 HTTP POST 동사 상호 작용을 모두 지원합니다. 이러한 URL에 대한 HTTP GET 요청은 데이터의 초기 HTML 보기("편집"의 경우 Dinner 데이터로 채워진 양식, "만들기"의 경우 빈 양식, "delete"의 경우 삭제 확인 화면)를 표시합니다. 이러한 URL에 대한 HTTP POST 요청은 DinnerRepository(그리고 여기에서 데이터베이스로)에서 Dinner 데이터를 저장/업데이트/삭제합니다.
URL | 동사 | 용도 |
---|---|---|
/Dinners/Edit/[id] | GET | 저녁 식사 데이터로 채워진 편집 가능한 HTML 양식을 표시합니다. |
POST | 특정 Dinner의 양식 변경 내용을 데이터베이스에 저장합니다. | |
/Dinners/Create | GET | 사용자가 새 저녁 식사를 정의할 수 있는 빈 HTML 양식을 표시합니다. |
POST | 새 Dinner를 만들고 데이터베이스에 저장합니다. | |
/Dinners/Delete/[id] | GET | 삭제 확인 화면을 표시합니다. |
POST | 데이터베이스에서 지정된 저녁 식사를 삭제합니다. |
지원 편집
먼저 "편집" 시나리오를 구현해 보겠습니다.
HTTP-GET 편집 작업 메서드
먼저 편집 작업 메서드의 HTTP "GET" 동작을 구현합니다. 이 메서드는 /Dinners/Edit/[id] URL이 요청될 때 호출됩니다. 구현은 다음과 같습니다.
//
// GET: /Dinners/Edit/2
public ActionResult Edit(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
return View(dinner);
}
위의 코드는 DinnerRepository를 사용하여 Dinner 개체를 검색합니다. 그런 다음 Dinner 개체를 사용하여 View 템플릿을 렌더링합니다. 템플릿 이름을 View() 도우미 메서드에 명시적으로 전달하지 않았기 때문에 규칙 기반 기본 경로를 사용하여 보기 템플릿을 resolve 것입니다. /Views/Dinners/Edit.aspx.
이제 이 보기 템플릿을 만들어 보겠습니다. 편집 메서드 내에서 마우스 오른쪽 단추를 클릭하고 "보기 추가" 상황에 맞는 메뉴 명령을 선택하여 이 작업을 수행합니다.
"보기 추가" 대화 상자 내에서 Dinner 개체를 해당 모델로 뷰 템플릿에 전달하고 있음을 나타내고 "편집" 템플릿을 자동으로 스캐폴드하도록 선택합니다.
"추가" 단추를 클릭하면 Visual Studio에서 "\Views\Dinners" 디렉터리 내에 새 "Edit.aspx" 보기 템플릿 파일을 추가합니다. 또한 아래와 같은 초기 "편집" 스캐폴드 구현으로 채워진 코드 편집기 내에서 새 "Edit.aspx" 보기 템플릿이 열립니다.
생성된 기본 "편집" 스캐폴드를 몇 가지 변경하고 아래 콘텐츠를 포함하도록 편집 보기 템플릿을 업데이트해 보겠습니다(노출하지 않으려는 속성 중 일부를 제거).
<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>
애플리케이션을 실행하고 "/Dinners/Edit/1" URL을 요청하면 다음 페이지가 표시됩니다.
보기에서 생성된 HTML 태그는 다음과 같습니다. 표준 HTML<입니다. "저장" 입력 type="submit"</> 단추를 누를 때 /Dinners/Edit/1 URL에 대한 HTTP POST를 수행하는 양식> 요소가 있습니다. HTML <입력 type="text"/> 요소는 편집 가능한 각 속성에 대해 출력되었습니다.
Html.BeginForm() 및 Html.TextBox() Html 도우미 메서드
"Edit.aspx" 뷰 템플릿은 Html.ValidationSummary(), Html.BeginForm(), Html.TextBox() 및 Html.ValidationMessage()와 같은 몇 가지 "Html 도우미" 메서드를 사용합니다. 이러한 도우미 메서드는 HTML 태그를 생성하는 것 외에도 기본 제공 오류 처리 및 유효성 검사 지원을 제공합니다.
Html.BeginForm() 도우미 메서드
Html.BeginForm() 도우미 메서드는 태그의 HTML <양식> 요소를 출력하는 메서드입니다. Edit.aspx 뷰 템플릿에서는 이 메서드를 사용할 때 C# "using" 문을 적용하고 있음을 알 수 있습니다. 열린 중괄호는 양식 콘텐츠의 <시작을 나타내고 닫는 중괄호는 /form> 요소의 <끝을> 나타냅니다.
<% using (Html.BeginForm()) { %>
<fieldset>
<!-- Fields Omitted for Brevity -->
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
<% } %>
또는 이와 같은 시나리오에 대해 "using" 문 접근 방식이 부자연스럽지 않은 경우 Html.BeginForm() 및 Html.EndForm() 조합(동일한 작업을 수행)을 사용할 수 있습니다.
<% Html.BeginForm(); %>
<fieldset>
<!-- Fields Omitted for Brevity -->
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
<% Html.EndForm(); %>
매개 변수 없이 Html.BeginForm()을 호출하면 HTTP-POST를 수행하는 양식 요소가 현재 요청의 URL로 출력됩니다. 따라서 편집 보기에서 양식 action="/Dinners/Edit/1" method="post"> 요소를 생성<합니다. 다른 URL에 게시하려는 경우 Html.BeginForm()에 명시적 매개 변수를 전달할 수도 있습니다.
Html.TextBox() 도우미 메서드
Edit.aspx 보기는 Html.TextBox() 도우미 메서드를 사용하여 input type="text"/> 요소를 출력<합니다.
<%= Html.TextBox("Title") %>
위의 Html.TextBox() 메서드는 단일 매개 변수를 사용합니다. 이 매개 변수는 입력 형식="text"/> 요소의 <id/name 특성과 텍스트 상자 값을 채우는 모델 속성을 모두 지정하는 데 사용됩니다. 예를 들어 편집 보기에 전달한 Dinner 개체에 "Title" 속성 값이 ".NET Futures"이므로 Html.TextBox("Title") 메서드 호출 출력: <input id="Title" name="Title" type="text" value=".NET Futures" />.
또는 첫 번째 Html.TextBox() 매개 변수를 사용하여 요소의 id/이름을 지정한 다음, 두 번째 매개 변수로 사용할 값을 명시적으로 전달할 수 있습니다.
<%= Html.TextBox("Title", Model.Title)%>
출력되는 값에 대해 사용자 지정 서식 지정을 수행하는 경우가 많습니다. .NET에 기본 제공되는 String.Format() 정적 메서드는 이러한 시나리오에 유용합니다. Edit.aspx 뷰 템플릿은 이 템플릿을 사용하여 시간 동안 초를 표시하지 않도록 EventDate 값(DateTime 형식)의 형식을 지정합니다.
<%= Html.TextBox("EventDate", String.Format("{0:g}", Model.EventDate)) %>
Html.TextBox()에 대한 세 번째 매개 변수는 필요에 따라 추가 HTML 특성을 출력하는 데 사용할 수 있습니다. 아래 코드 조각은 input type="text"/> 요소에 추가 size="30" 특성 및 class="mycssclass" 특성을 <렌더링하는 방법을 보여 줍니다. "class"는 C#의 예약된 키워드(keyword) 때문에 "@" 문자를 사용하여 클래스 특성의 이름을 이스케이프하는 방법을 확인합니다.
<%= Html.TextBox("Title", Model.Title, new { size=30, @class="myclass" } )%>
HTTP-POST 편집 작업 메서드 구현
이제 편집 작업 메서드의 HTTP-GET 버전이 구현되었습니다. 사용자가 /Dinners/Edit/1 URL을 요청하면 다음과 같은 HTML 페이지가 수신됩니다.
"저장" 단추를 누르면 양식 게시물이 /Dinners/Edit/1 URL로 이동하고 HTTP POST 동사를 사용하여 HTML <입력> 양식 값을 제출합니다. 이제 Dinner 저장을 처리하는 편집 작업 메서드의 HTTP POST 동작을 구현해 보겠습니다.
먼저 HTTP POST 시나리오를 처리한다는 "AcceptVerbs" 특성이 있는 DinnersController에 오버로드된 "편집" 작업 메서드를 추가합니다.
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
...
}
[AcceptVerbs] 특성이 오버로드된 작업 메서드에 적용되면 ASP.NET MVC는 들어오는 HTTP 동사에 따라 적절한 작업 메서드에 대한 디스패치 요청을 자동으로 처리합니다. /Dinners/Edit/[id] URL에 대한 HTTP POST 요청은 위의 Edit 메서드로 이동하지만 /Dinners/Edit/[id] URL에 대한 다른 모든 HTTP 동사 요청은 구현한 첫 번째 Edit 메서드(특성이 [AcceptVerbs]
없음)로 이동합니다.
측면 항목: HTTP 동사를 통해 차별화하는 이유는 무엇인가요? |
---|
단일 URL을 사용하고 HTTP 동사를 통해 해당 동작을 구분하는 이유는 무엇인가요? 편집 변경 내용 로드 및 저장을 처리하기 위한 두 개의 별도 URL만 있는 것은 어떨까요? 예: /Dinners/Edit/[id]를 사용하여 초기 양식을 표시하고 /Dinners/Save/[id]를 사용하여 양식 게시물을 처리하여 저장하시겠습니까? 두 개의 개별 URL을 게시할 때의 단점은 /Dinners/Save/2에 게시한 다음 입력 오류로 인해 HTML 양식을 다시 표시해야 하는 경우 최종 사용자가 브라우저의 주소 표시줄에 /Dinners/Save/2 URL이 있다는 것입니다(양식이 게시한 URL이므로). 최종 사용자가 이 다시 표시된 페이지를 브라우저 즐겨찾기 목록에 책갈피로 지정하거나 URL을 복사/붙여넣고 친구에게 전자 메일을 보내면 나중에 작동하지 않는 URL을 저장하게 됩니다(URL은 게시 값에 따라 달라지므로). 단일 URL(예: /Dinners/Edit/[id])을 노출하고 HTTP 동사로 처리를 구분하면 최종 사용자가 편집 페이지에 책갈피를 지정하거나 다른 사람에게 URL을 보내는 것이 안전합니다. |
양식 게시 값 검색
HTTP POST "편집" 메서드 내에서 게시된 양식 매개 변수에 액세스할 수 있는 다양한 방법이 있습니다. 한 가지 간단한 방법은 Controller 기본 클래스의 Request 속성을 사용하여 양식 컬렉션에 액세스하고 게시된 값을 직접 검색하는 것입니다.
//
// 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 });
}
위의 방법은 약간 자세히 설명하지만, 특히 오류 처리 논리를 추가한 후에는 그렇습니다.
이 시나리오의 더 나은 방법은 컨트롤러 기본 클래스에서 기본 제공 UpdateModel() 도우미 메서드를 활용하는 것입니다. 들어오는 양식 매개 변수를 사용하여 전달하는 개체의 속성 업데이트를 지원합니다. 리플렉션을 사용하여 개체의 속성 이름을 확인한 다음 클라이언트에서 제출한 입력 값에 따라 값을 자동으로 변환하고 할당합니다.
UpdateModel() 메서드를 사용하여 다음 코드를 사용하여 HTTP-POST 편집 작업을 간소화할 수 있습니다.
//
// 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 });
}
이제 /Dinners/Edit/1 URL을 방문하여 Dinner의 제목을 변경할 수 있습니다.
"저장" 단추를 클릭하면 편집 작업에 양식 게시가 수행되고 업데이트된 값이 데이터베이스에 유지됩니다. 그러면 Dinner의 세부 정보 URL로 리디렉션됩니다(새로 저장된 값이 표시됨).
편집 오류 처리
현재 HTTP-POST 구현은 오류가 있는 경우를 제외하고 잘 작동합니다.
사용자가 양식을 잘못 편집하는 경우 양식 수정을 안내하는 정보 오류 메시지와 함께 양식이 다시 표시되는지 확인해야 합니다. 여기에는 최종 사용자가 잘못된 입력(예: 형식이 잘못된 날짜 문자열)을 게시하는 경우와 입력 형식이 유효하지만 비즈니스 규칙 위반이 있는 경우가 포함됩니다. 오류가 발생하면 양식은 사용자가 원래 입력한 입력 데이터를 유지하여 변경 내용을 수동으로 다시 채울 필요가 없도록 해야 합니다. 이 프로세스는 양식이 성공적으로 완료될 때까지 필요한 만큼 반복되어야 합니다.
ASP.NET MVC에는 오류 처리 및 다시 표시를 쉽게 만드는 몇 가지 멋진 기본 제공 기능이 포함되어 있습니다. 이러한 기능이 작동하는 것을 보려면 다음 코드로 Edit 작업 메서드를 업데이트해 보겠습니다.
//
// 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);
}
}
위의 코드는 이전 구현과 유사합니다. 단, 현재 작업 주위에 try/catch 오류 처리 블록을 래핑하고 있습니다. UpdateModel()을 호출할 때 또는 DinnerRepository를 저장하려고 할 때 예외가 발생하는 경우(모델 내에서 규칙 위반으로 인해 저장하려는 Dinner 개체가 유효하지 않은 경우 예외가 발생함) catch 오류 처리 블록이 실행됩니다. 그 안에서 Dinner 개체에 있는 규칙 위반을 반복하고 ModelState 개체에 추가합니다(곧 설명하겠습니다). 그런 다음 보기를 다시 표시합니다.
이 작업을 확인하려면 애플리케이션을 다시 실행하고, Dinner를 편집하고, 빈 제목, "BOGUS"의 EventDate를 갖도록 변경하고, 국가/지역 값이 미국인 영국 전화 번호를 사용합니다. "저장" 단추를 누르면 HTTP POST 편집 메서드가 저녁 식사를 저장할 수 없으며(오류가 있기 때문에) 양식을 다시 표시합니다.
애플리케이션에 적절한 오류 환경이 있습니다. 잘못된 입력이 있는 텍스트 요소가 빨간색으로 강조 표시되고 유효성 검사 오류 메시지가 최종 사용자에게 표시됩니다. 양식은 사용자가 원래 입력한 입력 데이터를 유지하므로 아무 것도 다시 채울 필요가 없습니다.
어떻게, 당신은 물어 볼 수 있습니다, 이 발생 했습니까? Title, EventDate 및 ContactPhone 텍스트 상자가 빨간색으로 강조 표시되고 원래 입력한 사용자 값을 출력하는 것을 어떻게 알 수 있나요? 그리고 오류 메시지가 맨 위에 있는 목록에 어떻게 표시되었나요? 좋은 소식은 매직에서 발생하지 않았다는 것입니다. 오히려 입력 유효성 검사 및 오류 처리 시나리오를 쉽게 만드는 기본 제공 ASP.NET MVC 기능 중 일부를 사용했기 때문입니다.
ModelState 및 유효성 검사 HTML 도우미 메서드 이해
컨트롤러 클래스에는 View에 전달되는 모델 개체와 함께 오류가 있음을 나타내는 방법을 제공하는 "ModelState" 속성 컬렉션이 있습니다. ModelState 컬렉션 내의 오류 항목은 문제가 있는 모델 속성의 이름(예: "Title", "EventDate" 또는 "ContactPhone")을 식별하고 사용자에게 친숙한 오류 메시지를 지정할 수 있도록 허용합니다(예: "제목 필요").
Model 개체의 속성에 양식 값을 할당하는 동안 오류가 발생하면 UpdateModel() 도우미 메서드가 ModelState 컬렉션을 자동으로 채웁니다. 예를 들어 Dinner 개체의 EventDate 속성은 DateTime 형식입니다. 위의 시나리오에서 UpdateModel() 메서드가 문자열 값 "BOGUS"를 할당할 수 없는 경우 UpdateModel() 메서드는 해당 속성에서 할당 오류가 발생했음을 나타내는 항목을 ModelState 컬렉션에 추가했습니다.
개발자는 아래의 "catch" 오류 처리 블록 내에서 수행하는 것처럼 ModelState 컬렉션에 오류 항목을 명시적으로 추가하는 코드를 작성할 수도 있습니다. 이 블록은 Dinner 개체의 활성 규칙 위반을 기반으로 하는 항목으로 ModelState 컬렉션을 채웁니다.
[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);
}
}
ModelState와 Html 도우미 통합
Html.TextBox()와 같은 HTML 도우미 메서드는 출력을 렌더링할 때 ModelState 컬렉션을 검사. 항목에 대한 오류가 있는 경우 사용자가 입력한 값과 CSS 오류 클래스를 렌더링합니다.
예를 들어 "편집" 보기에서는 Html.TextBox() 도우미 메서드를 사용하여 Dinner 개체의 EventDate를 렌더링합니다.
<%= Html.TextBox("EventDate", String.Format("{0:g}", Model.EventDate)) %>
오류 시나리오에서 뷰가 렌더링되었을 때 Html.TextBox() 메서드는 ModelState 컬렉션을 확인하여 Dinner 개체의 "EventDate" 속성과 관련된 오류가 있는지 확인했습니다. 오류가 있는 것으로 확인되면 제출된 사용자 입력("BOGUS")을 값으로 렌더링하고 생성된 input type="textbox"/> 태그에 <css 오류 클래스를 추가했습니다.
<input class="input-validation-error"id="EventDate" name="EventDate" type="text" value="BOGUS"/>
원하는 대로 표시되도록 css 오류 클래스의 모양을 사용자 지정할 수 있습니다. 기본 CSS 오류 클래스인 "input-validation-error"는 \content\site.css 스타일시트에 정의되어 있으며 아래와 같습니다.
.input-validation-error
{
border: 1px solid #ff0000;
background-color: #ffeeee;
}
이 CSS 규칙은 잘못된 입력 요소를 아래와 같이 강조 표시한 원인입니다.
Html.ValidationMessage() 도우미 메서드
Html.ValidationMessage() 도우미 메서드를 사용하여 특정 모델 속성과 연결된 ModelState 오류 메시지를 출력할 수 있습니다.
<%= Html.ValidationMessage("EventDate")%>
위의 코드 출력: <span class="field-validation-error"> 값 'BOGUS'가 잘못<되었습니다/span>
Html.ValidationMessage() 도우미 메서드는 개발자가 표시되는 오류 문자 메시지를 재정의할 수 있는 두 번째 매개 변수도 지원합니다.
<%= Html.ValidationMessage("EventDate","*") %>
위의 코드는 EventDate 속성에 오류가 있을 때 기본 오류 텍스트 대신 span class="field-validation-error">*</span>을 출력<합니다.
Html.ValidationSummary() 도우미 메서드
Html.ValidationSummary() 도우미 메서드를 사용하여 ModelState 컬렉션에 있는 모든 자세한 오류 메시지의 ul><li//><ul> 목록과 함께 <요약 오류 메시지를 렌더링할 수 있습니다.
Html.ValidationSummary() 도우미 메서드는 선택적 문자열 매개 변수를 사용합니다. 이 매개 변수는 자세한 오류 목록 위에 표시할 요약 오류 메시지를 정의합니다.
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
필요에 따라 CSS를 사용하여 오류 목록의 모양을 재정의할 수 있습니다.
AddRuleViolations 도우미 메서드 사용
초기 HTTP-POST 편집 구현에서는 catch 블록 내에서 foreach 문을 사용하여 Dinner 개체의 규칙 위반을 반복하고 컨트롤러의 ModelState 컬렉션에 추가했습니다.
catch {
foreach (var issue in dinner.GetRuleViolations()) {
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(dinner);
}
NerdDinner 프로젝트에 "ControllerHelpers" 클래스를 추가하여 이 코드를 좀 더 깔끔하게 만들고 ASP.NET MVC ModelStateDictionary 클래스에 도우미 메서드를 추가하는 "AddRuleViolations" 확장 메서드를 구현할 수 있습니다. 이 확장 메서드는 ModelStateDictionary를 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);
}
}
}
그런 다음 HTTP-POST 편집 작업 메서드를 업데이트하여 이 확장 메서드를 사용하여 ModelState 컬렉션을 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);
}
}
편집 구현의 좋은 점은 컨트롤러 클래스나 View 템플릿 모두 Dinner 모델에서 적용되는 특정 유효성 검사 또는 비즈니스 규칙에 대해 아무것도 알 필요가 없다는 것입니다. 나중에 모델에 규칙을 추가할 수 있으며, 지원되기 위해 컨트롤러 또는 뷰에 코드를 변경할 필요가 없습니다. 이렇게 하면 코드 변경을 최소화하면서 향후 애플리케이션 요구 사항을 쉽게 발전시킬 수 있는 유연성이 제공됩니다.
지원 만들기
DinnersController 클래스의 "편집" 동작 구현을 완료했습니다. 이제 사용자가 새 Dinners를 추가할 수 있도록 하는 "만들기" 지원을 구현해 보겠습니다.
HTTP-GET 만들기 작업 메서드
먼저 create action 메서드의 HTTP "GET" 동작을 구현합니다. 이 메서드는 누군가가 /Dinners/Create URL을 방문할 때 호출됩니다. 구현은 다음과 같습니다.
//
// GET: /Dinners/Create
public ActionResult Create() {
Dinner dinner = new Dinner() {
EventDate = DateTime.Now.AddDays(7)
};
return View(dinner);
}
위의 코드는 새 Dinner 개체를 만들고 EventDate 속성을 향후 1주일로 할당합니다. 그런 다음 새 Dinner 개체를 기반으로 하는 View를 렌더링합니다. View() 도우미 메서드에 이름을 명시적으로 전달하지 않았기 때문에 규칙 기반 기본 경로를 사용하여 보기 템플릿을 resolve. /Views/Dinners/Create.aspx입니다.
이제 이 보기 템플릿을 만들어 보겠습니다. 만들기 작업 메서드 내에서 마우스 오른쪽 단추를 클릭하고 "보기 추가" 상황에 맞는 메뉴 명령을 선택하여 이 작업을 수행할 수 있습니다. "보기 추가" 대화 상자 내에서 Dinner 개체를 뷰 템플릿에 전달하고 있음을 나타내고 "만들기" 템플릿을 자동으로 스캐폴드하도록 선택합니다.
"추가" 단추를 클릭하면 Visual Studio에서 새 스캐폴드 기반 "Create.aspx" 보기를 "\Views\Dinners" 디렉터리에 저장하고 IDE 내에서 엽니다.
우리를 위해 생성된 기본 "create" 스캐폴드 파일을 몇 가지 변경하고 아래와 같이 수정해 보겠습니다.
<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>
이제 애플리케이션을 실행하고 브라우저 내에서 "/Dinners/Create" URL에 액세스하면 만들기 작업 구현에서 아래와 같이 UI를 렌더링합니다.
HTTP-POST 만들기 작업 메서드 구현
Create action 메서드의 HTTP-GET 버전이 구현되었습니다. 사용자가 "저장" 단추를 클릭하면 /Dinners/Create URL에 양식 게시물을 수행하고 HTTP POST 동사를 사용하여 HTML <입력> 양식 값을 제출합니다.
이제 create action 메서드의 HTTP POST 동작을 구현해 보겠습니다. 먼저 HTTP POST 시나리오를 처리한다는 "AcceptVerbs" 특성이 있는 DinnersController에 오버로드된 "Create" 작업 메서드를 추가합니다.
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create() {
...
}
HTTP-POST 사용 "Create" 메서드 내에서 게시된 양식 매개 변수에 액세스할 수 있는 다양한 방법이 있습니다.
한 가지 방법은 새 Dinner 개체를 만든 다음 UpdateModel() 도우미 메서드(예: 편집 작업)를 사용하여 게시된 양식 값으로 채우는 것입니다. 그런 다음 DinnerRepository에 추가하고, 데이터베이스에 보관하고, 사용자를 세부 정보 작업으로 리디렉션하여 아래 코드를 사용하여 새로 만든 저녁 식사를 표시할 수 있습니다.
//
// 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);
}
}
또는 Create() 작업 메서드가 Dinner 개체를 메서드 매개 변수로 사용하는 방법을 사용할 수 있습니다. ASP.NET MVC는 새 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);
}
위의 작업 메서드는 ModelState.IsValid 속성을 확인하여 Dinner 개체가 양식 게시 값으로 성공적으로 채워졌는지 확인합니다. 입력 변환 문제(예: EventDate 속성에 대한 "BOGUS" 문자열)가 있고 문제가 있는 경우 작업 메서드가 양식을 다시 표시하면 false가 반환됩니다.
입력 값이 유효한 경우 작업 메서드는 새 Dinner를 DinnerRepository에 추가하고 저장하려고 시도합니다. 이 작업은 try/catch 블록 내에서 래핑하고 비즈니스 규칙 위반이 있는 경우 양식을 다시 표시합니다(dinnerRepository.Save() 메서드가 예외를 발생시키는 경우).
이 오류 처리 동작이 작동하는지 확인하려면 /Dinners/Create URL을 요청하고 새 Dinner에 대한 세부 정보를 입력할 수 있습니다. 잘못된 입력 또는 값으로 인해 아래와 같이 강조 표시된 오류로 인해 만들기 양식이 다시 표시됩니다.
Create 양식이 편집 양식과 정확히 동일한 유효성 검사 및 비즈니스 규칙을 적용하는 방법을 확인합니다. 이는 유효성 검사 및 비즈니스 규칙이 모델에 정의되어 있고 애플리케이션의 UI 또는 컨트롤러 내에 포함되지 않았기 때문입니다. 즉, 나중에 유효성 검사 또는 비즈니스 규칙을 한 곳에서 변경/발전시키고 애플리케이션 전체에 적용할 수 있습니다. 기존 규칙이나 수정 사항을 자동으로 적용하기 위해 편집 또는 만들기 작업 메서드 내에서 코드를 변경할 필요가 없습니다.
입력 값을 수정하고 "저장" 단추를 다시 클릭하면 DinnerRepository에 대한 추가가 성공하고 새 Dinner가 데이터베이스에 추가됩니다. 그러면 /Dinners/Details/[id] URL로 리디렉션됩니다. 여기서 새로 만든 저녁 식사에 대한 세부 정보가 표시됩니다.
지원 삭제
이제 DinnersController에 "삭제" 지원을 추가해 보겠습니다.
HTTP-GET 삭제 작업 메서드
먼저 삭제 작업 메서드의 HTTP GET 동작을 구현합니다. 이 메서드는 누군가가 /Dinners/Delete/[id] URL을 방문할 때 호출됩니다. 구현은 다음과 같습니다.
//
// 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);
}
작업 메서드는 삭제할 Dinner를 검색하려고 시도합니다. Dinner가 있는 경우 Dinner 개체를 기반으로 View를 렌더링합니다. 개체가 없거나 이미 삭제된 경우 "Details" 작업 메서드에 대해 이전에 만든 "NotFound" 보기 템플릿을 렌더링하는 View를 반환합니다.
삭제 작업 메서드 내에서 마우스 오른쪽 단추를 클릭하고 "보기 추가" 상황에 맞는 메뉴 명령을 선택하여 "삭제" 보기 템플릿을 만들 수 있습니다. "보기 추가" 대화 상자 내에서 Dinner 개체를 뷰 템플릿에 모델로 전달하고 빈 템플릿을 만들도록 선택합니다.
"추가" 단추를 클릭하면 Visual Studio에서 "\Views\Dinners" 디렉터리 내에 새 "Delete.aspx" 보기 템플릿 파일을 추가합니다. 아래와 같이 삭제 확인 화면을 구현하기 위해 템플릿에 몇 가지 HTML 및 코드를 추가합니다.
<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>
위의 코드는 삭제할 Dinner의 제목을 표시하고 최종 사용자가 "삭제" 단추를 클릭하면 POST를 수행하는 양식> 요소를 /Dinners/Delete/[id] URL에 출력<합니다.
애플리케이션을 실행하고 유효한 Dinner 개체에 대한 "/Dinners/Delete/[id]" URL에 액세스하면 아래와 같이 UI를 렌더링합니다.
측면 항목: POST를 수행하는 이유는 무엇인가요? |
---|
삭제 확인 화면 내에서 양식을> 만드는 작업을 진행한 <이유는 무엇인가요? 표준 하이퍼링크를 사용하여 실제 삭제 작업을 수행하는 작업 메서드에 연결하는 것은 어떨까요? 그 이유는 웹 크롤러 및 검색 엔진이 URL을 검색하고 링크를 따를 때 실수로 데이터가 삭제되는 것을 방지하려고 하기 때문입니다. HTTP-GET 기반 URL은 액세스/크롤링에 "안전한" 것으로 간주되며 HTTP-POST URL을 따르지 않아야 합니다. 좋은 규칙은 항상 HTTP-POST 요청 뒤에 파괴적인 또는 데이터 수정 작업을 배치하는 것입니다. |
HTTP-POST 삭제 작업 메서드 구현
이제 삭제 확인 화면을 표시하는 삭제 작업 메서드의 HTTP-GET 버전이 구현되었습니다. 최종 사용자가 "삭제" 단추를 클릭하면 /Dinners/Dinner/[id] URL에 양식 게시물을 수행합니다.
이제 아래 코드를 사용하여 삭제 작업 메서드의 HTTP "POST" 동작을 구현해 보겠습니다.
//
// 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");
}
Delete 작업 메서드의 HTTP-POST 버전은 삭제할 dinner 개체를 검색하려고 시도합니다. 찾을 수 없는 경우(이미 삭제되었기 때문에) "NotFound" 템플릿을 렌더링합니다. Dinner를 찾으면 DinnerRepository에서 삭제됩니다. 그런 다음, "삭제된" 템플릿을 렌더링합니다.
"삭제된" 템플릿을 구현하려면 작업 메서드를 마우스 오른쪽 단추로 클릭하고 "보기 추가" 상황에 맞는 메뉴를 선택합니다. 뷰의 이름을 "Deleted"로 지정하고 빈 템플릿으로 지정합니다(강력한 형식의 모델 개체를 사용하지 않음). 그런 다음, 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>
이제 애플리케이션을 실행하고 유효한 Dinner 개체에 대한 "/Dinners/Delete/[id]" URL에 액세스하면 아래와 같이 Dinner 삭제 확인 화면이 렌더링됩니다.
"삭제" 단추를 클릭하면 /Dinners/Delete/[id] URL에 대한 HTTP-POST가 수행됩니다. 그러면 데이터베이스에서 Dinner가 삭제되고 "삭제됨" 보기 템플릿이 표시됩니다.
모델 바인딩 보안
ASP.NET MVC의 기본 제공 모델 바인딩 기능을 사용하는 두 가지 방법에 대해 설명했습니다. 첫 번째는 UpdateModel() 메서드를 사용하여 기존 모델 개체의 속성을 업데이트하고, 두 번째 메서드는 ASP.NET MVC의 모델 개체 전달 지원을 작업 메서드 매개 변수로 사용합니다. 이러한 두 기술은 모두 매우 강력하고 매우 유용합니다.
이 힘은 또한 책임을 줍니다. 사용자 입력을 수락할 때 항상 보안에 대해 편집증이 되는 것이 중요하며, 개체를 입력에 바인딩할 때도 마찬가지입니다. HTML 및 JavaScript 삽입 공격을 방지하기 위해 항상 HTML에서 입력한 값을 HTML로 인코딩하고 SQL 삽입 공격을 주의해야 합니다(참고: 이러한 유형의 공격을 방지하기 위해 매개 변수를 자동으로 인코딩하는 애플리케이션에 LINQ to SQL 사용 중임). 클라이언트 쪽 유효성 검사에만 의존해서는 안 되며, 항상 서버 쪽 유효성 검사를 사용하여 가짜 값을 보내려는 해커를 방지해야 합니다.
ASP.NET MVC의 바인딩 기능을 사용할 때 고려해야 할 한 가지 추가 보안 항목은 바인딩하는 개체의 scope. 특히 바인딩할 수 있도록 허용하는 속성의 보안 영향을 이해하고 최종 사용자가 업데이트할 수 있어야 하는 속성만 허용해야 합니다.
기본적으로 UpdateModel() 메서드는 들어오는 양식 매개 변수 값과 일치하는 모델 개체의 모든 속성을 업데이트하려고 시도합니다. 마찬가지로 작업 메서드 매개 변수로 전달된 개체는 기본적으로 양식 매개 변수를 통해 모든 속성을 설정할 수 있습니다.
사용량별로 바인딩 잠금
업데이트할 수 있는 속성의 명시적 "포함 목록"을 제공하여 사용량별로 바인딩 정책을 잠글 수 있습니다. 이 작업은 아래와 같이 추가 문자열 배열 매개 변수를 UpdateModel() 메서드에 전달하여 수행할 수 있습니다.
string[] allowedProperties = new[]{ "Title","Description",
"ContactPhone", "Address",
"EventDate", "Latitude",
"Longitude"};
UpdateModel(dinner, allowedProperties);
작업 메서드 매개 변수로 전달된 개체는 허용되는 속성의 "포함 목록"을 아래와 같이 지정할 수 있는 [Bind] 특성도 지원합니다.
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create( [Bind(Include="Title,Address")] Dinner dinner ) {
...
}
형식별로 바인딩 잠금
형식별로 바인딩 규칙을 잠글 수도 있습니다. 이렇게 하면 바인딩 규칙을 한 번 지정한 다음 모든 컨트롤러 및 작업 메서드에서 모든 시나리오(UpdateModel 및 작업 메서드 매개 변수 시나리오 포함)에 적용할 수 있습니다.
형식에 [Bind] 특성을 추가하거나 애플리케이션의 Global.asax 파일 내에 등록하여 형식별 바인딩 규칙을 사용자 지정할 수 있습니다(형식을 소유하지 않는 시나리오에 유용). 그런 다음 Bind 특성의 Include 및 Exclude 속성을 사용하여 특정 클래스 또는 인터페이스에 바인딩할 수 있는 속성을 제어할 수 있습니다.
NerdDinner 애플리케이션의 Dinner 클래스에 이 기술을 사용하고 바인딩 가능한 속성 목록을 다음으로 제한하는 [Bind] 특성을 추가합니다.
[Bind(Include="Title,Description,EventDate,Address,Country,ContactPhone,Latitude,Longitude")]
public partial class Dinner {
...
}
RSVP 컬렉션을 바인딩을 통해 조작하는 것을 허용하지 않으며, 바인딩을 통해 DinnerID 또는 HostedBy 속성을 설정하도록 허용하지 않습니다. 보안상의 이유로 작업 메서드 내에서 명시적 코드를 사용하여 이러한 특정 속성만 조작합니다.
CRUD Wrap-Up
ASP.NET MVC에는 양식 게시 시나리오를 구현하는 데 도움이 되는 다양한 기본 제공 기능이 포함되어 있습니다. 이러한 다양한 기능을 사용하여 DinnerRepository를 기반으로 CRUD UI 지원을 제공했습니다.
모델 중심 접근 방식을 사용하여 애플리케이션을 구현하고 있습니다. 즉, 모든 유효성 검사 및 비즈니스 규칙 논리는 컨트롤러 또는 뷰가 아닌 모델 계층 내에서 정의됩니다. Controller 클래스와 View 템플릿은 Dinner 모델 클래스에서 적용되는 특정 비즈니스 규칙에 대해 아무것도 알지 못합니다.
이렇게 하면 애플리케이션 아키텍처를 클린 유지하고 더 쉽게 테스트할 수 있습니다. 나중에 모델 계층에 비즈니스 규칙을 추가할 수 있으며, 지원되기 위해 컨트롤러 또는 뷰에 코드를 변경할 필요가 없습니다 . 이를 통해 향후 애플리케이션을 발전시키고 변경할 수 있는 많은 민첩성을 제공할 것입니다.
이제 DinnersController를 사용하면 저녁 식사 목록/세부 정보뿐만 아니라 지원을 만들고 편집하고 삭제할 수 있습니다. 클래스의 전체 코드는 아래에서 찾을 수 있습니다.
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");
}
}
다음 단계
이제 DinnersController 클래스 내에서 기본 CRUD(만들기, 읽기, 업데이트 및 삭제) 지원 구현이 있습니다.
이제 ViewData 및 ViewModel 클래스를 사용하여 양식에서 더 풍부한 UI를 사용하도록 설정하는 방법을 살펴보겠습니다.