How to handle global Exception in ASP.Net MVC 2 - exception

My base controller has this override:
protected override void OnException(ExceptionContext filterContext)
{
// http://forums.asp.net/t/1318736.aspx
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500)
{
return;
}
}
I want to catch the exception and mail it. I can do the mail part, but not sure how to gather the exception? I am new to MVC. In Web Forms I had this in global.asax
void Application_Error(object sender, EventArgs e)
{
string AdminEmail = "myEmail#domain";
// Code that runs when an unhandled error occurs
Exception objErr = Server.GetLastError().GetBaseException();
string err = "Error Caught in Application_Error event\n" +
"Error in: " + Request.Url.ToString() +
"\n Error Message:" + objErr.Message.ToString() +
"\n Stack Trace:" + objErr.StackTrace.ToString();
// Below uses: using System.Diagnostics;
//EventLog.WriteEntry("Sample_WebApp", err, EventLogEntryType.Error);
//Server.ClearError(); // Clear error prohibits it from showing on page
//additional actions...
MyApp.Utility.Email.Send(AdminEmail, CommonLibrary.FromEmail, "Asp.net Application Error", err);
}
Web.config
<customErrors mode="On" />

I think you need to implement the same logic in your override method on base controller. Something like this:
protected override void OnException(ExceptionContext filterContext)
{
string AdminEmail = "myEmail#domain";
// Code that runs when an unhandled error occurs
Exception objErr = filterContext.Exception;
string err = "Error Caught in Application_Error event\n" +
"Error in: " + Request.Url.ToString() +
"\n Error Message:" + objErr.Message.ToString() +
"\n Stack Trace:" + objErr.StackTrace.ToString();
// Below uses: using System.Diagnostics;
//EventLog.WriteEntry("Sample_WebApp", err, EventLogEntryType.Error);
//Server.ClearError(); // Clear error prohibits it from showing on page
//additional actions...
MyApp.Utility.Email.Send(AdminEmail, CommonLibrary.FromEmail, "Asp.net Application Error", err);
base.OnException(filterContext);
}

Related

Is there any way within middleware running on ASP.NET Core 2.2 to detect if the request is for an ApiController?

I have an application with both MVC and 'new' ApiController endpoints in ASP.NET Core 2.2 co-existing together.
Prior to adding the API endpoints, I have been using a global exception handler registered as middleware using app.UseExceptionHandler((x) => { ... } which would redirect to an error page.
Of course, that does not work for an API response and I would like to return an ObjectResult (negotiated) 500 result with a ProblemDetails formatted result.
The problem is, I'm not sure how to reliably determine in my 'UseExceptionHandler' lambda if I am dealing with an MVC or a API request. I could use some kind of request URL matching (eg. /api/... prefix) but I would like a more robust solution that won't come back to bite me in the future.
Rough psuedo-code version of what I'm trying to implement is:
app.UseExceptionHandler(x =>
{
x.Run(async context =>
{
// extract the exception that was thrown
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
try
{
// generically handle the exception regardless of what our response needs to look like by logging it
// NOTE: ExceptionHandlerMiddleware itself will log the exception
// TODO: need to find a way to see if we have run with negotiation turned on (in which case we are API not MVC!! see below extensions for clues?)
// TODO: ... could just use "/api/" prefix but that seems rubbish
if (true)
{
// return a 500 with object (in RFC 7807 form) negotiated to the right content type (eg. json)
}
else
{
// otherwise, we handle the response as a 500 error page redirect
}
}
catch (Exception exofex)
{
// NOTE: absolutely terrible if we get into here
log.Fatal($"Unhandled exception in global error handler!", exofex);
log.Fatal($"Handling exception: ", ex);
}
});
});
}
Any ideas?
Cheers!
This might be a bit different than what you expect, but you could just check if the request is an AJAX request.
You can use this extension:
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Headers == null)
return false;
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
And then middleware with an invoke method that looks like:
public async Task Invoke(HttpContext context)
{
if (context.Request.IsAjaxRequest())
{
try
{
await _next(context);
}
catch (Exception ex)
{
//Handle the exception
await HandleExceptionAsync(context, ex);
}
}
else
{
await _next(context);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
//you can do more complex logic here, but a basic example would be:
var result = JsonConvert.SerializeObject(new { error = "An unexpected error occurred." });
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
see this SO answer for a more detailed version.
If you want to check whether the request is routed to ApiController, you could try IExceptionFilter to hanlde the exceptions.
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (IsApi(context))
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
var message = context.Result;
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new { ErrorMsg = message });
}
else
{
}
}
private bool IsApi(ExceptionContext context)
{
var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = controllerActionDesc
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));
return attribute == null ? false : true;
}
}
Thanks to all of the advice from others, but I have realised after some more thought and ideas from here that my approach wasn't right in the first place - and that I should be handling most exceptions locally in the controller and responding from there.
I have basically kept my error handling middleware the same as if it was handling MVC unhandled exceptions. The client will get a 500 with a HTML response, but at that point there isn't much the client can do anyway so no harm.
Thanks for your help!

Asp.Net Core MVC capture application exception details

After some research I could not find a way to capture application exceptions in asp.net core mvc with preserving default error page behaviour. There are actually two ways for custom handling application errors. First and simple way is to configure app.UseExceptionHandler("/Home/Error"); this in the Startup.cs file, but this way I'd lost the default DEVELOPMENT error page pretty view. Other solution to customize error handling in asp.net core mvc is to define exception handler inline, but that would cause default error page to override as well:
app.UseExceptionHandler(
options => {
options.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "text/html";
var ex = context.Features.Get<IExceptionHandlerFeature>();
if (ex != null)
{
var err = $"<h1>Error: {ex.Error.Message}</h1>{ex.Error.StackTrace }";
await context.Response.WriteAsync(err).ConfigureAwait(false);
}
});
}
);
I need just to capture error details, without overriding the default behaviour (pretty default error page, et cetera). I don't need any custom exception handler, in fact I just need to grab exception. I'd like to do it at application level, so custom ExceptionHandlerAttribute that implements IExceptionFilter won't work. That solution would remove the default error page, also I need to catch middleware errors, not only controler exceptions. Following approach is not applicable:
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
String message = String.Empty;
var exceptionType = context.Exception.GetType();
if (exceptionType == typeof(UnauthorizedAccessException))
{
message = "Unauthorized Access";
status = HttpStatusCode.Unauthorized;
}
else if (exceptionType == typeof(NotImplementedException))
{
message = "A server error occurred.";
status = HttpStatusCode.NotImplemented;
}
else if (exceptionType == typeof(MyAppException))
{
message = context.Exception.ToString();
status = HttpStatusCode.InternalServerError;
}
else
{
message = context.Exception.Message;
status = HttpStatusCode.NotFound;
}
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
var err = message + " " + context.Exception.StackTrace;
response.WriteAsync(err);
}
}
That's the page, that I'd like to keep:
The solution is to use Elm for ASP.NET Core applications, the sample code is provided by Microsoft on their GitHub account: https://github.com/aspnet/Diagnostics, also there is reworked, stable version of the ASP.NET Core MVC logger, described in my article https://www.codeproject.com/Articles/1164750/Error-logging-in-ASP-NET-Core-MVC-Elmah-for-Net-Co. Happy coding!

android volley does not receive php json object

I'm using android volley for send request to server side, in the other hand on server side I use php.
Here is my android code to receive response from server(does not matter what params is, because I do not need params on server side yet!):
JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Log.d("ATA d Response", response.toString());
VolleyLog.v("ATA Response:%n %s", response.toString(4));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("ATA Response", " RESPONSE ERROR");
VolleyLog.e("Error: ", error.getMessage());
}
});
req.setShouldCache(false);
queue.getCache().clear();
queue.add(req);
Now, when in server side i return as response something like this:
if( isset($_POST)) {
echo json_encode(array("x"=>"y", "m"=>"n"));
exit();
}
every this is ok, and response is what server sends!
But! when in server side I return as response something like this:
if( isset($_POST)) {
echo json_encode(array(array("0"=>"1"),array("2"=>"3"),array("4"=>"5")));
exit();
}
Response is not ok and get this error:
06-17 02:28:53.456 D/Volley: [1296] BasicNetwork.logSlowRequests: HTTP response for request=<[ ] MY_URL 0xa64d9c1b NORMAL 1> [lifetime=3645], [size=60], [rc=200], [retryCount=0]
06-17 02:28:53.466 D/ATA Response: RESPONSE ERROR
06-17 02:28:53.466 E/Volley: [1] 2.onErrorResponse: Error:
What's wrong with my code?
Try sending a content type header. application/json should do the job.
header("Content-type:application/json");
send header before producing any outputs (i.e before echo statement).

System.Reflection.TargetInvocationException when trying to GET a json object web api

I'm trying to send an object type UserEntry to the client.
the route I used was: http://localhost:3027/api/userapi/getinfo?username=myUsername
What is the cause of this error or what is wrong with my code?
[HttpGet, Route("api/userapi/getinfo")]
public async Task<string> getUserInfo([FromUri]string username)
{
UserEntry u = await UserEntry.getUserInfo(username);
return new JavaScriptSerializer().Serialize(u);
}
Here is what inner exception shows:
InnerException: {
Message: "An error has occurred.",
ExceptionMessage: "Invalid operation. The connection is closed.",
ExceptionType: "System.InvalidOperationException",
StackTrace: " at System.Data.SqlClient.SqlConnection.GetOpenConnection() at System.Data.SqlClient.SqlConnection.get_ServerVersion()"
}
I checked and made sure that there was no error in connecting with the database, but it still shows error.
I temporarily solved it by making it synchronous
I solved it by making it synchronous
[HttpGet,Route("api/userapi/getinfo")]
public SimpleUser getUserInfo([FromUri]string username)
{
var ur = new UserRepository();
return ur.getUser(username).First();
}
public IEnumerable<SimpleUser> getUser(string username)
{
UserEntryDBContext context = new UserEntryDBContext();
UserEntry u = context.Users.Where( x => x.username == username).FirstOrDefault();
List<SimpleUser> s = new List<SimpleUser>();
s.Add(new SimpleUser(u));
return s;
}
but I still have no idea what causes the error nor how can I make it asynchronous.

'System.IO.FileNotFoundException' after calling RfcommDeviceService.FromIdAsync(...)

I'm trying to make a function to connect to a specific Bluetooth device. I'm somewhat sure the DeviceInformation parameter is valid so the issue should be just contained to the function below. A short period of time after the line RfcommDeviceService.FromIdAsync(...) I will see A first chance exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.ni.dll in the Output in Visual Studio and then see The program '...' has exited with code -1 (0xffffffff).. Additionally, the exception is not being caught by try{} catch(Exception e){} so that might mean there is an issue elsewhere.
public async Task<bool> Connect(DeviceInformation deviceInformation)
{
try
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
rfcommService = await RfcommDeviceService.FromIdAsync(deviceInformation.Id);
});
System.Diagnostics.Debug.WriteLine("edfdshjkfdsklfdjslkf");
if (rfcommService == null)
{
return false;
}
System.Diagnostics.Debug.WriteLine(rfcommService.Device.ToString());
await streamSocket.ConnectAsync(
rfcommService.ConnectionHostName,
rfcommService.ConnectionServiceName);
dataReader = new DataReader(streamSocket.InputStream);
dataWriter = new DataWriter(streamSocket.OutputStream);
return true;
}
catch (Exception e)
{
Debug.WriteLine("Exception while connecting: " + e.Message);
Debug.WriteLine(e.StackTrace);
return false;
}
}
I also have the following Capabilities in Package.appxmanifest:
<Capabilities>
<Capability Name="internetClientServer" />
<DeviceCapability Name="proximity" />
<m2:DeviceCapability Name="bluetooth.rfcomm">
<m2:Device Id="any">
<m2:Function Type="name:serialPort" />
</m2:Device>
</m2:DeviceCapability>
</Capabilities>
Turns out the DeviceInformation of making a Bluetooth connection is for WinRT desktop/tablet and not phone. The solution was to use a PeerInformation method.
The function now looks like the following:
public async Task<bool> Connect(PeerInformation peerInfo)
{
streamSocket = new StreamSocket();
try
{
await streamSocket.ConnectAsync(peerInfo.HostName, "{00001101-0000-1000-8000-00805f9b34fb}");
}
catch (System.Exception)
{
return false;
}
return true;
}
Writing can be done using await streamSocket.OutputStream.WriteAsync(rawMessage.AsBuffer());
. Reading I still haven't figured out how to do yet but the issue I was having with this question was resolved by the above.