3

I have a custom ExceptionHandler for my Web API:

public class GlobalExceptionHandler : ExceptionHandler
{
    private class ErrorInformation
    {
        public string Message { get; set; }
        public DateTime ErrorDate { get; set; }
    }

    public override void Handle(ExceptionHandlerContext context)
    {
        context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,
            new ErrorInformation { Message = "We apologize but an unexpected error occurred. Please try again later.", ErrorDate = DateTime.UtcNow }));
    }
}

Here is the controller code

[HttpPost]
public async Task<HttpResponseMessage> UserNameChange(UserNameChangeRequest updateUsersRequest)
{
    throw new Exception("sdfsdf");
}

I am registering it like this

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

        var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        json.SerializerSettings.Converters.Add(new JsonStringEncoder());



        config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
}

If I put a breakpoint, it gets hit just fine. But the output to the client is still HTML, not the expected JSON.

UPDATE: I added the following as a filter attribute, still same result. NO JSON just HTML error page public class CustomExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) {

        var exceptionMessage = actionExecutedContext.Exception.InnerException?.Message ?? actionExecutedContext.Exception.Message;


        var response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError, new { Message = exceptionMessage });

        actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.OK, new {ErrorMessage = actionExecutedContext.Exception.Message},
            new JsonMediaTypeFormatter());


    }
}

I created a new project, added my NewtonSoft JSON config and a customer ErrorAtribute, works as expected.

What in my project could be stopping the error from coming across as JSON? Even when I navigate in a browser to the API endpoint it shows as a generic HTML error but in the sample project with the same code it shows as JSON message I want???

JCircio
  • 525
  • 2
  • 7
  • 18

7 Answers7

0

Would you try to set MediaTypeFormatter to Json;

    context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,
                new ErrorInformation { Message = "We apologize but an unexpected error occurred. Please try again later.", ErrorDate = DateTime.UtcNow }, 
                JsonMediaTypeFormatter.DefaultMediaType));
lucky
  • 12,734
  • 4
  • 24
  • 46
0

Below is my package installed

<packages>
  <package id="Microsoft.AspNet.Cors" version="5.2.3" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebApi" version="5.2.2" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebApi.Cors" version="5.2.3" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.2" targetFramework="net462" />
  <package id="Newtonsoft.Json" version="6.0.4" targetFramework="net462" />
</packages>

it working for me , i.e. returns me json data for precaution you can remove xmlformatter as given below

public static void Register(HttpConfiguration config)
{
    config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
    config.Formatters.Remove(config.Formatters.XmlFormatter);
}

below is handle method

public override void Handle(ExceptionHandlerContext context)
{
   string errorMessage = "An unexpected error occured";
     var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new ErrorInformation() 
                    {
                        Message = "We apologize but an unexpected error occured. Please try again later.",
                        ErrorDate = DateTime.UtcNow 
                    }
                  );
   response.Headers.Add("X-Error", errorMessage);
   context.Result = new ResponseMessageResult(response); 
}

output

enter image description here

Community
  • 1
  • 1
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
  • @PranayI must be doing something wrong, no combination of code gets this to work. I commented out every other filter attribute also. – JCircio Dec 12 '17 at 17:41
  • @JCircio Do you have `Newtonsoft.Json` Nuget package? – Greg Dec 12 '17 at 17:44
  • @JCircio - can you please copy and past my code and have a try – Pranay Rana Dec 12 '17 at 17:45
  • @Greg - yes ...i added that – Pranay Rana Dec 12 '17 at 17:45
  • @PranayRana I know you did, it works. If JCircio didn't then Web Api wouldn't serialize, since Json.Net is the default it relies on. You have to specify to use the built in .Net one. – Greg Dec 12 '17 at 17:46
  • @JCircio - can you please add pacakges as mine...and have try as Greg told – Pranay Rana Dec 12 '17 at 17:47
  • @Pranay adding this breaks all other JSON being returned (Grids, pages, ect) json.UseDataContractJsonSerializer = true; – JCircio Dec 12 '17 at 17:50
  • @JCircio - i am not asking you to add json.UseDataContractJsonSerializer = true;..i am asking you to make use of newtonsoft.json pkg – Pranay Rana Dec 12 '17 at 17:52
  • @PranayRana I am using Newtonsoft.JSON package 10.0.3 What should the register function look like? (Mine is above) – JCircio Dec 12 '17 at 17:55
  • No luck.. nothing seems to make any difference. Obviously something is not right, but I cannot find it. – JCircio Dec 12 '17 at 18:00
  • @JCircio By using Newtonsoft.Json you can remove `UseDataContractJsonSerializer`. My answer recommended it, because it didn't look like you tried default Netwonsoft.Json approach. – Greg Dec 12 '17 at 18:09
  • @Greg thanks.. I added as per suggestion and removed it. no difference – JCircio Dec 12 '17 at 18:09
0

I think you're missing the following line:

json.UseDataContractJsonSerializer = true;

Once you add that line it should correctly serialize into JSON for you. Without the serializer, you set the formatter but it doesn't actually do anything.

Based on the code it looks like you aren't using the default Newtonsoft.Json settings, but rather are geared for the data contract serializer.

Note: This answer is only useful if you don't want to use Newtonsoft.Json as the default serializer.

Greg
  • 11,302
  • 2
  • 48
  • 79
0

Use ExceptionLogger instead, e.g.:

namespace Microsoft.UPP.Calc.WebApi.Logging
{
    public sealed class AiExceptionLogger : ExceptionLogger
    {
        private readonly ITelemetryClient _telemetryClient;

        public AiExceptionLogger(ITelemetryClient telemetryClient)
        {
            _telemetryClient = telemetryClient;
        }

        public override void Log(ExceptionLoggerContext context)
        {
            LogException(context.Exception);
        }

        public void LogException(Exception exception)
        {
            var properties = Enumerable.Zip(exception.Data.Keys.Cast<string>(),
                                            exception.Data.Values.OfType<string>(),
                                            (k, v) => new { Key = k, Value = v })
                                       .Where(x => x.Value != null)
                                       .ToDictionary(x => x.Key, x => x.Value);
            _telemetryClient.TrackException(exception, properties);
        }

        public void LogEvent(string eventName, IDictionary<string, string> properties)
        {
            _telemetryClient.TrackEvent(eventName, properties);
        }
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
0

Here is a nice article that outlines one way to achieve this. I have used this technique in the past and it works great. Search for TextPlainErrorResult.

Global Error Handling in ASP.NET Web API 2

To determine if your app is using custom http errors either open the custom error section under your app in IIS or look in the web.config for a httpErrors section. This construct has been around for a while. If you are carrying fourth a legacy application then look in the web.config for a section as follows:

<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL">
  <clear />
  <error statusCode="404" path="/errors/404.aspx" responseMode="ExecuteURL" />
  <error statusCode="500" path="/errors/500.aspx" responseMode="ExecuteURL" />
  <error statusCode="400" path="/errors/400.aspx" responseMode="ExecuteURL" />
</httpErrors>
Ross Bush
  • 14,648
  • 2
  • 32
  • 55
  • Yes, I can catch the error, but what I want to do is return JSON to the client.my project for some reason is NOT doing this. My test project with the exact filter attibute IS.. – JCircio Dec 12 '17 at 20:31
  • Are you using custom errors to override in IIS in which case you need to specify the response.TrySkipIisCustomErrors somewhere in the pipeline. – Ross Bush Dec 12 '17 at 20:38
  • Not that im aware. But this is a large project started many years ago. Where can I find this? – JCircio Dec 12 '17 at 20:46
  • Ok, I looked into this, we are not doing anything of the sort. But it does make sense, since no matter what I do I will not get JSON but the generic HTML error page – JCircio Dec 12 '17 at 20:53
  • I added the config section to check. – Ross Bush Dec 12 '17 at 20:54
  • Yeah, thats what I was thinking.. I have no such section in my config.. BTW I am using IIS Express just FYI – JCircio Dec 12 '17 at 20:56
  • @JCircio You're configuring and building your `JsonMediaTypeFormatter` in the your global file? – Greg Dec 12 '17 at 21:26
  • @Greg Did you mean to post on my message? – Ross Bush Dec 12 '17 at 21:29
  • @Greg in both the main project and my test project I am doing it in the WebApiConfig.cs file.. I have moved it from there to the global as well with no changes – JCircio Dec 12 '17 at 21:30
  • @Greg Some where some magical "thing" is overriding my code. when I open another tab and navigate to the main page and logout then go back to the tab that is pointed to my endpoint I do in fact get a JSON obejct in the broswer that says {Message: "you are not authorized"} but when I log in and point to the endpoint instead of seeing JSON for the exception I see the HTML Error page. – JCircio Dec 12 '17 at 21:32
  • @JCircio I know by default it defaults to XML, but without more code I'm not sure how much more of a help I'll be. – Greg Dec 12 '17 at 21:37
  • @Greg, What do you need to see? Could StructureMap be doing something? – JCircio Dec 12 '17 at 21:38
  • @JCircio Can you post the code on GitHub and send me Repository? So I can see all the WebApi stuff ? – Greg Dec 12 '17 at 23:38
  • @Greg Bad news is I cannot post much code. Good news is this is going to be related to a nuget package. I added ALL the packages from the main app into the test APP and now the test app fails. Im tracking it down and will know more shortly and post for the next poor soul when I know more – JCircio Dec 12 '17 at 23:54
  • @JCircio Did you pull a .Net Core Nuget package? – Greg Dec 12 '17 at 23:55
0

So, it turns out the culprite was a NuGet Package called "SerilogWeb.Classic.WebApi"

I am not sure exactly what this so called "package" is doing, but seems to be intercepting my ExceptionFilters for WebApi's.

Took an entire day to figure this out, so, I hope this helps the next person with an issue like this.

JCircio
  • 525
  • 2
  • 7
  • 18
  • You might consider marking this as the answer, even if it is your own, due to helping others who stumble upon bit. – Ross Bush Dec 15 '17 at 04:41
  • For future reference : This should now be fixed in SerilogWeb.Classic.WebApi v4.0.5 by this PR : https://github.com/serilog-web/classic-webapi/pull/17 – tsimbalar Feb 28 '19 at 22:00
0

Set IIS in Application pool to use intregated mode App pool screen shot

Mee Tung
  • 1
  • 1