Override Elmah logged error message - asp.net

Is there any way of overriding the error message logged by Elmah without duplicating it?
I have a custom exception class:
public class BusinessException : Exception
{
// detailed error message used for logging / debugging
public string InternalErrorMessage { get; set; }
public BusinessException(string message, string internalMessage)
:base(message)
{
InternalErrorMessage = internalMessage;
}
}
From the code, i throw an exception like this:
string detailedErrorMessage = string.Format("User {0} does not have permissions to access CreateProduct resource", User.Identity.Name);
throw new BusinessException("Permission denied", detailedErrorMessage);
When Elmah logs the error, it only logs Permission denied message. However, i need to log the InternalErrorMessage property of the exception instead.
I've tried to create a custom HandleErrorAttribute to do this, but this duplicates the exceptions logged:
public class ErrorHandleAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled == true)
return;
Exception exception = filterContext.Exception;
BusinessException businessException = exception as BusinessException;
if (businessException != null)
{
ErrorSignal.FromCurrentContext().Raise(new Exception(businessException.InternalErrorMessage, exception));
}
}
}

I think your issue might be here:
if (businessException != null) {
ErrorSignal.FromCurrentContext().Raise(
new Exception(businessException.InternalErrorMessage, exception));
}
When you create a new exception rather than something like this:
if (businessException != null) {
ErrorSignal.FromCurrentContext().Raise(businessException));
}
I have this done the code for one of my sites and can see if I can recreate your issue later today (assuming this does not work). I think this is the SO post that helped me implement: How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
Edit:
Re-reading your question and the other answer I realize I was trying to solve your attempted correction and not the actual problem. Have you tried something like this solution which is only a slight deviation from your current attempt:
public override void OnException(ExceptionContext filterContext) {
if (filterContext.ExceptionHandled == true) {
return;
}
Exception exception = filterContext.Exception;
BusinessException businessException = exception as BusinessException;
if (businessException != null) {
var customEx = new Exception(
businessException.InternalErrorMessage, new BusinessException());
ErrorSignal.FromCurrentContext().Raise(customEx);
return;
}
}
Check that the InternalErrormessage is returning what you expect, and I expect that forcing a return here will prevent the exception from being logged twice. Otherwise it is essentially what you had done.

I suspect you'll need to create your own errorlog implementation to log anything other than the standard properties. This shouldn't be too difficult.
Using the SqlErrorLog as an example, you only need to override the log method & put in your own logic to modify what the Error class contains before calling the base implementation.
Using what #Matthew has also said will stop you logging the exception twice.
All the source code is here

Related

How to handle Internal server error (500) on spring rest API to custom the message?

I am working on spring rest api and I would like to sure everything is working fine. I would like to log abnormal behaviors database connection error among others, I'm working with couchbase database and I'm getting in the endpoint response for example for this kind of exception: CouchbaseQueryExecutionException the next message: Unable to execute query due to the following n1ql errors: \n{\"msg\":\"No index available on keyspace kids_club that matches your query. Use CREATE INDEX or CREATE PRIMARY INDEX to create an index, or check that your expected index is online.\",\"code\":4000} and a very long trace.
For this i found a solution on internet that is extend ResponseEntityExceptionHandler and override handleExceptionInternal method like this:
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
private static String DEFAULT_VALIDATION_ERROR_CODE = "KC-0020";
#ExceptionHandler(MiddlewareException.class)
protected ResponseEntity<ResponseBody> handleKidsClubException(MiddlewareException ex) {
return buildErrorResponse(HttpStatus.valueOf(ex.getHttpStatus()), ex.toError());
}
#ExceptionHandler(ServiceUnavailableException.class)
protected ResponseEntity<ResponseBody> handleServiceUnavailable(ServiceUnavailableException ex) {
return buildErrorResponse(INTERNAL_SERVER_ERROR, ex);
}
#ExceptionHandler(NoSuchElementException.class)
protected ResponseEntity<ResponseBody> handleNoFoundElement(NoSuchElementException ex) {
return buildErrorResponse(NOT_FOUND, ex);
}
#ExceptionHandler(CouchbaseQueryExecutionException.class)
protected ResponseEntity<ResponseBody> handleCouchbaseQueryException(ConstraintViolationException ex) {
return buildErrorResponse(BAD_REQUEST, ex);
}
}
But I'm not able to catch any kind of Internal Server Error in this way.
It seems like spring is handle and building the final message to the user.
Any ideas to resolve this?
Thanks.
#ExceptionHandler(NullPointerException.class)
public final ResponseEntity<Object> handleNullPointerException(NullPointerException ex, WebRequest request) {
LOGGER.info("Entering into the handleAllException method");
System.out.println("Exception is : " + ex.getClass());
ResponseData error = new ResponseData();
error.setRespCode(HttpStatus.NOT_FOUND.toString());
error.setRespMessage(ex.getLocalizedMessage());
error.setTimestamp(LocalDateTime.now());
return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
please try this way below to check whether you are able to catch exception or not.. From the sysout you will get the exact exception. Then you can use that exception to catch any particular exception from that business logic..

Custom Error message with #Preauthorize and ##ControllerAdvice

We are using spring and spring-security-3.2. Recently We are adding annotations #PreAuthorize to RestAPIs(earlier it was URL based).
#PreAuthorize("hasPermission('salesorder','ViewSalesOrder')")
#RequestMapping(value = "/restapi/salesorders/", method = RequestMethod.GET)
public ModelAndView getSalesOrders(){}
We already have Global exception handler which annotated with - #ControllerAdvice and custom PermissionEvaluator in place, everything works fine except the error message.
Lets say some user is accessing API At moment without having 'ViewSalesOrder' permission then spring by default throws the exception 'Access is denied',but didn't tell which permission is missing (Its our requirement to mention which permission is missing).
Is it possible to throw an exception which also include the permission name, so final error message should be look like "Access is denied, you need ViewSalesOrder permission"(here permission name should be from #PreAuthorize annotation)?
Please note that we have 100 such restAPI in place so generic solution will be highly appreciated.
There is no pretty way of achieving what you expect since PermissionEvaluator interface doesn't let you pass the missing permission along with the result of the evaluation.
In addition, AccessDecisionManager decides on the final authorization with respect to the votes of the AccessDecisionVoter instances, one of which is PreInvocationAuthorizationAdviceVoter which votes with respect to the evaluation of #PreAuthorize value.
Long story short, PreInvocationAuthorizationAdviceVoter votes against the request (giving the request –1 point) when your custom PermissionEvaluator returns false to hasPermission call. As you see there is no way to propagate the cause of the failure in this flow.
On the other hand, you may try some workarounds to achieve what you want. One way can be to throw an exception within your custom PermissionEvaluator when permission check fails. You can use this exception to propagate the missing permission to your global exception handler. There, you can pass the missing permission to your message descriptors as a parameter. Beware that this will halt execution process of AccessDecisionManager which means successive voters will not be executed (defaults are RoleVoter and AuthenticatedVoter). You should be careful if you choose to go down this path.
Another safer but clumsier way can be to implement a custom AccessDeniedHandler and customize the error message before responding with 403. AccessDeniedHandler provides you current HttpServletRequest which can be used to retrieve the request URI. However, bad news in this case is, you need a URI to permission mapping in order to locate the missing permission.
I have implemented the second possible solution mentioned by Mert Z. My solution works only for #PreAuthorize annotations used in the API layer (e.g. with #RequestMapping). I have registered a custom AccessDeniedHandler bean in which I get the value of the #PreAuthorize annotation of the forbidden API method and fills it into error message.
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private DispatcherServlet dispatcherServlet;
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
if (!response.isCommitted()) {
List<HandlerMapping> handlerMappings = dispatcherServlet.getHandlerMappings();
if (handlerMappings != null) {
HandlerExecutionChain handler = null;
for (HandlerMapping handlerMapping : handlerMappings) {
try {
handler = handlerMapping.getHandler(request);
} catch (Exception e) {}
if (handler != null)
break;
}
if (handler != null && handler.getHandler() instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler.getHandler();
PreAuthorize methodAnnotation = method.getMethodAnnotation(PreAuthorize.class);
if (methodAnnotation != null) {
response.sendError(HttpStatus.FORBIDDEN.value(),
"Authorization condition not met: " + methodAnnotation.value());
return;
}
}
}
response.sendError(HttpStatus.FORBIDDEN.value(),
HttpStatus.FORBIDDEN.getReasonPhrase());
}
}
#Inject
public void setDispatcherServlet(DispatcherServlet dispatcherServlet) {
this.dispatcherServlet = dispatcherServlet;
}
}
The handler is registered in WebSecurityConfigurerAdapter:
#EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
#EnableWebSecurity
public abstract class BaseSecurityInitializer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
...
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
Beware that if there is also a global resource exception handler with #ControllerAdvice the CustomAccessDeniedHandler won't be executed. I solved this by rethrowing the exception in the global handler (as advised here https://github.com/spring-projects/spring-security/issues/6908):
#ControllerAdvice
public class ResourceExceptionHandler {
#ExceptionHandler(AccessDeniedException.class)
public ResponseEntity accessDeniedException(AccessDeniedException e) throws AccessDeniedException {
log.info(e.toString());
throw e;
}
}
You can throw an org.springframework.security.access.AccessDeniedException from a method that was called inside an EL-Expression:
#PreAuthorize("#myBean.myMethod(#myRequestParameter)")
Ideally, the #PreAuthorize annotation should be supporting String message(); in addition to the SpEl value. But, for whatever reason, it does not. Most of the suggestions here seem unnecessarily cumbersome and elaborate. As #lathspell has suggested, the simplest way to provide your own error message - along with any custom access validation logic - would be to add a simple method that performs the check and throws the AccessDeniedException in case the check fails, and then reference that method in the SpEl expression. Here's an example:
#RestController
#RequiredArgsConstructor // if you use lombok
public class OrderController {
private final OrderService orderService;
...
#GetMapping(value = "/salesorders", produces = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("#orderController.hasPermissionToSeeOrders(#someArgOfThisMethod)")
public Page<OrderDto> getSalesOrders(
// someArgOfThisMethod here, perhaps HttpRequest, #PathVariable, #RequestParam, etc.
int pageIndex, int pageSize, String sortBy, String sortOrder) {
Pageable pageRequest = PageRequest.of(pageIndex, pageSize, Sort.Direction.fromString(sortOrder), sortBy);
return ordersService.retrieveSalesOrders(..., pageRequest);
}
public static Boolean hasPermissionToSeeOrders(SomeArgOfTheTargetMethod argToEvaluate) {
//check eligibility to perform the operation based on some data from the incoming objects (argToEvaluate)
if (condition fails) {
throw new AccessDeniedException("Your message");
}
return true;
}

Unable to Set Cookie in ASP.NET MVC IExceptionFilter

I've implemented a custom IExceptionFilter to handle an exception some users are experiencing with a third party library our application is consuming. When this particular error state occurs, I need to modify the user's cookies (to clear their session state), but what I am finding is that no cookies seem to make it out of the filter. Not sure what's wrong or even how to debug it.
I've modified the functional bits filter to simplify the intent, but here's the gist of the filter. I've ensured that it is the first filter to run on the controller and tested removing the HandleErrorAttribute filter as well to no avail. After the below code runs, "somecookie" is never set on the client.
public class HandleSessionErrorAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException("filterContext");
var exception = filterContext.Exception as HttpException;
if (exception != null && exception.Message.Equals("The session state information is invalid and might be corrupted."))
{
filterContext.HttpContext.Response.Cookies.Add(new HttpCookie("somecookie")
{
Value = DateTime.Now.ToString(),
Expires = DateTime.Now.AddMinutes(5),
});
}
}
}
Okay, figured it out. Two issues were preventing the logic from succeeding:
The HandleErrorAttribute must run before any attribute which modifies the response. Part of the implementation of HandleErrorAttribute is to Clear() the response.
CustomErrors must be On for this to work
The initialization code which worked:
GlobalFilters.Filters.Add(new HandleErrorAttribute() { Order = 0 });
GlobalFilters.Filters.Add(new HandleSessionErrorAttribute() { Order = 1 });

How to prevent Elmah logging errors handled in a Error Attribute

I am using Elmah and Elmah.mvc packages in a asp.net, mvc4 web app. I have a specific controller action where I want to handle HttpAntiForgeryExceptions in a specific manner. I have created a custom HandleErrorAttribute that inherits from HandleErrorAttribute and implements IExceptionFilter.
Under the specific circumstances I want to handle, I set the ExceptionContext.ExceptionHandled to true. The behaviour that the user sees is correct and the error is handled as I want it to be. However, it also logs an error to Elmah, which I don't want it to do, as I would like to keep the Elmah log for true errors.
The controller annotation looks like:
[ValidateAntiForgeryToken]
[CustomHandleAntiforgeryError]
public ActionResult ControllerMethod(Model model)
{
...
}
The CustomHandleAntiforgeryError looks like:
public class CustomHandleAntiforgeryErrorAttribute:
HandleErrorAttribute, IExceptionFilter
{
public override void OnException(ExceptionContext filterContext)
{
if (circumstancesAreOk)
{
filterContext.ExceptionHandled = true;
return;
}
}
}
Is there anything else I need to do to prevent this error being logged with Elmah?
--- EDIT ---
Looking at the Elmah.MVC source the HandleErrorAttribute logs both handled and unhandled errors
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled) // if unhandled, will be logged anyhow
return;
var e = context.Exception;
var httpContext = context.HttpContext.ApplicationInstance.Context;
if (httpContext != null &&
(RaiseErrorSignal(e, httpContext) // prefer signaling, if possible
|| IsFiltered(e, httpContext))) // filtered?
return;
LogException(e, httpContext);
}
I would like a way within my custom attribute to signal to Elmah not to log this error and would appreciate any ideas.
See ErrorFiltering from Elmah's documentation. Here's the introduction:
When an unhandled exception is reported to ELMAH by ASP.NET, an
application can decide whether to dismiss the exception or not. There
are two ways for an application to do this, either programmatically or
declaratively via the configuration file. The simpler of the two is
programmatically because you do not need to learn anything new except
write an event handler in your favorite language. The downside of the
programmatic approach is that you need to write code and modify your
web application (requiring possibly a static re-compile). With the
configuration-based approach, you can simply apply filtering of
exceptions to a running application.
What I did to solve this I think is ugly, but worked for the weird error filter corner case that I was experiencing. I added a custom HandleErrorAttribute, copied from the Elmah HandleErrorAttribute and included a null check in the OnException method.
public override void OnException(ExceptionContext context)
{
base.OnException(context);
if (!context.ExceptionHandled) // if unhandled, will be logged anyhow
return;
string[] formKeys = context.HttpContext.Request.Form.AllKeys;
var e = context.Exception;
// linked to CustomErrorAttribute
if (e == null)
{
return;
}
bool test = HostingEnvironment.IsHosted;
var httpContext = context.HttpContext.ApplicationInstance.Context;
if (httpContext != null &&
(RaiseErrorSignal(e, httpContext) // prefer signaling, if possible
|| IsFiltered(e, httpContext))) // filtered?
return;
LogException(e, httpContext);
}
Then, in my error filter, which I did not want to trigger messaging to Elmah, as well as setting the ExceptionContext ExceptionHandled to true I set the Exception to null
public class MyCustomErrorFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (blah, blah, blah, weird things but not something terrible)
{
filterContext.Exception = null;
filterContext.ExceptionHandled = true;
return;
}
}
}
If it was something more involved and a regular occurrence I would probably fork Elmah and look at creating a custom Elmah build as this feels a little bit hacky to rely on in multiple situations.

Best way to abort/cancel action and response from ActionFilter

Best way to abort/cancel action from ActionFilter
I've got this ActionFilter, and it's suppose to end the connection immediately and return a 401 Unauthroized:
public class SignInRequired : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// User is verified, continue executing action
if (Acme.Web.CurrentUser != null)
{
return;
}
// End response with 401 Unauthorized
var response = HttpContext.Current.Response;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
// Prevent the action from actually being executed
filterContext.Result = new EmptyResult();
}
}
I learned how you can cancel the action from executing by setting 'context.Result = new EmptyResult()` here, but I'm not sure if this is the best way to flush the response and close the connection.
Setting the response will mean the action doesn't get called.
public override void OnActionExecuting(HttpActionContext actionContext)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
As other answers have said, though, authentication should be done with an AuthorizeAttribute (Docs for Web.API or for MVC).
On .net core 2.2, 3.0 and 3.1 and .net 5 the below example works fine
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = new UnauthorizedObjectResult("user is unauthorized");
}
The answer that #OdeyinkaOlubunmi is correct for Web API or specifically System.Web.Http.Filters.ActionFilterAttribute but it can't be used for System.Web.Mvc.ActionFilterAttribute. AuthorizeAttribute and overriding AuthorizeCore is a good way to go but if you use #Vadim's example for a GlobalFilter you will end up with the following error in a standard configuration:
HTTP Error 404.15 - Not Found The request filtering module is
configured to deny a request where the query string is too long.
This is because the default /Login?ReturnUrl= will keep appending new values until the query string causes an exception.
The way I have solved it for MVC is like this:
public class DebugActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext actionContext)
{
actionContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
return;
}
}
You can set the result of filterContext for the Exception page like this:
filterContext.Result = new RedirectResult("~/Error/Unauthorized");
See more details here on section Canceling Filter Execution
You probably want to make it an AuthorizeAttribute. That will set the result to be an UnAuthorizedResult automatically, plus it has the benefit of being run before any other filters. Alternatively you can set the Result to be a new HttpUnauthorizedResult
public class SignInRequiredAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !Acme.Web.CurrentUser != null;
}
}
using .net core 2.1 the solutions above did not work for me , so i tried this and it worked :-
context.HttpContext.Response.StatusCode = 401;
return;
if there is better solutions for .net core 2.1 i am open for suggestions

Resources