2012-03-02 1 views
7

Chcę móc zaznaczyć akcję na kontrolerze, która będzie wywoływana zarówno z wywołań ajaxowych, jak i za pośrednictwem funkcji RenderAction. Problem polega na tym, że oba te atrybuty wyprowadzają lub implementują różne abstrakcje. Jedno wyjście jest następujące:Łączenie AjaxOnlyAttribute i ChildActionOnlyAttribute w jeden filtr akcji

[AjaxOnly] 
PartialViewResult GetViewAjax(int foo) { return GetView(foo); } 
[ChildActionOnly] 
PartialViewResult GetView(int foo) { ... } 

Ale to wcale nie jest miłe.


AjaxOnly przypisują mówię to:

public sealed class AjaxOnlyAttribute : ActionFilterAttribute 
{ 
    #region Public members 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (filterContext == null) 
      throw new ArgumentNullException("filterContext"); 
     if (filterContext.HttpContext.Request.Headers["X-Requested-With"] != "XMLHttpRequest") 
      filterContext.Result = new HttpNotFoundResult(); 
    } 

    #endregion 
} 

Metoda ta jest pobierana z kontraktami MVC3. Ważnym komentarz dlaczego warunek nie jest filterContext.HttpContext.Request.IsAjaxRequest() została wykonana przez zespół dev i mówi co następuje:

// Dev10 #939671 - If this attribute is going to say AJAX *only*, then we need to check the header 
// specifically, as otherwise clients can modify the form or query string to contain the name/value 
// pair we're looking for. 

Odpowiedz

16

To nie ma żadnego sensu. Te 2 atrybuty wzajemnie się wykluczają. Jeśli działanie zostanie oznaczone jako [ChildActionOnly], klient nigdy nie będzie mógł uzyskać do niego bezpośredniego dostępu za pomocą żądania HTTP (synchronicznego lub asynchronicznego). Jeśli chcesz, aby akcja była kiedykolwiek dostępna za pomocą AJAX, nie powinieneś dekorować jej atrybutem [ChildActionOnly].

Nie wiem, co to jest atrybut [AjaxOnly] i skąd pochodzi, ale w zależności od tego, w jaki sposób jest zaimplementowany, konieczne może być jego zmodyfikowanie w celu umożliwienia żądania akcji potomnych, jeśli opiera się tylko na metodzie Request.IsAjaxRequest(). Na przykład, jeśli jest coś takiego:

public class AjaxOnlyAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (!filterContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      filterContext.Result = new HttpNotFoundResult(); 
     } 
    } 
} 

warto dostosować go tak:

public class AjaxOrChildActionOnlyAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (!filterContext.HttpContext.Request.IsAjaxRequest() && 
      !filterContext.IsChildAction 
     ) 
     { 
      filterContext.Result = new HttpNotFoundResult(); 
     } 
    } 
} 
+0

Dlaczego? Załóżmy, że twoja akcja jest ponownie używana z różnych miejsc, zarówno przez żądania RenderAction(), jak i ajax. Załóżmy, że są one ponownie używane na różnych stronach jako coś, co powoduje rysowanie standardowego panelu narzędzi w wielu miejscach i jest używane nieco inaczej w różnych kontekstach. –

+0

@Hohhi, więc chcesz, aby Twoje działanie było dostępne tylko przez wywołanie AJAX lub działanie jako dziecko ? –

+0

Dokładnie, dziękuję za twoją sugestię, zaktualizowałbym pytanie i powiedziałbym, jak daleko zajdę w pewnym czasie –

1

Inspirowane na odpowiedź Darin i kodu źródłowego ChildActionOnlyAttribute „s, jest to rozwiązanie Przyszedłem z, co myślę, że jest odrobinę lepiej:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
public class AjaxOrChildAttribute : ActionMethodSelectorAttribute 
{ 
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo) 
    { 
     return controllerContext.IsChildAction || controllerContext.RequestContext.HttpContext.Request.IsAjaxRequest(); 
    } 
} 

ten sposób walidacja odbywa się zanim jeszcze próbuje wykonać, a błąd pojawi się po wpisaniu w adresie URL jest dokładna Sam e jako próbujący każdy niepoprawny adres URL.