I'm trying to implement custom error handling within a Web API project using my web.config file. My issue is that the redirect is not happening. I have the following set up in my web config:
<customErrors mode="On" defaultRedirect="~/Page/Error?errorId=59">
<error statusCode="403" redirect="~/Page/Error?errorId=59"/>
</customErrors>
This is where it may be tricky:
I'm using a custom attribute that I'm placing in my controller, and the attribute returns a 403 status code if the request is not coming from a listed referrer.
Here's the attribute Code:
public class Action1DebugActionWebApiFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// pre-processing
HttpRequestMessage request = actionContext.Request;
string ipAddress = ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
if (!IsIpAddressAllowed(ipAddress.Trim()))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
}
}
Then I add the attribute to my controller like so:
[Action1DebugActionWebApiFilter]
[HttpGet]
[Route("MyRedirect")]
public IHttpActionResult MyRedirect([FromUri]UserModel myUser)
I know the 403 is getting produced because I can see it in my Firefox network tab. But the redirect isn't happening (verified in Fiddler). I just get a blank screen on the controller redirect with all the Get parameters just sitting there.
Any ideas?
That is because the <customErrors> section does not apply to asp web api.
Related
In my ASP.NET WebAPI controller, the following routing setup works correctly:
[Route("api/products")]
public class ProductsController : ApiController
{
[HttpGet]
public IHttpActionResult Get()
{
return Ok();
}
}
However, when I change the route to api/catalog/products I start getting 403 errors when accessing the route. Why would that extra segment cause a 403 error and how can I fix it?
Here is my config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}
First I would suggest you update the attribute routes to follow the suggested format
[RoutePrefix("api/products")]
public class ProductsController : ApiController {
//GET api/products
[HttpGet]
[Route("")] //(Default route)
public IHttpActionResult Get() {
return Ok();
}
}
that uses the [RoutePrefix] attribute on the ApiController and adding the [Route] attribute on the action.
Next, 403 Forbidden typically occurs when you try to browse to a directory on a site where the Web site does not have the Directory Browsing feature enabled, and the default document is not configured.
In this case you may have an actual folder which is conflicting with the default route of the ProductsController.
when you update the route and try to call api/catalog/products it will try to return the actual content of that folder which will fail if the feature is not enabled.
Either remove or rename the folder to something that does not conflict with any of your controller routes.
Reference Attribute Routing in ASP.NET Web API 2
I have an Action Attribute
ReportAccessAttribute
Added this attribute to a controller action
ReportingController
My application is in angularjs
When tested with other controller and action this attribute works fine.
Now I am requesting that Action method of controller from my application by setting the url in an anchor tag.
This anchor tag opens a new tab and sends the request to server.
In this case Action attribute do not execute.
Please suggest.
Code Sample
Action Attribute
public class ReportAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
//Some work...!!
base.OnActionExecuting(actionContext);
}
}
Controller
public class ReportingController : Controller
{
public ReportingController()
{
//Some initializations..!!
}
[Filters.ReportAccess]
public ActionResult Report(string reportName)
{
//Report generation logic.
}
}
When accessed from my angularjs app it gets executed eg: using a service to call that method.
But when I create a url and set anchor tag value to that url and click on that anchor tag. It opens that url in new tab and then it don't executes.
Thanks for the response.
The problem was I create the attribute using namespaces from WebApi dll and the controller from MVC dll.
That's why it wasn't intercepted in the execution pipeline.
I am developing an ASP.Net MVC 3 Web Application. I need to have my website secured with an SSL certificate, however, I only want this used when the application is on my live server, NOT on my test server.
Therefore, I setup an AppSetting in my Web Config like so
<appSettings>
<add key="SSL" value="false" />
</appSettings>
Then in my Account Controller I get this value (either True or False) and using the value, decide whether or not to set the RequiresHttps attribute on my LogOn Action. I would like to do something like so
public class AccountController : Controller
{
public string SSL = System.Configuration.ConfigurationManager.AppSettings["SSL"];
if (SSL.Equals("true"))
{
[RequireHttps]
}
public ActionResult LogOn()
{
return View();
}
}
But I know I can't put my IF statement where it currently is, however, hopefully you get the idea of what I am trying to achieve.
Does anyone have any suggestions as to how I can implement my idea?
Thanks.
Subclass the RequireHttpAttribute (note this code is changed from my original answer - this new version will be more efficient):
public class RequireHttpsIfEnabledAttribute : RequireHttpsAttribute
{
//this setting can't be changed without a recycle, so get it once and cache it.
private static readonly Lazy<bool> HttpsRequired = new Lazy<bool>(() => {
//if the AppSettings["SSL"] returns null you raise an exception if you do a
//.Equals on it - so do it on the constant instead. And make sure it's case
//insensitive!
return "true".Equals(System.Configuration.ConfigurationManager.AppSettings["SSL"],
StringComparison.OrdinalIgnoreCase);
});
public override void OnAuthorization(AuthorizationContext filterContext)
{
//calling the base will fire the HTTPS check. Not calling it will allow
//non-SSL requests through
if (HttpsRequired.Value)
base.OnAuthorization(filterContext);
}
}
Now you just decorate your controllers/actions as before - but with your new attribute:
[RequireHttpsIfEnabled]
public class AccountController : Controller
{
//....
}
I am trying to implement a very basic login scheme for my MVC3 site. If I understand correctly, instead of adding the [Authorize] markup to each of my controller classes, I should be able to simply implement a global setting. To accomplish this, I have added the following into global.asax:
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
}
and in my webconfig, I added:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
The result is that the resulting page is totally blank. Looking at the url, it seems that mvc is redirecting to my login route as expected except the page empty. If I comment out the code in global.asax and just place the [Authorize] markup directly in each contoller, it works as expected.
As a workaround, I have implemented what I have read the MVC2 best practice to be, which was to create a BaseController:Controller class, add the [Authorize] markup to it, and then change the inherentences of all of my controllers to inheret from BaseController instead of Controller.
That seems to work well enough for now.
But why isn't the global.asax implementation working?
Let's see what's happening here:
You are navigating to /
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Same as 3.
Same as 4.
...
I think you get the point. The LogOn action should be excluded from authentication otherwise the user can never get a chance to login to your web site.
Since you have applied the Authorize attribute globally this cannot be done. One possible way is to write a custom AuthorizeAttribute that will be applied globally and which will exclude this action from authentication.
So you could write a marker attribute:
public class AllowAnonymousAttribute : Attribute
{
}
and a global custom authorize attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var exclude = ((AllowAnonymousAttribute[])filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), false)).Any();
if (!exclude)
{
base.OnAuthorization(filterContext);
}
}
}
that will be registered:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizeAttribute());
}
Now all that's left for you is to decorate the controller actions that you want to be excluded from authentication with our marker attribute:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult LogOn()
{
return View();
}
[AllowAnonymous]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
...
}
}
How do you setup SessionStateAttribute as a global filter in MVC3?
In my Global.asax I have this in the RegisterGlobalFilters method.
filters.Add(new SessionStateAttribute(SessionStateBehavior.Disabled));
And in my home controller I have this.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
Session["Blend"] = "Will it blend?";
return View();
}
public ActionResult About()
{
return View();
}
}
But for some reason it still lets me use the Session. However if I decorate the HomeController class itself with the attribute, I get an error on the line utilizing the Session about a Object reference being null, which I'm guessing is intended if the Session is never created?
I am starting to wonder if there is something wrong with my project. I've been getting little problems like this one with standard behavior that are supposed to just work.
Anyone else had problems with things like this?
SessionStateAttribute is not an action filter, so you cannot add it as a global action filter. It's a special attribute which allows you to decorate your controllers with and have a more fine grained control over the session mode per controller.
To disable the session globally for the entire application put the following in your web.config:
<sessionState mode="Off" />