Good morning guys. I'm having the following problem. I created an override method to validate referer:
public class VerifyOutUrlReferer : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext != null)
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Validate"},
{"action", "Referer"},
{"code", 123ER}
});
}
}
}
[VerifyOutUrlReferer]
public ActionResult GeneralError(string errorCode)
{
return View();
}
and add it on top of the method...
I try to call this method by screen controller that validates 404 error, so that does not always redirects to her because the referer is empty. Could someone help me how I can call this screen by controllers.
Related
How can I redirect logged in users (in every page) to a view asking them to complete their info?
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (userIsLoggedIn)
{
filterContext.Result = new RedirectResult(userInfoView);
}
base.OnActionExecuting(filterContext);
}
You can't redirect to a view, you need to redirect to an action method and that action method would return the view:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (userIsLoggedIn)
{
filterContext.Result = new RedirectToAction("UserInfoActionName", "UserInfoControllerName");
}
base.OnActionExecuting(filterContext);
}
This blog explains exactly the same problem.
RedirectToAction is avaible in MVC Core. I archived this as follows in MVC 4:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!condition)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary{ { "controller", "YourController" },
{ "action", "YourAction" }
});
}
}
This will redirect you to the given action then to the relevant view.
I've a custom AuthorizeAttribute in my website. It has some logic about the Result created for unathorized requests.
In some cases, I want to trigger its validation manually*. I don't know if its possible. As I haven't found how to do that, I thought that I could extract the logic to get the Result to a diferrent method, and call it when I want. But then I don't know how to execute the ActionResult (outside de controllers).
How can I do to manually execute authorize validation? If not possible, how can I do to execute an ActionResult outside a controller?
*I need to trigger it manually because some request may pass the validation (because the session is created) and then, when accessing my services, found that the session was closed by someone else. I wouldn't like to add a call to the services in OnAuthorization to reduce services calls.
I'm not sure if its the best, but I've found a way to get it working (still listening for better answers).
When I call the services and notice that the work session has expired, all I do is removing the active user in the web session.
My custom authorize attribute also implements IResultFilter and IExceptionFilter.
In both OnResultExecuted and OnException I validate the active user once more. If the session was removed, then apply the same ActionResult that I would apply in OnAuthorization.
Here is the final class:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IResultFilter, IExceptionFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnException(ExceptionContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
{
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
}
public static ActionResult Validate(HttpContextBase httpContext)
{
if (UserActiveInSession)
return null;
// Different rules to build an ActionResult for this specific case.
}
}
I did not get Diego answer's, But Just simply answering the title, I got it to work like that, You can use it as attribute on controllers actions and also trigger it manually at any place in C# or in Razor views.
namespace SomeNameSpace
{
public class CustomAuthorizeAttributeMVC : AuthorizeAttribute
{
private readonly string[] rolesParams;
public CustomAuthorizeAttributeMVC(params string[] roles)
{
this.rolesParams = roles;
}
public bool IsAuthorized { get {
//Do your authorization logic here and return true if the current user has permission/role for the passed "rolesParams"
string[] allowedRoles = new string[] {"role 1", "role 2", "role 3"};
return allowedRoles.Intersect(rolesParams).Any(); //for the example
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return this.IsAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//...
}
}
public class AuthorizeHelper
{
public static bool HasPermission(params string[] roles)
{
return new CustomAuthorizeAttributeMVC(roles).IsAuthorized;
}
}
}
Usage example:
[CustomAuthorizeAttributeMVC("role 2")]
public ActionResult SomeAction()
{
return Content("Authorized !");
}
public ActionResult SomeOtherAction()
{
if(AuthorizeHelper.HasPermission("role 2"))
{
return Content("Authorized !");
}
return Content("401 Not Authorized !");
}
And as said, it can be used in Razor views by calling it normally
#if(AuthorizeHelper.HasPermission("role 2")) {
//...
}
Thanks
After writing code in global.asax this error ocured. If i keep a break point to check it was firing and spinning and eventually browser outcomes with the above responce["Your application has redirected loops"].
`
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check sessions here
if (HttpContext.Current.Session["username"] == null)
{
filterContext.Result = new RedirectResult("~/Account/Login");
return;
}
base.OnActionExecuting(filterContext);
}
}`
Really funny but why this stupid error occuring again and again.Any Idea?
Remove this ActionFilter from AccountController
When you redirect to "~/Account/Login" the code of OnActionExecuting will be called again so HttpContext.Current.Session["username"] == null is true and you have your redirect loop.
So, you need other condition for check this
I'm sorry, i didn't read your question properly
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check sessions here
if (HttpContext.Current.Session["username"] == null)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary {
{ "Controller", "Accounts" },
{ "Action", "Login" }
});
}
base.OnActionExecuting(filterContext);
}
}
Hope this works.
I've got this code.
public ActionResult Index()
{
ReceiptModel model = new ReceiptModel();
try
{
model = new ReceiptModel(context);
}
catch (BussinessException bex)
{
ModelState.AddModelError("Index", bex.MessageToDisplay);
return View("Index");
}
return View(model);
}
BussinesException ir returned from database and then displayed for user. I have to put on every controller method try-catch statement, which is a bit tedious. Is there any easier way how to handle these exceptions?
P.S. All other exceptions are handled with HandleExceptionAttribute
UPDATE:
I used Floradu88 approach. So Now i have something like this.
public sealed class HandleBussinessExceptionAttribute : HandleErrorAttribute, IExceptionFilter
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.Controller.TempData["UnhandledException"] = filterContext.Exception;
filterContext.ExceptionHandled = true;
((Controller)filterContext.Controller).ModelState.AddModelError(
((BussinessException)filterContext.Exception).Code.ToString(),
((BussinessException)filterContext.Exception).MessageToDisplay
);
filterContext.Result = new ViewResult
{
ViewName = this.View,
TempData = filterContext.Controller.TempData,
ViewData = filterContext.Controller.ViewData,
};
}
}
and on Controller action i put
[HandleBussinessExceptionAttribute(Order = 2, ExceptionType = typeof(BussinessException), View = "Login")]
i also tried in exception handler:
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(filterContext.RouteData));
and then handle error in action with ModelState.IsValid but values to action comes null. So for now i use first approach. When i have a bit more time i'll try to fix second approach.
Please read the documentation on this part:
http://msdn.microsoft.com/en-us/library/gg416513%28v=vs.98%29.aspx
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
Too much content to be posted here:
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
And your controller:
public class ProductsController : ApiController
{
[NotImplExceptionFilter]
public Contact GetContact(int id)
{
throw new NotImplementedException("This method is not implemented");
}
}
According to this post, Create a custom binder and put the model in TempData:
public class AppBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
controllerContext.Controller.TempData["model"] = bindingContext.Model;
}
}
Register it in global.asax:
ModelBinders.Binders.DefaultBinder = new AppBinder();
Create a BaseController and override the OnException:
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
this.ModelState.AddModelError(string.Empty, filterContext.Exception.Message);
filterContext.Result = new ViewResult
{
ViewName = filterContext.RouteData.Values["action"].ToString(),
TempData = filterContext.Controller.TempData,
ViewData = filterContext.Controller.ViewData
};
base.OnException(filterContext);
}
All Actions int The Controllers which inherited from this base controller will show their unhandled exceptions their own view in validation summery (remember to have
#Html.ValidationSummary(true)
in page). it works for me, hope works for you too!
ASP.NET MVC 2.0
I'm doing Post-Redirect-Get, if I get errors on post, I need to include ModelErrors along for the ride to along -Redirect-Get route.
I send it through 'TempData':
TempData["modelErors"] =
ModelState.
Where(item => item.Value.Errors.Count > 0).
ToDictionary(
item => item.Key,
item => item.Value.Errors.Select(error=>error.ErrorMessage).ToList()
);
And then reinsert it into a ModelState:
if (TempData.ContainsKey("modelErors")) {
foreach (var errors in (IDictionary<string,IList<string>>) TempData["modelErors"]) {
foreach (var error in errors.Value) {
ModelState.AddModelError(errors.Key, error);
}
}
}
Is there a better way?
You should really only PRG after a successful post. Otherwise it's fine to return from the post if there's an error.
Otherwise you need to use cookies, session or request variables to store that information for the next request.
In ASP.NET MVC2 by default I think TempData uses Session state to store the information for the next request.
I think that the most cleaner solution was to use ActionFilterAttribute like this :
public class RedirectErrorAttribute : ActionFilterAttribute
{
#region Methods & Function
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.Controller.TempData.ContainsKey("modelErrors"))
{
foreach (var errors in (Dictionary<string, List<string>>)filterContext.Controller.TempData["modelErrors"])
foreach (var error in errors.Value)
filterContext.Controller.ViewData.ModelState.AddModelError(errors.Key, error);
}
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Controller.ViewData.ModelState.Values.Any(x => x.Errors.Count > 0))
{
if (filterContext.Controller.TempData.ContainsKey("modelErrors"))
filterContext.Controller.TempData.Remove("modelErrors");
else
{
filterContext.Controller.TempData["modelErrors"] =
filterContext.Controller.ViewData.ModelState.
Where(item => item.Value.Errors.Count > 0).
ToDictionary(
item => item.Key,
item => item.Value.Errors.Select(error => error.ErrorMessage).ToList()
);
filterContext.Controller.TempData.Keep("modelErrors");
}
}
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
}
#endregion
}
After you just have to put your attribute on the top of the action that throw the error and the action that received the error like this :
[RedirectError]
public ActionResult Delete(Guid id)
{
[RedirectError]
public ActionResult Get(Guid id)
{
And that works like a charm with clean manageable code.
Hope this Help!
Julien