2010-02-22 14 views
10

Kilka moich działań kontrolera ma standardowy zestaw zachowania związanego z niepowodzeniem. Ogólnie rzecz biorąc, chcę:Tworzenie właściwości ViewResults poza kontrolerami w ASP.NET MVC

  • obciążenia obiekt na podstawie danych trasy (IDS i podobne)
    • Jeśli dane trasy nie wskazuje ważnego obiektu (np: poprzez URL hacking) następnie poinformować użytkownika o problemie i powrotu HTTP 404 Not Found
  • potwierdzić, że bieżący użytkownik ma odpowiednie uprawnienia do obiektu
    • Jeśli użytkownik nie ma uprawnień, Poinformuj użytkownika o problemie i zwróć komunikat HTTP 403 Forbidden
  • Jeśli powyższe powiodło się, zrób coś z tym obiektem, który jest specyficzny dla akcji (np. renderuj go w widoku).

Kroki te są tak wystandaryzowane, że chcę mieć kod wielokrotnego użytku, aby zastosować to zachowanie.

Mój obecny plan ataku było posiadanie metody pomocnika do zrobienia czegoś takiego:

public static ActionResult HandleMyObject(this Controller controller, 
    Func<MyObject,ActionResult> onSuccess) { 
    var myObject = MyObject.LoadFrom(controller.RouteData). 
    if (myObject == null) return NotFound(controller); 
    if (myObject.IsNotAllowed(controller.User)) return NotAllowed(controller); 
    return onSuccess(myObject); 
} 

# NotAllowed() is pretty much the same as this 
public static NotFound(Controller controller){ 
    controller.HttpContext.Response.StatusCode = 404 
    # NotFound.aspx is a shared view. 
    ViewResult result = controller.View("NotFound"); 
    return result; 
} 

Problem polega na tym, że Controller.View() jest chroniony sposób i tak nie jest dostępne od pomocnika . Spojrzałem na tworzenie nowej instancji ViewResult jawnie, ale jest wystarczająco dużo właściwości, aby ustawić, że jestem ostrożny o tym, bez uprzedniego poznania pułapek.

Jaki jest najlepszy sposób utworzenia widoku ViewResult spoza określonego kontrolera?

Odpowiedz

4

Kiedy to pisałem, myślałem o jednym.

Zamiast powyższego kodu w pomocniku, mógłbym umieścić go w podklasie kontrolera, a następnie podklasy tej klasy dla moich rzeczywistych kontrolerów. To pozwoli mi zadzwonić do chronionej metody View().

Nie podoba mi się to szczególnie ponieważ jest to requires inheritance to work, ale nadal jest to opcja.

+0

+1: Tak bym to zrobił. –

+0

Nie podoba mi się to, że kontroler może mieć tylko jeden zestaw wspólnych działań: to, co on (pojedynczo) dziedziczy. W tym konkretnym przypadku nie jest to problemem, ale widzę, że staje się on coraz bardziej nieporęczny w miarę rozwoju projektu. –

+0

Rozumiem, że twoje pytanie ma lata, ale pomyślałem, że wspomnę o innej możliwości. Możesz udostępnić publiczną wersję 'View()' nazwaną 'GetView()' na twoim podstawowym kontrolerze, wtedy możesz trzymać swojego pomocnika w oddzielnej klasie. Nadal będziesz musiał przekazać kontroler do swojego pomocnika, co jest nieco niezręczne, ale pozwala na kompozycję zamiast dziedziczenia. – devuxer

0

Innym sposobem będzie za pomocą dekoratorów:

public class StatusCodeViewResultDecorator : ViewResult { 
    ViewResult wrapped; 
    int code; 
    string description; 

    public StatusCodeViewResultDecorator(ViewResult wrapped, int code, string description) { 
    this.wrapped = wrapped; 
    this.code = code; 
    this.description = description; 
    } 

    public override void ExecuteResult(ControllerContext context) { 
    wrapped.ExecuteResult(context); 
    context.RequestContext.HttpContext.Response.StatusCode = code; 
    context.RequestContext.HttpContext.Response.StatusDescription = description; 
    } 
} 

a może metodę rozszerzenia, aby to czystsze:

public static class ViewResultExtensions { 
    public static ViewResult WithStatus(this ViewResult viewResult, int code, string description) { 
    return new StatusCodeViewResultDecorator(viewResult,code,description); 
    } 
} 

Mogłeś po prostu powiedzieć:

return View("MyView").WithStatus(404,"Not found"); 

w swojej kontroler.

+1

To naprawdę nie pomaga, ponieważ nadal musisz utworzyć bazę ViewResult w kontrolerze, co jest tym, czego próbuję uciec. –

2

Miałem to samo pytanie i odebrałem to inaczej. Naprawdę nie chciałem korzystać z dziedziczenia, więc zamiast tego użyłem lambda.

Po pierwsze, mam obiekt, który mijam z mojego kontrolera do metody chcę wrócić na stanowisku:

public struct MyControllerContext 
{ 
    public HttpRequestBase Request { get; set; } 
    public HttpResponseBase Response { get; set; } 
    public DocsController Controller { get; set; } 

    public Func<string, object, ViewResult> ViewResult; 
    public ViewResult View(string viewName, object model) 
    { 
     return this.ViewResult(viewName, model); 
    } 
} 

I utworzyć wystąpienie tego i przekazać go jako parametr do metody, która będzie zwraca wynik:

// In the controller 
var context = new DocsControllerContext() 
{ 
    Request = Request, 
    Response = Response, 
    Controller = this, 
    ViewResult = (viewName, model) => 
    { 
     return View(viewName, model); 
    } 
}; 

var returnValue = methodInfo.Invoke(toInvoke, new object[] { context }); 
return returnValue; 

Następnie w metodzie I powołuje, mogę zadzwonić context.View("ViewName", model);. Może być wiele odmian tego, podstawową ideą jest użycie wywołania zwrotnego.

6

Po prostu przeczytaj ten post, ponieważ miałem ten sam problem z filtru działania. Moim rozwiązaniem było jawne utworzenie akcji widoku. Jest to oparte na chronionej metodzie View() zgodnie ze źródłem MVC, więc powinno wypełnić wymagane właściwości. W każdym razie wydaje się działać bez problemów.

public static NotFound(Controller controller){ 
    controller.HttpContext.Response.StatusCode = 404; 

    ViewResult result = new ViewResult { 
      ViewName = "NotFound", 
      ViewData = controller.ViewData, 
      TempData = controller.TempData 
     }; 
    return result; 
} 

Trochę za późno, ale to zadziałało.

Powiązane problemy