Wykorzystanie ActionSelectionAttribute w ASP.NET MVC

23 listopada 2008, 13:46

KomentarzeKomentarze: 2 KategorieKategorie: asp.netmvc

Budując aplikację przy użyciu ASP.NET MVC możemy trafić na następujący problem. Otóż standardowo jedna forma na stronie skojarzona jest z konkretną akcją kontrolera. Na przykład:

<form action="User/Save" />

Często zdarza się jednak, że na takiej formie mamy więcej niż jeden input typu submit. Wciśnięcie któregoś z nich spowoduje wysłanie danych formy zawsze do tej samej metody "Save" zdefiniowanej w kontrolerze "User".

W metodzie "Save" możemy w sumie sprawdzić z jakiego submita wyszła akcja i stosownym if'em zdecydować co ma się dziać dalej. Na szczęście możemy sobie nieco zautomatyzować korzystając z filtru typu "ActionSelectionAttribute". Poniżej prezentuję kod atrybutu "AcceptInputAttribute":

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Reflection;
   4:  using System.Web.Mvc;
   5:   
   6:  namespace Softio.Web.MVC.Filters
   7:  {
   8:      [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
   9:      public class AcceptInputAttribute : ActionSelectionAttribute
  10:      {
  11:   
  12:          public AcceptInputAttribute()
  13:          {
  14:              Names = new List<string>();
  15:          }
  16:   
  17:          public AcceptInputAttribute(string name)
  18:              : this()
  19:          {
  20:              Names.Add(name);
  21:          }
  22:   
  23:          public AcceptInputAttribute(params string[] name)
  24:              : this()
  25:          {
  26:              Names.AddRange(name);
  27:          }
  28:   
  29:          public List<string> Names { get; private set; }
  30:   
  31:          public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
  32:          {
  33:              if (controllerContext.HttpContext.Request.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
  34:              {
  35:                  foreach (string n in Names)
  36:                  {
  37:                      if (!controllerContext.HttpContext.Request.Form[n].IsNullOrEmpty())
  38:                      {
  39:                          return true;
  40:                      }
  41:                  }
  42:              }
  43:              return false;
  44:          }
  45:      }
  46:  }

Atrybut ten nadany metodzie "Save" zbada, czy został przesłany zdefiniowany element formy.

   1:      [AcceptInput("Save", "SaveAndStay")]
   2:      public ActionResult Save(
   3:          int id,
   4:          string saveandstay)
   5:      {
   6:          // Zapisz dane
   7:   
   8:          if (saveandstay != null)
   9:          {
  10:              return Edit(id);
  11:          }
  12:          return RedirectToAction("Index");
  13:      }
  14:   
  15:      [AcceptInput("DeletePermanently")]
  16:      public ActionResult Save(int id)
  17:      {
  18:          // Usuń z bazy
  19:          return RedirectToAction("Index");
  20:      }

W powyższym kodzie mamy dwie metody Save, gdzie jedna z nich zapisuje dane użytkownika do bazy i w zależności od istnienia "SaveAndStay" wychodzi do listy lub zostaje w edycji. Druga metoda ma za zadanie usunięcie użytkownika z bazy.

Do zadziałania potrzebne jest nadanie atrybutu "name" dla "submitów". Odpowiednio dla powyższego przykładu:

<input type="submit" name="Save" value="Zapisz" />
<input type="submit" name="SaveAndStay" value="Zapisz i zostań" />
<input type="submit" name="DeletePermanently" value="Usuń" />

W ten oto prosty sposób uzyskaliśmy elastyczność i zarazem estetyczność kodu.

Słyszałem i czytałem, że problemy takie ludzie rozwiązują za pomocą JavaScryptu i podmieniają atrybut "action". A Wy jak to robicie w swoich aplikacjach z wykorzystaniem MVC? :)

 

Errata:

W wersji Beta atrybut ten został przemianowany na ActionMethodSelectorAttribute.

 

Ten artykuł jest widoczny także na: ZINE.NET

Jeśli spodobał Ci się ten artykuł, to podziel się tą informacją z innymi.

Gravatar
Roman Jendrusz
2009-03-16 19:49

Fajne rozwiązanie.

Moja propozycja to użycie zapytań Http typu Delete w celu obsługi akcji Usuń. Wówczas można wyeliminować dodatkowy przez ciebie stworzony atrybut, dlatego, że problem rozwiąże wbudowany atrybut AcceptVerbsAttribute. Jedynym minusem tego rozwiązani jest wymóg korzystania z javascriptu, co nie zawsze jest możliwe.

Więcej na ten temat można znaleźć na blogu stephen'a walther'a http://stephenwalther.com/blog/archive/2009/01/21/asp.net-mvc-tip-46-ndash-donrsquot-use-delete-links-because.aspx

Gravatar
dario-g
2009-03-16 20:58

Znam tip z tego bloga, ale może nie wystarczyć 'verbów' do wszystkich zastosowań ;)

Dodaj komentarz Dodaj komentarz

Twój email (niepublikowany/opcjonalnie):

Twoja strona (opcjonalnie):

Imię i nazwisko/nick (wymagane):

Treść (wymagane):

Chcę być poinformowany o kolejnych komentarzach:

To pole zostaw puste

Uwaga! Zastrzegam sobie prawo do usuwania obraźliwych i wulgarnych komentarzy.