2009-11-05 26 views
16

Zaimplementowałem obsługę błędów w witrynie ASP.NET MVC in a way like suggests this post.Przyjazny dla użytkownika ASP.NET MVC błąd 401

Przy 404 błędach wszystko działa dobrze. Ale jak poprawnie wyświetlać przyjazny dla użytkownika ekran dla 401 error? Zwykle nie rzucają wyjątku, który można obsłużyć wewnątrz Application_Error(), ale raczej akcja zwraca HttpUnauthorizedResult. Jednym z możliwych sposobów jest dodanie następującego kodu do końca Application_EndRequest() metody

if (Context.Response.StatusCode == 401) 
{ 
    throw new HttpException(401, "You are not authorised"); 
    // or UserFriendlyErrorRedirect(new HttpException(401, "You are not authorised")), witout exception 
} 

Ale wewnątrz Application_EndRequest() Context.Session == null, errorController.Execute() nie dlatego, że nie można użyć domyślnego TempDataProvider.

// Call target Controller and pass the routeData. 
    IController errorController = new ErrorController(); 
    errorController.Execute(new RequestContext( 
     new HttpContextWrapper(Context), routeData)); // Additional information: The SessionStateTempDataProvider requires SessionState to be enabled. 

Czy można zaproponować najlepsze praktyki dotyczące "obsługi przyjaznej dla użytkownika" 401 w aplikacji ASP.NET MVC?

Dzięki.

+0

http://stackoverflow.com/questions/2580596/how-do-you-handle-ajax-requests -when-user-is-not-authenticated/2595436 # 2595436 – Cherian

Odpowiedz

14

Sprawdź atrybut HandleErrorAttribute. Dodaj do niego podklasę lub dodaj własną implementację, która obsłuży wszystkie żądane kody statusu. Możesz spowodować, że zwróci osobny widok błędu dla każdego typu błędu.

Oto pomysł, jak utworzyć filtr wyjątków błędów obsługi. Wyrzuciłem większość rzeczy, aby skupić się tylko na naszych podstawowych rzeczach. Całkowicie spójrz na oryginalną implementację, aby dodać sprawdzenia argumentów i inne ważne rzeczy.

public class HandleManyErrorsAttribute : FilterAttribute, IExceptionFilter 
{ 
    public virtual void OnException(ExceptionContext filterContext) 
    { 
     if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) 
      return; 

     Exception exception = filterContext.Exception; 

     string viewName = string.Empty; 
     object viewModel = null; 
     int httpCode = new HttpException(null, exception).GetHttpCode(); 
     if (httpCode == 500) 
     { 
      viewName = "Error500View"; 
      viewModel = new Error500Model(); 
     } 
     else if (httpCode == 404) 
     { 
      viewName = "Error404View"; 
      viewModel = new Error404Model(); 
     } 
     else if (httpCode == 401) 
     { 
      viewName = "Error401View"; 
      viewModel = new Error401Model(); 
     } 

     string controllerName = (string)filterContext.RouteData.Values["controller"]; 
     string actionName = (string)filterContext.RouteData.Values["action"]; 
     filterContext.Result = new ViewResult 
     { 
      ViewName = viewName, 
      MasterName = Master, 
      ViewData = viewModel, 
      TempData = filterContext.Controller.TempData 
     }; 
     filterContext.ExceptionHandled = true; 
     filterContext.HttpContext.Response.Clear(); 
     filterContext.HttpContext.Response.StatusCode = httpCode; 

     filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; 
    } 
} 

Wtedy „ozdobić” swoje działania kontrolera z tego atrybutu:

[HandleManyErrors] 
public ActionResult DoSomethingBuggy() 
{ 
    // ... 
} 
+0

Rozumiem, że to rozwiązanie wymaga udekorowania atrybutem HandleManyErrors dla każdego kontrolera lub potencjalnie bezpiecznego działania (działanie, które może prowadzić do 401). Ale byłoby miło mieć globalną obsługę 401 (na przykład w Global.asax), albo nie mam racji i to jest zły pomysł? – Roman

+0

W modelu MVC nie zawsze wiadomo z góry, czy trasa jest w porządku, czy nie. Trasa może rozwiązać, aby osiągnąć działanie kontrolera, ale po sprawdzeniu dostarczonego parametru z bazą danych możesz zdecydować, że trasa z tym parametrem nie może pobrać wymaganego zasobu, więc zwrócisz błąd 4xx. Dzięki temu dynamicznemu zachowaniu łatwiej jest sprawdzić rzeczy i rzucić wyjątek HttpException ze swoich działań kontrolera wiedząc, że będą obsługiwane przez atrybut, aby zwrócić stronę błędu. –

+0

Dla zachowania statycznego można dodać ostatnią trasę "catchall", aby dopasować wszystkie trasy, które nie zostały przechwycone przez wcześniej zdefiniowane reguły. Slogan wywoła działanie kontrolera, które po prostu wyrzuci wyjątek HttpException. W ten sposób masz logikę obsługi błędów statycznych w pliku Global.asax, której towarzyszy dynamiczna obsługa błędów bezpośrednio w odpowiednich akcjach kontrolera. –

0

Jeśli używasz ASP.NET MVC, jesteś bardziej niż prawdopodobne, aby korzystać z usług IIS, więc dlaczego don” t po prostu skonfigurować IIS, aby użyć niestandardowej strony błędu 401 dla tej aplikacji sieciowej/katalogu wirtualnego?

+0

Potrzebuję innego zachowania dla żądań pełnego żądania zwrotnego i żądania AJAX oraz większej kontroli nad przetwarzaniem błędów. – Roman

+3

Ponieważ możesz chcieć dokładniejszego kontrolowania ziarna dokładnie w jaki sposób prezentowany jest 404 (HTML i HTTP). Możesz także chcieć innych informacji na stronie 404, takich jak treści dynamiczne, które będą działać tylko w aplikacji ur. możesz logować określone wiadomości i śledzić ścieżki 404 za pomocą mechanizmu logowania aplikacji - wszystko to musi być wykonane wewnątrz aplikacji. –

+0

i Asp Net MVC 3 również działają na Apache2. (z Mono) – JCasso

6

Udało mi się rozwiązać to w bardzo prosty sposób. Chciałem pokazać niestandardową stronę dla zalogowanych użytkowników ("nie masz pozwolenia, bla bla ...") i przekierować nieuwierzytelnionych użytkowników na stronę logowania (domyślne zachowanie). Więc wprowadziła zwyczaj AuthorizeAttribute (słownie CustomAuthorizeAttribute) z zastosowaniem metody HandleUnauthorizedRequest nadpisane w taki sposób, że jeśli użytkownik jest uwierzytelniony ustawić właściwość wyniku argumentu filterContext z ViewResult (w udostępnionym folderze) o nazwie AccessDenied.aspx

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
{ 
    if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 
    } 
    else 
    { 
     filterContext.Result = new ViewResult { ViewName = "AccessDenied" }; 
    } 
} 

Następnie należy użyć tego nowego atrybutu. Pozdrawiam.

+0

Dla każdego, kto się zastanawia, ten atrybut jest wymieniony tylko w MSDNs Framework 4.0. Nie jest dostępny w wersji 3.5 – Gregory

+1

Może nie działać z uwierzytelnianiem "Windows". Próbowałem i niestety nie udało się .. –

-1

W jednym z moich projektów używam kodu z uvita.

mam ASP.NET MVC2 i używam uwierzytelnianie usługi Active Directory w bez strony logowania. Mam stronę NoAuth.aspx, która korzysta ze strony wzorcowej witryny, integracja układu aplikacji WWW.

To jest plik web.config.

<system.web> 
    <authentication mode="Windows" /> 
    <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider"> 
     <providers> 
      <clear /> 
      <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" 
      applicationName="/" /> 
     </providers> 
    </roleManager> 
</system.web> 

Nowa klasa CustomAutorizeAttribute

using System.Web.Mvc; 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      base.HandleUnauthorizedRequest(filterContext); 
     } 
     else 
     { 
      filterContext.Result = new ViewResult { ViewName = "NoAuth"}; 
     } 
    } 
} 

a kontroler

[CustomAuthorize(Roles = "ADRole")] 
public class HomeController : Controller 
{ 
    public HomeController() 
    { 
    } 
} 
Powiązane problemy