I'm trying to catch errors coming from one of my controllers. Elmah is not catching them so in trying to find out why, I reduced the problem to the simplest form which excludes Elmah.
[HttpGet]
[Route("foo")]
public HttpResponseMessage Foo()
{
throw new Exception("test");
}
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new UnhandledExceptionFilter());
GlobalConfiguration.Configure(WebApiConfig.Register);
public class UnhandledExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context) {
dosomething(); //It never reaches here
Note: a proper JSON error is emitted to the client.
Instead of doing GlobalConfiguration.Configuration.Filters.Add, go to the Register method in the WebApiConfig class and do config.Filters.Add(new ...()); instead, that works for me.
Related
I have a project where there is a controller within a web forms application.
My controller is called Token
public class TokenController : BaseTokenController
{
public override bool IsInherited => true;
}
this controller inherits from BaseTokenController
public abstract class BaseTokenController : ApiController
{
public abstract bool IsInherited { get; }
public virtual bool Post([FromBody]TokenValidateArgs args)
{
if (!IsInherited)
throw new Exception("Attempt to call base token controller not allowed");
return args.Validate();
}
public virtual string Get()
{
if (!IsInherited)
throw new Exception("Attempt to call base token controller not allowed");
return new Token()
}
}
In my global.asax I have a method called Register:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("API default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
and in global.asax Application_Start the first line of code is
protected void Application_Start(object sender, EventArgs e)
{
Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I did have this working, however I then merged with the master branch for the project and it stopped working. I can't see anything that has changed.
this is the error:
No HTTP resource was found that matches the request URI 'https://localhost:44398/api/token'.
No type was found that matches the controller named 'token'.
Has anyone else experienced a similar issue and would know how to fix this. I have read other threads and tried to fix this by putting in a RoutePrefix on the controller, moving the order of execution on the global.asax and calling the get() method directly
So for my issue it turns out the project that has the "BaseTokenController" was running version 5.2.3 of WebApi however the web application project was running 5.2.4 which caused a conflict, resulting in a 404 error. To fix this I downgraded the app to 5.2.3
I am having a tuff time trying to get an instace of a HttpRequestMessage so I can pass it to the method GetCacheOutputProvider below from an ActionFilter and/or normal ASP.NET MVC Controller. I know I can from the Web API, but what about these instances.
public class CacheResetFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var cache = GlobalConfiguration.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(HTTPREQUESTMESSAGE);
cache.Contains("eventid=" + eventId);
base.OnActionExecuted(filterContext);
}
1.In a MVC Controller you can do like:
public class HomeController : Controller
{
public ActionResult Test()
{
HttpRequestMessage httpRequestMessage =
HttpContext.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
return View();
}
}
2.In action filter you can do like :
public class HttpRequestMessageAttribute : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuted(System.Web.Mvc.ActionExecutedContext filterContext)
{
HttpRequestMessage httpRequestMessage =
filterContext.HttpContext.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
//var cache = GlobalConfiguration.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(httpRequestMessage);
//cache.Contains("eventid=" + eventId);
base.OnActionExecuted(filterContext);
}
}
OR
public class HttpRequestMessageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpRequestMessage httpRequestMessage =
filterContext.HttpContext.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
base.OnActionExecuting(filterContext);
}
}
Hopefully it's help for you.
I don't think there is a simple way. You want an instance of HttpRequestMessage class which sematically represents a (current) request to WebAPI. But you are not inside WebAPI and don't handle any WebAPI requests. Thus it is logical that you can't easily have a valid instance of HttpRequestMessage (if you could, what URL would it point to?). IMHO the most obvious way to work this around is to use RegisterCacheOutputProvider method from CacheOutputConfiguration to inject your own cache provider that would return an instance of IApiOutputCache that you can directly access using other means (such as globally visible singleton). It looks there is only one standard implementation of IApiOutputCache: MemoryCacheDefault. So it looks like if you return it from your registered provider, you'll be OK.
If you want to be a more hacky, it looks like all MemoryCacheDefault instances internally use the same shared (static) field to do the actual work so you probably can just create new MemoryCacheDefault in your filter or controller and still be OK for now, but to me this sounds way to hacky comparing to the alternative from the first part of my answer.
I am trying to remove the "X-Frame-Options" header for only a specific controller's actions using:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuting(filterContext);
}
However, that doesn't seem to work at all. The only way I can get it to work at all on my site is to add this code to the global.asax below. I am pretty sure I am missing the correct step in the ASP.NET MVC / IIS pipeline that allows me to overwrite the IIS setting of that header. Is this possible?
protected void Application_EndRequest()
{
Response.Headers.Remove("X-Frame-Options");
}
As for why I want to do this, I am building a widget that user's will be able to use on their personal sites through the use of an iframe, but allow them to post back information to our site. I realize there are security implications to turning this header off, and while I welcome any suggestions on how to mitigate those risks, I just want to know if what I am asking is possible.
OnResultExecuting happens too early in the MVC lifecycle. The header has not been set yet.
What you need is the OnResultExecuted method which is run after the View is rendered.
Here's how you write a filter class for what you are looking for:
using System.Web.Mvc;
namespace Test.Filters
{
public class RemoveXFrameOptionsAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuted(filterContext);
}
}
}
Then to use it, decorate whatever Controller or Action you want this filter applied.
[RemoveXFrameOptions]
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
}
or
public class TestController : Controller
{
[RemoveXFrameOptions]
public ActionResult Index()
{
return View();
}
}
I've got an abstract class shown below which gets inherited by all the other controllers. Is it possible to test this method at all? Btw, I'm trying to use MOQ but no luck. If you could help me will be much appreciated:
public abstract class ApplicationController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
//do some stuff here
}
}
If you take a look at the source code of base Initialize method you will find out that what it does is that it sets up ControllerContext and url stuff. Now, download MvcContrib TestHelper and check out TestControllerBuilder . The builder sets up everything you need in order to have controller context and other stuff which you depend upon.
Ok, we are not over yet - you wanted to test your own override of Initialize right?
TestControllerBuilder doesnt call your Initialize because it does initialization in different way. I suggest you to factor out your custom Initialize() logic out into different method. Then create fake (stub) subclass with public method that calls this factored out protected Initialize. Are you with me?
something like:
public abstract class ApplicationController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
MyInitialzie()
}
protected void MyInitialize()
{
ControllerContext.XXX // do whatewer you want here. Context is already setted up
}
}
class FakeController: ApplicationController
{
public void CallMyInitialize()
{
MyInitialize();
}
}
Later in test class:
[Test]
public void MyInitializeTest()
{
TestControllerBuilder builder = new TestControllerBuilder();
FakeController controller = new FakeController();
builder.InitializeController(controller);
controller.CallMyInitialize();
//TODO: verification of MyInitialize assumptions
}
Is that clear?
I know it's a little odd, specifically because a Page inherits from the System.Web.Page (or something) and a WebService inherits from System.Web.Service (or something).
But just thought I'd ask if there is any way to do this? Does anyone have suggestions to do this?
public class MyWebService : System.Web.Services.WebService
{
[WebMethod]
public String MyMethod()
{
return "";
}
}
public class MyWebPage : System.Web.UI.Page
{
protected void ExecuteButton_Click(Object sender, EventArgs e)
{
...
}
}
Not sure what you are looking for but you can have [WebMethod] as an attribute to your method on .aspx.cs page. I use it to execute this method from .aspx page.
ex:-
[WebMethod]
public static void YourMethod(string parameter)
{
}
From the code that you have posted thing I cannot see the static method on your page secondly I dont see the call in your service to the method.
try doing something like this
public static type MyMethodOnPage()
and the in your servie method call this static method.
public type MyMethodInService()
{
return MyPageName.MyMethodOnPage();
}
Yes we have. We can use AJAX callbacks.
Check this URL
http://geekswithblogs.net/frankw/archive/2008/03/13/asp.net-ajax-callbacks-to-web-methods-in-aspx-pages.aspx