I have a long time polling controller in my MVC3 project. It has its timeout set to 30 seconds. I have a HandleErrorAttribute implementation that handles logging of all errors.
Since the timout throws a TimeoutException it means these will be presented in the log.
I need to intercept this error before my HandleErrorAttribute class gets it and return a json object instead of the 500 error page. Whats the best approach for this?
I did this and it works
public class HandleTimeout : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is TimeoutException)
{
filterContext.Result = new { Timeout = true }.AsJson();
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = 200;
}
base.OnException(filterContext);
}
}
Best approach?
I went with this route, the difference from my above code is that I also check if the Controller is Async, because we only want to handle Timeouts in this fashion if we are in a long time polling scenarios.
public class HandleTimeout : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is TimeoutException && filterContext.Controller is AsyncController)
{
filterContext.HttpContext.Response.StatusCode = 200;
filterContext.Result = new { Timeout = true }.AsJson();
filterContext.ExceptionHandled = true;
}
base.OnException(filterContext);
}
}
The notion of best is very subjective. I prefer not to talk about it as different people have different definition of it. For me using a custom exception filter is a very good approach to handle this case without polluting your controller with exception handling code.
Related
My controllers return unified RequestResult:
public Task<RequestResult> SomeAction()
{
...
return new RequestResult(RequestResultType.NotFound);
}
public class RequestResult
{
public RequestResultType Type { get;set; }
... //actual data
}
public enum RequestResultType
{
Success = 1,
NotFound = 2
}
So basically RequestResult combines actual Action data and error type (if it happened). Now I need to specify Response Type at some point in case if Action returned Error. My best guess here is to use Middleware:
public class ResponseTypeMiddleware
{
private readonly RequestDelegate next;
public ResponseTypeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
var response = context.Response.Body; //how to access object?
}
}
but I can't figure out what to do with it. What I'd perfectly like to do is to check if response is of type RequestResult, then specify ResponseType equal BadRequest. But I don't see how I can do it here as what I have is just a stream. May be I can hijack into pipeline earlier, before result was serialized (Controller?).
P. S. The reason why I don't use Controller.BadRequest directly in Action is that my Action's logic is implemented via CQRS command/query handlers, so I don't have direct access to Controller.
As you are going to process controller's action result (MVC), the best way is to use ActionFilter or ResultFilter here, instead of Middleware. Filters in ASP.NET Core are a part of MVC and so know about controllers, actions and so on. Middleware is a more common conception - it is an additional chain in application request-response pipeline.
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
// get or set controller action result here
var result = context.Result as RequestResult;
}
}
I'm currently having a problem using a JPA Listener to update/persist the current user updating/creating an object. Here is the JPAListener's code
private static UserSession userSession = null;//Scoped-session bean
// yes i know i'm accessing a session stored in HTTP in persistence layer
#PreUpdate
public void preUpdate(AbstractDAOAuditedEntity abstractEntity) {
abstractEntity.setModificationDate(new Date());
// use userSession here to set currentUser or system
}
#PrePersist
public void prePersist(AbstractDAOAuditedEntity abstractEntity) {
// same
}
public static void setUserSession(UserSession userSession) {
DAOEntityListener.userSession = userSession;
}
If i do it while processing an HttpRequest it works, because userSession is bound to an Http Session managed by spring.
But now i have a new usage, i'm receiving data from a JmsMessage, this mean i'm running in a thread without HttpContext, and so the listener crash when trying to use userSession.
As a really quick and really dirty fix i did the following :
boolean haveUser = true;
try {
userSession.getUser();
} catch (Exception e) {
haveUser = false;
}
if (!haveUser) {}
My question is not so about how to make it works but how i should have handle this properly, whether i'm in HttpContext or not ?
In a WEBAPI filter, im trying to calculate response size.
A similar process works for MVC controllers.
Inside actionExecutedContext.Response. i cant see a filter?
So I tried this filter below but this doesnt work.
How can i get the length of a WEBApi response ?
I could stick this in Global.ASAX and it works, but then every http call is logged...
So an API filter would be ideal. Is there something obviously wrong here ?
public class BosAPIFilter : System.Web.Http.Filters.ActionFilterAttribute{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
base.OnActionExecuted(actionExecutedContext);
var httpContext = actionExecutedContext.Request.Properties["MS_HttpContext"] as HttpContextWrapper;
if (httpContext != null) {
actionExecutedContext.Response.
httpContext.Response.Filter = new ResponseStreamHandler(httpContext.Response.Filter);
var handler = httpContext.Response.Filter as ResponseStreamHandler;
var adminService = new AdminServices();
adminService.HttpTrace(httpContext, handler);
}
}
public class ResponseStreamHandler : MemoryStream {
private readonly Stream _responseStream;
public long ResponseSize { get; private set; }
public ResponseStreamHandler(Stream responseStream) {
this._responseStream = responseStream;
ResponseSize = 0;
}
public override void Write(byte[] buffer, int offset, int count) {
this.ResponseSize += count;
this._responseStream.Write(buffer, offset, count);
}
// ReSharper disable once RedundantOverridenMember
public override void Flush() { base.Flush(); }
}
In ASP.NET Web API pipeline, action filters run before the result you return from the action method gets serialized. If you look at actionExecutedContext.Response.Content inside the filter, it will be System.Net.Http.ObjectContent (depending on your action method). So, you can calculate the response size only later in the pipeline. You can use a message handler to do this but then the granularity is not at the action method level. The lowest granularity you can get is at a route level. One way you get around this is to set a flag in the request dictionary from the filter and log from the handler only when the flag is set.
I have a site that needs to be secured with SSL. How can I set up a route or IIS configuration that will automatically send any request received on the non-SSL protocol over to the SSL protocol? Is this something that can be handled as a routing rule, or would it be best to use the RequireHttps attribute in the primary controller only on the methods rather than on the controller itself and detect the protocol in the Index() method?
As a note: I read this question that makes use of UrlRewriting and IIS7's Application Request Routing, but I am stuck with IIS6 and UrlRewriting is not an option for me.
Something like this will help:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new RequireHttpsAttribute());
}
This is what we use. Would love to hear if it can be improved.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class EnforceHttpsAttribute : RequireHttpsAttribute
{
private static bool AuthorizeCore(HttpContextBase httpContext)
{
return httpContext.Request.IsSecureConnection;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!AuthorizeCore(filterContext.HttpContext))
{
HandleNonHttpsRequest(filterContext);
}
else
{
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(CacheValidateHandler, null);
}
}
// ReSharper disable RedundantAssignment
private static void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
// ReSharper restore RedundantAssignment
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
private static HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
return !httpContext.Request.IsSecureConnection
? HttpValidationStatus.IgnoreThisRequest
: HttpValidationStatus.Valid;
}
}
Response to comment 1
Good question, I'm not sure. HandleNonHttpsRequest comes from the base RequireHttpsAttribute. I just did an inspection in fiddler2, and there was only 1 request sent over http. However the response came back over https.
I just realized we use the above to make RequireHttps work with the output cache. You might be better off just using the RequireHttps attribute.
I'm pretty new to MVC ASP.NET. I read about OnException override method. I'm thinking, whether I should put try catch {throw} on Controller or Model in order for OnException to be invoked. OR, I try catch not required, OnException will be invoked autmatically if any exception occurs?
Thanks Heaps.
"Called when an unhandled exception occurs in the action."
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onexception.aspx
If you don't handle (i.e. "catch") an exception, the OnException method should be called.
I ended up doing this:
Created LogAndRedirectOnErrorAttribute class that uses abstract class FilterAttribute and implements IExceptionFilter as shown below:
public class LogAndRedirectOnErrorAttribute : FilterAttribute,IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
//Do logging here
Util.LogError(Utility.GetExceptionDetails(filterContext.Exception), TraceEventType.Critical.ToString());
//redirect to error handler
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(
new { controller = "Error", action = "Index" }));
// Stop any other exception handlers from running
filterContext.ExceptionHandled = true;
// CLear out anything already in the response
filterContext.HttpContext.Response.Clear();
}
}
And on Each Controller Class where necessary, use the above attribute:
[LogAndRedirectOnError]
public class AccountController:Controller
{
.....
}