I need to log exceptions and bad requests in my API. Currently I am using try catch to catch the exception and add to my logs in the catch block. Is this the right way? I read about Global Error Handling in ASP.NET. How can I implement that approach for this case?
Below is my API Controller example:
[HttpPost]
[Authorize]
[ValidateModel]
[Route("CheckProgramOwner")]
public async Task<IHttpActionResult> CheckProgramOwner([FromBody] CheckProgramOwner _data)
{
try
{
using (var db = new VisualVoiceFlowEntities())
{
var Result= await db.VVF_ScriptFlow.Where(s => s.ProgramId == _data.ProgramId).OrderByDescending(s => s.ID).FirstOrDefaultAsync();
if(Result== null)
{
Log.Error("Error in CheckProgramOwner POST Request - " + "ProgramId not found");
return Content(HttpStatusCode.BadRequest, "ProgramId not found");
}
string CurrentOwner = Result.ReadBy.ToString();
return Ok(CurrentOwner);
}
}
catch (Exception ex)
{
Log.Error("Error in CheckProgramOwner POST Request - " + ex.Message, ex);
NewRelic.Api.Agent.NewRelic.NoticeError("Error in CheckProgramOwner POST Request - " + ex.Message, null);
return Content(HttpStatusCode.InternalServerError, "Internal Server Error. Please Contact Admin.");
}
}
If you read the document previously posted by Casey, you will find a link to the following document, which explains how to implement and register an exception filter globally:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling#registering_exception_filters
You could then implement your logging logic in the filter's body thus avoiding having to repetitively log errors on each try/catch. I would suggest logging the more obvious errors using your original approach and use the filter to log any other errors (that you might not expect.)
I did it using ExceptionFilter.
I created Exception Filter Class as below -
public class MyExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
log4net.ThreadContext.Properties["addr"] = HttpContext.Current.Request.UserHostAddress;
log4net.ThreadContext.Properties["Hostname"] = Dns.GetHostName().ToString();
log4net.ThreadContext.Properties["PCName"] = Dns.GetHostAddresses(Environment.MachineName)[0].ToString();
string RequestMethod = context.Request.Method.Method;
dynamic ControllerInfo = context.ActionContext.ControllerContext.Controller;
string RequestName = ControllerInfo.Url.Request.RequestUri.LocalPath.ToString().Replace("/api/", "").Replace("/VVFAPI", "");
Log.Error("Error in " + RequestName +" "+ RequestMethod+ " Request - " + context.Exception.Message, context.Exception);
NewRelic.Api.Agent.NewRelic.NoticeError("Error in " + RequestName + " " + RequestMethod + " Request - " + context.Exception.Message, null);
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("Internal Server Error. Please Contact Admin."),
ReasonPhrase = "Critical Exception."
};
context.Response = msg;
}
}
Also, I changed my controller accordingly
[HttpPost]
[Authorize]
[ValidateModel]
[MyExceptionFilter]
[Route("CheckProgramOwner")]
public async Task<IHttpActionResult> CheckProgramOwner([FromBody] CheckProgramOwner _data)
{
Log.Info("CheckProgramOwner POST Request");
using (var db = new VisualVoiceFlowEntities())
{
var Result = await db.VVF_ScriptFlow.Where(s => s.ProgramId == _data.ProgramId).OrderByDescending(s => s.ID).FirstOrDefaultAsync();
if (Result == null)
{
Log.Error("Error in CheckProgramOwner POST Request - " + "ProgramId not found");
return Content(HttpStatusCode.BadRequest, "ProgramId not found");
}
string CurrentOwner = Result.ReadBy.ToString();
return Ok(CurrentOwner);
}
}
Related
Good day Someone.
I am new to asp.net mvc.
I created a simple web application using asp.net mvc.
The application is used to upload image from to a folder. The application works well in visual studio, but once i publish and put on iis it does not upload the image.
I am thinking if there is way to debug the published version so that i can get where the issued is ?
Kindly help on how i can debug the published version and how to solve the problem .
Below is where it is catching the error
string path = System.IO.Path.Combine(targetLocation, file.FileName);
I Would suggest doing the debugging for ASP.NET MVC Application to follow the below points.
Server-side - You should use do exception filter to write any issues to text files and track it
Front - End - the developer tool in the browser level for javascript related things.
DataBase Side - You can use the SQL profiler to use catch the server-side debugging.
add try catch block in your codes
and in the catch code log the exception error message into a file in server to see what is the exact error as following:
add this model to your solution under "Models" folder or anywhere
public class ExceptionLogger
{
private string sLogFormat;
public void ErrorLog(string sErrMsg)
{
try
{
string LogDirectory = "C:\LogFiles\";
CheckCreateLogDirectory(LogDirectory);
LogDirectory = (LogDirectory + "Log_" + DateTime.Now.ToString("dd_MM_yyyy", new CultureInfo("en-us")) + ".txt");
sLogFormat = DateTime.Now.ToString("dd/MM/yyyy", new CultureInfo("en-us")) + " " + DateTime.Now.ToString("HH:mm:ss", new CultureInfo("en-us")) + " ==> ";
StreamWriter sw = new StreamWriter(LogDirectory, true);
sw.WriteLine(sLogFormat + sErrMsg);
sw.Flush();
sw.Close();
}
catch (Exception e) {
}
}
private bool CheckCreateLogDirectory(string LogPath)
{
bool loggingDirectoryExists = false;
DirectoryInfo oDirectoryInfo = new DirectoryInfo(LogPath);
if (oDirectoryInfo.Exists)
{
loggingDirectoryExists = true;
}
else
{
try
{
Directory.CreateDirectory(LogPath);
loggingDirectoryExists = true;
}
catch
{
throw new Exception();
// Logging failure
}
}
return loggingDirectoryExists;
}
}
Then in your controller or repository where errors may happen do the following:
a- add this code in top:
ExceptionLogger Err = new ExceptionLogger();
//you need to write "using" in top to refactor the import error
// something like this: using YourProjectName.Models;
b- in your code blocks add try catch block
try{
//your code
}catch(Exception ex){
Err.ErrorLog(ex.Message + " --- , ---- " + ex.InnerException);
}
another way is to add OnException event on your BaseController which will detect any error or exception, hence you can log it without try-catch blocks, do it as following:
define this in your baseController: ExceptionLogger Err = new ExceptionLogger(); with its using then add the event:
protected override void OnException(ExceptionContext filterContext)
{
var isSent = false;
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
{
return;
}
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = filterContext.Exception.Message
}
};
LogError("Controller: AjaxCall" + " Action: AjaxCall" + filterContext.Exception.Message, filterContext.Exception);
}
else if (filterContext.Exception is HttpAntiForgeryException)
{
Response.Clear();
Server.ClearError(); //make sure you log the exception first
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { action = "Logout", controller = "Account" }));
}
else
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
if (actionName == "_Menu")
{
isSent = true;
Response.Redirect("~/Account/Logout");
}
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Error/Error.cshtml",
MasterName = null,
ViewData = new ViewDataDictionary(model),
TempData = filterContext.Controller.TempData
};
////Redirect or return a view, but not both.
LogError("Controller: " + controllerName + " Action: " + actionName + filterContext.Exception.Message, filterContext.Exception);
}
// log the error by using your own method
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
if (isSent == false)
{
filterContext.HttpContext.Response.StatusCode = 500;
}
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
private void LogError(string message, Exception exception)
{
Err.ErrorLog(message + " && " + exception.Message);
}
now when errors happen then you will go to c:/logfiles folder in the server and find the exact error happened
I am intentionally making a violation of a unique constraint in my database, and trying to handle an exception.
This among else is in my form:
HttpResponseMessage response = KorisniciService.PostResponse(k);
if (response.IsSuccessStatusCode)
{
MessageBox.Show(Messages.add_usr_succ);
DialogResult = DialogResult.OK;
Close();
}
else
{
string message = response.ReasonPhrase;
if (string.IsNullOrEmpty(Messages.ResourceManager.GetString(response.ReasonPhrase)))
message = Messages.ResourceManager.GetString(response.ReasonPhrase);
MessageBox.Show("Error code: " + response.StatusCode + " Message: " + message);
}
My controller:
public IHttpActionResult PostKorisnici(Korisnici obj)
{
if (!ModelState.IsValid)
return BadRequest();
try
{
obj.KorisnikId = Convert.ToInt32(dm.esp_Korisnici_Insert(obj.Ime, obj.Prezime, obj.Email, obj.Telefon, obj.KorisnickoIme, obj.LozinkaSalt, obj.LozinkaHash, obj.Status, obj.Adresa, obj.GradId).FirstOrDefault());
}
catch (EntityException ex)
{
throw CreateHttpResponseException(Util.ExceptionHandler.HandleException(ex), HttpStatusCode.Conflict);
}
foreach (var item in obj.Uloge)
{
dm.esp_KorisniciUloge_Insert(obj.KorisnikId, item.UlogaId);
}
return CreatedAtRoute("DefaultApi", new { id = obj.KorisnikId }, obj);
}
HttpResponseException making function:
private HttpResponseException CreateHttpResponseException(string reason, HttpStatusCode code)
{
HttpResponseMessage msg = new HttpResponseMessage()
{
StatusCode = code,
ReasonPhrase = reason,
Content = new StringContent(reason)
};
return new HttpResponseException(Request.CreateResponse(msg));
}
Exception handler class:
public class ExceptionHandler
{
public static string HandleException(EntityException error)
{
SqlException ex = error.InnerException as SqlException;
switch (ex.Number)
{
case 2627:
{
return GetConstraintExceptionMessage(ex);
}
default:
return error.Message + "(" + error +")";
}
}
/*Message "Violation of UNIQUE KEY constraint 'CS_KorisnickoIme'. Cannot insert duplicate key in object 'dbo.Korisnici'. The duplicate key value is (farish).\r\nThe statement has been terminated." string*/
private static string GetConstraintExceptionMessage(SqlException error)
{
string newMessage = error.Message;
int startIndex = newMessage.IndexOf("'");
int endIndex = newMessage.IndexOf("'", startIndex + 1);
if (startIndex>0 && endIndex>0)
{
string constraintName = newMessage.Substring(startIndex + 1, endIndex - startIndex - 1);
if (constraintName == "CS_KorisnickoIme")
newMessage = "username_con";
else if (constraintName == "CS_Email")
newMessage = "email_con";
}
return newMessage;
}
So when I produce an error, instead of a popup window (which shows up fine in a tutorial video) I get a System.Web.Http.HttpResponseException in a first catch block of my post method and nothing passed back to my form.
I think because the exception is being thrown and not inside a try/catch block, or the catch block receiving the CreateHttpResponseException, is absorbing it and not providing a response object.
EDIT
Can you post the code for KorisniciService.PostResponse?
and nothing passed back to my form
And what is the eventual result? From the form code you have posted it should either popup message box with success message, or popup message box with fail message. What actually happens?
2nd EDIT
Following further information, use this in your form code...
try
{
HttpResponseMessage response = KorisniciService.PostResponse(k);
if (response.IsSuccessStatusCode)
{
MessageBox.Show(Messages.add_usr_succ);
DialogResult = DialogResult.OK;
Close();
}
}
catch(HttpResponseException ex)
{
string message = ex.ReasonPhrase;
if (string.IsNullOrEmpty(Messages.ResourceManager.GetString(ex.ReasonPhrase)))
message = Messages.ResourceManager.GetString(ex.ReasonPhrase);
MessageBox.Show("Error code: " + ex.StatusCode + " Message: " + message);
}
I'm having an issue with sending a POST request for Pinterest in a UWP app I'm working on. I already have the access code from a previous WebAuthenticationBroker function. I've tried using the WebAuthenticationBroker with the UseHttpPost option under options with authenticating async, but, as I've provided my if functions, it returns ERROR. I just get a "message": "405: Method Not Allowed" and "type" : "http". I've looked all over, I've even tried using an HttpClient and PostAsync(), but I couldn't get it to get the access token. Any advise to what I'm doing wrong?
private void OutputToken(string TokenUri)
{
int tokenString = TokenUri.IndexOf('=');
string TheTokenUri = TokenUri.Substring(tokenString + 54);
PinterestReturnedTokenText.Text = TheTokenUri;
outputToken = TheTokenUri;
}
private async void auth()
{
try
{
string CallbackUrl = "https://localhost/";
string PinterestUrl = "https://api.pinterest.com/v1/oauth/token?grant_type=authorization_code&client_id=" + PinterestClientID + "&client_secret=" + PinterestClientSecret + "&code=" + outputCode;
Uri StartUri = new Uri(PinterestUrl);
Uri EndUri = new Uri(CallbackUrl);
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseHttpPost, StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
OutputToken(WebAuthenticationResult.ResponseData.ToString());
await GetPinterestNameAsync(WebAuthenticationResult.ResponseData.ToString());
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
OutputToken("HTTP Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseErrorDetail.ToString());
}
else
{
OutputToken("Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseStatus.ToString());
}
}
catch (Exception Error)
{
PinterestReturnedTokenText.Text = "ERROR";
}
}
I am a newbie to C# & am trying to use the Lync SDK to search for a Lync user programmatically to get their status. I am not sure how to pass the results to the webservice response when the async callback gets executed.
This is the code:
Webservice Controller GET endpoint:
// GET: api/Lync/5
public String Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
lync.Search("medina");
//lync.Search("medina",
//(lyncContacts) =>
//{
// log.Debug("Search Results Callback fired!");
// log.Info("Results found: " + lyncContacts.Count);
// return lyncContacts.ToString();
//});
//Console.WriteLine(name);
//Console.ReadLine();
return "testUser";
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
The above method calls the business service lync.search(..) which is as follows:
public void Search(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
//List<ContactInformationType> ContactInformationList = new List<ContactInformationType>();
//ContactInformationList.Add(ContactInformationType.Activity);
//ContactInformationList.Add(ContactInformationType.Availability);
// ContactInformationList.Add(ContactInformationType.CapabilityString);
//ContactSubscription contactSubscription = LyncClient.GetClient().ContactManager.CreateSubscription();
Console.WriteLine("Searching for contacts on " + searchKey);
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
if (searchResults.Contacts.Count > 0)
{
log.Info("Search results found: " + searchResults.Contacts.Count);
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
log.Debug("Display Name: " + displayName);
log.Debug("Availability: " + currentAvailability);
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
//done(contactList);
}
return;
}
else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
} else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
}
I tried to use Task+await but I am having a hard time trying to figure out how can i return the results from the callback funtion as results from the search method. How do i capture this in the controller class?
If you would like to use the async/await pattern for this use Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx
Google for examples. Like this:
public async Task<List<LyncContact>> SearchAsync(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
Console.WriteLine("Searching for contacts on " + searchKey);
var cm = LyncClient.GetClient().ContactManager;
var searchResults = await Task<SearchResults>.Factory.FromAsync<String>(
cm.BeginSearch,
cm.EndSearch, searchKey, null);
if (searchResults.Contacts.Count > 0)
{
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
}
}
return contactList
}
In the controller you then can do:
public async Task<String> Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
var lyncContacts = await SearchAsync("medina");
log.Info("Results found: " + lyncContacts.Count);
return lyncContacts.ToString();
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
Or you can use a TaskCompletionSource, see http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html (older post but principle is still valid.
Warning
I cannot compile this since I do not have access to the libraries you use. It should work but there might be some compile errors
What does it do
Callbacks are not a nice model to work with, as you have found out. using Tasks and the async/await model are far easier to work with. Luckily for you and me they have created several methods that can act as a bridge between the old and the new model. Using Task.FromAsync you can transform IAsyncResult methods to an easier to use Task based methodology. Stephen Cleary has written some nice blogs about them. See the post I mentioned earlier.
It looks like, by looking at your commented out code, that you tried to provide some sort of callback to your Search function. That would work, it should be of type Action<Microsoft.Lync.Model.SearchResults>
public void Search(string searchKey, Action<SearchResults> callback)
{
....
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
callback(searchResults);
});
....
}
Then just uncomment the code in your controller:
lync.Search("medina",
(lyncContacts) =>
{
log.Debug("Search Results Callback fired!");
log.Info("Results found: " + lyncContacts.AllResults.Count);
});
I call a a methode by HttpPost in my ServiceController from my View http://localhost/Case/Form
My serviceController class:
using System.Web;
using System.Web.Mvc;
[...]
namespace ApnNephro.WebUI.Controllers
{
public class ServicesController : Controller
{
public string GetDomain()
{
string url = "";
try
{
string _url = Request.Url.AbsolutePath;
int _si = (_url.IndexOf("//"));
int _fi = (_url.Substring(_si + 2)).IndexOf("/"); // first /
url = _url.Substring(0, _fi + _si + 2);
return url.ToString();
}
catch (InvalidOperationException ioe)
{
throw new Exception(ioe.Message + " / " + ioe.StackTrace);
}
catch (Exception e)
{
throw new Exception(e.Message + " / " + e.StackTrace);
}
return url.ToString();
}
[...]
And i call him in my MailHelper.cs :
private static string domain = new ServicesController().GetDomain();
But an exception occure because Request is null...
So, my Question is, why Request is null ?
Is it assigned only in the current view controller, so here in my CaseController only ?
You are creating a new controller, that is not associated with a request.
Instead of putting the get domain in a controller and then creating a new one,
You can just access the current request via the HttpContext.Current.Request.
This is also available in a static method.