How to better troubleshoot this 500 Error in MVC Web API - asp.net-mvc

I have an MVC Web API project that I am working on. I created a controller with an action. I am able to hit the action properly using Postman, but when an external system tries to reach my controller, it gets a 500 error. The owner of the external service cannot give me any details beyond that, they can only retry the request.
Here is one of the log entries of their requests in IIS log
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
2017-02-15 20:38:58 192.168.2.34 POST /Route/to/actionName 8002 - 192.168.2.37 Apache-HttpClient/4.5.2+(Java/1.8.0_102) - 500 0 0 146
First I thought may be the action is being hit, so I added an exception handler and added logging.
[Route("actionName")]
[HttpPost]
public IHttpActionResult actionName(MessageModel message)
{
try
{
// code to handle the action
}
catch (Exception e)
{
// Code to log exception in the log file
}
}
Tried above and saw nothing in the log, I have run tests for failed requests to make sure the above exception handler logs and it does.
So the next thing I decided to do was to handle application level errors in Global.asax and log exception there.
protected void Application_Error(object sender, EventArgs e)
{
if (Request.HttpMethod == "POST")
{
var request = SomeMethodToReadRequestContentsInString();
var service = new SomeExceptionLoggingService();
var exception = Server.GetLastError();
if (exception == null)
{
exception = new ApplicationException("Unknown error occurred");
}
service.LogException(exception, Request.UserHostAddress, Request.UserAgent, request);
}
}
And to my surprise, nothing in the log file.
So then I decided to log ALL Post requests and see if I register ANYTHING in the log.
protected void Application_EndRequest(object sender, EventArgs e)
{
if (Request.HttpMethod == "POST")
{
var request = Helper.ReadStreamUnknownEncoding(Request.InputStream);
var service = new InterfaceTestingService();
var exception = Server.GetLastError();
if (exception == null)
{
exception = new ApplicationException("No Error in this request");
}
service.LogException(exception, Request.UserHostAddress, Request.UserAgent, request);
}
}
And again, nothing!
How do I catch this bug? My goal is to see the Content-Type, and contents.
I tried to add a Custom Field in IIS log settings to include `Content-Type', but the log files still don't have that.

I added a handler for Application_BeginRequest logging everything I did in Application_EndRequest. And it turns out, the content-length was zero, and there was no content. I also restarted IIS Web Server to get it to log custom fields too.
What's strange is that if I send empty content through Postman, I get the action code executed but for some reason when they do it, it doesn't.

Related

Why am I not getting redirected to logoutSuccessUrl?

This is causing me quite a headache. All I find is how to disable the redirect.
I'm trying to do the opposite and can't seem to figure out how!
What I'm trying to achieve is: after a successful logout, redirect the user to another page (be that the login page or a logout success page, whatever).
What happens is: after a successful logout, I stay on the same page, even though I can see the correct response under network the network tab of the developer tools.
Here's what I currently have:
security config:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf(c -> c.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.oauth2Login()
.loginPage("/login").permitAll()
.and()
.logout()
.logoutSuccessUrl("/logged-out").permitAll()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
}
controller method:
#GetMapping("logged-out")
public ModelAndView loggedOut(ModelMap model) {
return new ModelAndView("logged-out", model);
}
js script hooked to the button:
let logout = function() {
let request = new XMLHttpRequest();
request.open("POST", "/logout");
request.setRequestHeader("X-XSRF-TOKEN", Cookies.get('XSRF-TOKEN'));
request.setRequestHeader("Accept", "text/html");
request.send();
}
the log events after clicking the button:
DEBUG 15539 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : GET "/logged-out", parameters={}
DEBUG 15539 --- [nio-8080-exec-9] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.hamargyuri.petprojectjava2021.controller.HomeController#loggedOut(ModelMap)
DEBUG 15539 --- [nio-8080-exec-9] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html]
DEBUG 15539 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Completed 200 OK
and here's what I see in the browser
I'm sure there must be some very stupid thing I'm missing here, but I'm at the stage of banging my head to the wall, so I'd appreciate any guidance!
So, it seems like my frontend code was at fault, as it's the one that should do something if the logout call was successful.
I know it's very basic, but this is what I've changed in the js:
let goHome = function() {
window.location.href = "/";
}
let logout = function() {
let request = new XMLHttpRequest();
request.open("POST", "/logout");
request.setRequestHeader("X-XSRF-TOKEN", Cookies.get('XSRF-TOKEN'));
request.onload = goHome;
request.send();
}
I also removed the explicit logoutSuccessUrl, the default is fine, all I need a successful response, then the above function will take me "home".

ActiveMQ test connection

I am trying to test ActiveMQ connection and return a value. it crashes on line:
httpResponse = client.execute(theHttpGet);
It is not my code I am trying to debug it. Can anyone help me to understand why the code is using HttpGet?
public ActivemqBrokerInfo(String serverAddress, int port, String apiUrl, int timeout) {
// Default Activemq location
this.serverAddress = String.format("http://%s:%s/%s", serverAddress, port, apiUrl);
int timeoutInMs = timeout;
HttpClientBuilder builder = HttpClientBuilder.create();
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeoutInMs).build();
builder.setDefaultRequestConfig(requestConfig);
client = builder.build();
}
public ActivemqBrokerInfo(String serverAddress) {
this(serverAddress, DEFAULT_PORT, DEFAULT_API_URL, DEFAULT_TIMEOUT);
}
#Override
public boolean testConnection() {
HttpGet theHttpGet = new HttpGet(serverAddress);
theHttpGet.addHeader("test-header-name", "test-header-value");
HttpResponse httpResponse = null;
try{
httpResponse = client.execute(theHttpGet);// Code is crashing on this line
} catch (IOException ex){
LOGGER.error("Broker down: ", ex);
}
return httpResponse != null;
}
When ActiveMQ runs is normally starts an embedded web server. This web server is used to host the web admin console as well as the Jolokia endpoint which acts as an HTTP facade in front of the broker's MBeans. In other words, any client can send HTTP requests to specially formed URLs on the broker to get results from the underlying management beans. This is exactly what your bit of code appears to be doing. It appears to be sending an HTTP request to the Jolokia endpoint (i.e. api/jolokia) in order to determine if the broker is alive or not.
Based on the information provided it is impossible to determine why testConnection() is not returning successfully since you've included no information about the configuration or state of the broker.
I recommend you add additional logging to see what may be happening and also catch Exception rather than just IOException.

No errors are being raised when unsuccessfully writing to Azure service bus

When writing a message to the Azure Service Bus (using Microsoft.Azure.ServiceBus standard library, not the .Net Framework version) it works fine. However, when switching networks to a network that blocks that traffic and running it again I would expect an error being raised by SendAsync yet no error is thrown, therefor the function considers the send successful even though it is not.
Am I missing some logic to make sure that errors do get raised and trapped, it seems to be inline with all the examples I have seen.
I have tried this possible solution ..
Trouble catching exception on Azure Service Bus SendAsync method
.ContinueWith(t =>
{
Console.WriteLine(t.Status + "," + t.IsFaulted + "," + t.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);
.. and at no point does ContinueWith get hit.
[HttpPost]
[Consumes("application/json")]
[Produces("application/json")]
public ActionResult<Boolean> Post(Contract<T> contract)
{
Task.Run(() => SendMessage(contract));
// Other stuff
}
private async Task<ActionResult<Boolean>> SendMessage(Contract<T> contract)
{
JObject json = JObject.FromObject(contract);
Message message = new Message();
message.MessageId = Guid.NewGuid().ToString();
message.ContentType = ObjectType;
message.PartitionKey = ObjectType;
message.Body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(contract));
foreach (KeyValuePair<String, String> route in DataRouting)
{
JToken jToken = json.SelectToken(route.Value);
if (jToken != null)
{
message.UserProperties[route.Key] = jToken.Value<String>();
}
else
{
String routeError = $"Could not find routing information in request for: {route.Key} in {route.Value}";
Logger.LogError(routeError);
return new UnprocessableEntityObjectResult(routeError);
}
}
// Send the message
try
{
await topicClient.SendAsync(message);
}
catch(Exception ex)
{
return new UnprocessableEntityObjectResult($"'Could not transmit message to service bus - {ex.Message}'");
}
return new OkObjectResult(true);
}
I expect that the error trap would be hit if the SendAsync fails to send the message. However it essentially fire and forgets, the message send is blocked by the firewall but is never reported to the caller by throwing an error.
Ok, found the answer, but I will leave this out there in case anyone else does this to themselves. It was down to my general muppetry when putting the MVC Controller together. Set async on the Post action and configure the await on the send. Obvious really but I missed it.
public virtual async Task<ActionResult<Boolean>> Post(Contract<T> contract){}
...
// Send the message
try
{
await topicClient.SendAsync(message).ConfigureAwait(false);
return new OkObjectResult(true); // Success if we got here
}
catch(Exception ex)
{
return new UnprocessableEntityObjectResult($"'Could not transmit message to service bus - {ex.Message}'");
}

exception inside of OnException

I created a custom attribute, inheriting from HandleErrorAttribute:
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
try
{
Utility.LogAndNotifyOfError(filterContext.Exception, null, true);
}
catch(Exception ex)
{
filterContext.Exception = ex;
}
}
}
, and then registered with:
filters.Add(new CustomHandleErrorAttribute());
This has always worked as intended. However a common problem with my log method is that it uses a custom event log source when writing to the event log, which the app pool account typically doesn't have the permissions to create. Creating the event log source is a simple powershell script, however I wanted to actually include that tidbit in the error:
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
The problem is that the catch in the OnException is never hit. When debugging, the custom error I throw from LogAndNotifyOfError instead triggers a second call to OnException, and the detail of my ErrorHandlerException is never seen. I want the asp.net error page that comes up to be with my custom error detail rather than the SecurityException that was originally raised.
You can even see the surrounding try in the displayed error:
Edit: Entire log method listed:
public static void LogAndNotifyOfError(Exception ex, String extraInfo, Boolean sendEmail)
{
//if the error handler itself faulted...
if (ex is ErrorHandlerException)
return;
string eventLogName = "Application";
string eventLogSource = "MySourceName";
String error = ex.ToString();
if (error.Length > 28000)
error.Substring(0, 28000);//event log is limited to 32k
error += "\r\n\r\nAdditional Information: \r\n"
+ "Machine Name: " + Environment.MachineName + "\r\n"
+ "Logged in user:" + App.CurrentSecurityContext.CurrentUser?.UserId + "\r\n"
+ extraInfo + "\r\n";
EventLog log = new EventLog(eventLogName);
log.Source = eventLogSource;
try
{
log.WriteEntry(error, EventLogEntryType.Error);
}
catch(SecurityException ex1)
{//this doesn't work - for some reason, OnError still reports the original error.
throw new ErrorHandlerException($"The event log could not be written to due to a SecurityExcption. The likely issue is that the '{eventLogSource}' does not already exist. Please run the following powershell command:\r\n"
+ $"New - EventLog - LogName Application - Source {eventLogSource}", ex1);
}
//if the email-to field has been set...
if (!String.IsNullOrEmpty(App.Config.General.ErrorHandlerSendToAddresses) && sendEmail)
{
//...then send the email
MailMessage email = new MailMessage();
email.To.Add(App.Config.General.ErrorHandlerSendToAddresses);
email.IsBodyHtml = false;
email.Subject = String.Format("Error in {0}", eventLogSource);
email.Body = email.Subject + "\r\n\r\n"
//+ "Note: This error may be occuring continuously, but this email is only sent once per hour, per url, in order to avoid filling your mailbox. Please check the event log for reoccurances and variations of this error.\r\n\r\n"
+ "The error description is as follows: \r\n\r\n"
+ error + "\r\n\r\n";
SmtpClient smtp = new SmtpClient();
smtp.Send(email);
}
}
I figured it out (sort of). It would appear that when the newly throw exception has an inner exception, it is only displaying that inner exception. It does not matter what the type is on the outer or inner exception.

How to retrieve exact reason of the error from async HttpRequest?

I am trying to figure out how to find out exact reason of (async) HttpRequest (from 'dart:html') failure, and, to be honest, I am a bit lost here.
The onError callback receives only HttpRequestProgressError object, which doesn't have anything useful, and the HttpRequest object itself has "status" set to "0" in case of failure, even console shows "Failed to load resource" with no details.
What I want is to know the exact reason - like "connection refused" or "host name not resolved".
Is this possible at all?
Thank you!
Unfortunately, there is no property to report the error as detailed as you'd like. The reason is that JavaScript doesn't support this.
There are the properties status and statusText on the HttpRequest object (which you could get from your HttpRequestProgressEvent with evt.target, but those represent HTTP status codes. Every other error has the status code 0 - request failed. This could be anything, and the only place to look at is the browser's console, because this is an Exception thrown by the browser.
If your request was synchronous, you could surround the send() with a try-catch. If your request is async, this won't work.
See here
#library('Request');
#import('dart:html');
#import("dart:json");
typedef void RequestHandler(String responseText);
typedef void ErrorHandler(String error);
class ResourceRequest {
XMLHttpRequest request;
RequestHandler _callbackOnSuccess;
ErrorHandler _callbackOnFailure;
ResourceRequest.openGet(String url, RequestHandler callbackOnSuccess, [ErrorHandler callbackOnFailure])
: request = new XMLHttpRequest(),
_callbackOnSuccess = callbackOnSuccess,
_callbackOnFailure = callbackOnFailure {
request.open("GET", url, async : true);
request.on.loadEnd.add((XMLHttpRequestProgressEvent e) => onLoadEnd(e));
}
void send() {
request.send();
}
void onLoadEnd(XMLHttpRequestProgressEvent event) {
if (request.readyState == 4 && request.status == 200) {
_callbackOnSuccess(request.responseText);
} else if (_callbackOnFailure != null) {
_callbackOnFailure(request.statusText);
}
}
}

Resources