Skip to content
This repository has been archived by the owner on Jan 24, 2021. It is now read-only.

Add content negotiation to the default error handler #1911

Closed
wants to merge 1 commit into from
Closed

Add content negotiation to the default error handler #1911

wants to merge 1 commit into from

Conversation

jonathanfoster
Copy link

...r now supports application/json, application/xml, and text/html. The default content type is text/html when all media types are supported (/). If a non-supported media type is provided then the response status code will be 406 not acceptable.

I made a change to ContextExtensions.GetExceptionDetails that could be considered breaking. I changed the method to return string.Empty instead of "None" when no exception is found. I didn't see any other usages in the solution, but I could see a situation where a consumer of the library has a check for "None" that will now fail. I don't think it's a huge concern, but something to consider. I added a unit test to ensure the method returns string.Empty in the future. If there's an issue with this change then I could always revert it. I just thought that string.Empty is a more idomatic return value in this situation.

@jonathanfoster
Copy link
Author

@thecodejunkie: Any feedback on this PR? This resolves issue #1905.

@khellang
Copy link
Member

khellang commented May 3, 2015

@jonathanfoster Currently you're duplicating a lot of code from the IResponseNegotiator service. Would it be possible to use that instead? The upside is that you'd then be able to leverage custom formatters etc. for errors 😄

@jonathanfoster
Copy link
Author

Duplicate code is no bueno. I'll dive into the Content Negotiation wiki page so I can get a better idea of where the IResponseNegotiator fits in.

@thecodejunkie
Copy link
Member

@jonathanfoster did you ever resolve the code duplication issue? Thanks

@thecodejunkie thecodejunkie added this to the 1.3 milestone Aug 18, 2015
@thecodejunkie thecodejunkie self-assigned this Aug 18, 2015
@thecodejunkie
Copy link
Member

Ping @jonathanfoster :)

@jonathanfoster
Copy link
Author

I'm not dead yet! No, I haven't resolved the duplication issue yet. It took me some time to grok the code base and I think I have a better plan for integration now. I had done some spikes and I can't remember where I left off before getting pulled into something else. I'll take a look again this weekend to see if I can wrap things up. If not I'll close the PR and let someone else takeover.

@khellang
Copy link
Member

👍

@jonathanfoster
Copy link
Author

So I was able to remove the duplicate code by leveraging the IResponseNegotiator. Now I'm running into some challenges with rendering the default error pages due to the ViewProcessor handling HTML accept headers. I'm looking into the default view engine classes to see if there's an opportunity to short circuit the default view resolution and render a response from the static error files instead.

@jonathanfoster jonathanfoster changed the title Added content negotiation to the default error handler. The error handle... Add content negotiation to the default error handler Aug 30, 2015
@jonathanfoster
Copy link
Author

@thecodejunkie, everything is working now. I wrapped the call to responseNegotiator.NegotiateContent in a try catch and if a ViewNotFound exception is thrown I render a response from the embedded HTML resource files. I had some reservations about this approach since it uses an exception for control flow, but in the end it was the cleanest solution I could find.

This PR now includes 2 potential breaking changes. The DefaultStatusCodeHandler no longer has a parameter-less constructor, the constructor requires an instance of IResponseNegotiator. This may not be an issue since all instances are constructed using the IoC container. The ContextExtension.GetExceptionDetails method now returns string.Empty instead of 'None'. This is a more idiomatic return value especially when viewing the output in JSON or XML.

string errorPage;

if (!this.errorPages.TryGetValue(statusCode, out errorPage))
if (!this.errorMessages.ContainsKey(statusCode) || !this.errorPages.ContainsKey(statusCode))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this condition correct? We bail out if the statusCode is not in both dictionaries. Is that right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. This is a protection against calling this method without first checking HandlesStatusCode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I don't understand that. How is this protecting against calling without first checking the status code?
I read it as ensuring that there is always a message and an error page for the status code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this protects against a status that doesn't have a message and an error page. I was referring to calling code that doesn't check HandlesStatusCode before calling Handle. Then someone could inadvertently pass in a non-supported status.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think I finally understand 😴

@jchannon jchannon modified the milestones: 1.4, 1.3 Sep 14, 2015
/// <summary>
/// Default error handler result
/// </summary>
public class DefaultStatusCodeHandlerResult
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an implementation detail of the DefaultStatusCodeHandler, right? Maybe this should just be a private class?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this an internal class since it's referenced in functional tests.

@horsdal
Copy link
Member

horsdal commented Oct 23, 2015

Replaced by #2081

@horsdal horsdal closed this Oct 23, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants