Skip to content

Add extensible handlers to DeveloperExceptionPage #8536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
2 tasks done
DamianEdwards opened this issue Mar 14, 2019 · 16 comments
Closed
2 tasks done

Add extensible handlers to DeveloperExceptionPage #8536

DamianEdwards opened this issue Mar 14, 2019 · 16 comments
Assignees
Labels
area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlewares Done This issue has been fixed enhancement This issue represents an ask for new feature or an enhancement to an existing one

Comments

@DamianEdwards
Copy link
Member

DamianEdwards commented Mar 14, 2019

Add extensibility point to sub-systems to add handlers via DI that get inspected when an exception occurs. The handler can choose to render a response (e.g. the current EF diagnostic middleware page would change to this) or pass-through (do nothing). The middleware would fallback to rendering an HTML error page if no handler matched and the client sent an Accept header that included text/html, otherwise it would just return the error as plain text. For API responses, MVC would register a handler that formats a ProblemDetails with the relevant information according to the configured formatters.

There's 2 work items here:

  • - Allowing components to take over the rendering of the error page depending on the exception type (like how EF has the database error page)
  • - Rendering a plaintext response if the client isn't a browser (if the Accept header does not contain text/html)
@DamianEdwards DamianEdwards added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Mar 14, 2019
@Eilon Eilon added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Mar 15, 2019
@davidfowl davidfowl added this to the 3.0.0-preview4 milestone Mar 15, 2019
@davidfowl davidfowl added Needs: Design This issue requires design work before implementating. area-middleware and removed area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Mar 15, 2019
@davidfowl
Copy link
Member

davidfowl commented Mar 31, 2019

As a strawman design for the first part of this issue, we could introduce a simple interface like this:

public interface IDeveloperPageExceptionFilter
{
    Task HandleExceptionAsync(HttpContext context, Exception exception, Func<HttpContext, Exception, Task> next);
}

This follows the filter pattern we have elsewhere in the product (middleware, startup filters etc)

  • Short circuiting the default logic means you don't call next
  • With access to the Exception and next you can change the exception type in a filter and call next with a more specific exception (it would allow for doing things like exception unwrapping)

We could provide an ExceptionContext in case we wanted to expand it further in the future with more data (it clashes with the ExceptionContext type in MVC though so maybe a different name).

cc @rynowak @Tratcher

@rynowak
Copy link
Member

rynowak commented Apr 1, 2019

This pattern seems OK, how do we decide on the order? Are these in DI or options? What's the lifetime? Those are some of the rough edges in MVC's filters. If it's based on options then the answers are pretty straightforward IMO.

+1 for the idea of chain of responsibility here. Is the idea that we register the current behaviour as the last filter?

I'm not sure the idea of transforming the exception is valuable. I think it's fine if the contract allows it, but I don't see why its necessary.

I don't think we need a context here. If we get that far, we might want to consider using features or enhancing existing contracts on http context.

note: since nothing clears the endpoint on throw - you could use endpoint metadata in one of these filters.

@davidfowl
Copy link
Member

This pattern seems OK, how do we decide on the order?

DI order, like IStartupFilters. I don't think the order will matter that much in practice as I'm hoping the majority use case is to filter on exception type if you're not wholesale changing exception formatting (then you just need to be the first).

Are these in DI or options?

DI.

What's the lifetime?

Singleton

I'm not sure the idea of transforming the exception is valuable. I think it's fine if the contract allows it, but I don't see why its necessary.

Turning an Aggregate into something else or a TargetInvocationException into something. It might be contrived but we'll see what people do.

note: since nothing clears the endpoint on throw - you could use endpoint metadata in one of these filters.

👍 That's pretty useful.

@rynowak
Copy link
Member

rynowak commented Apr 1, 2019

DI order, like IStartupFilters. I don't think the order will matter that much in practice as I'm hoping the majority use case is to filter on exception type if you're not wholesale changing exception formatting (then you just need to be the first).

I think there's another important dimension here - what is the client? We've wanted for a while to have a JSON version for APIs.

If the framework registers handlers for you then it won't be straightforward to resolve ordering problems.

Turning an Aggregate into something else or a TargetInvocationException into something. It might be contrived but we'll see what people do.

If that's the case though, then I wouldn't do it that way as a user. It's a little too clever.

If there are places the framework always throws a TIE or an AggyBoy we should treat those as bugs should being unwrapping for you.

@rynowak
Copy link
Member

rynowak commented Apr 1, 2019

Imagine registering a handler that does problemdetails for an API based on IApiBehaviourMetadata ([ApiController])

@davidfowl
Copy link
Member

I think there's another important dimension here - what is the client? We've wanted for a while to have a JSON version for APIs.
If the framework registers handlers for you then it won't be straightforward to resolve ordering problems.

That's a good question. If you look at the original issue, we also want to change the output to the plain text exception if text/html isn't in the accept header (or if there's no accept header). That's the lowest common denominator and wouldn't be expressed as a filter (it'll just be built in).

So that was the intent. If a filter ends up being greedy it'll never run but I don't know that we want to do any more complicated matching (content type/metadata/exception type). I think in a typical application with MVC and EF, we'll have 2 of these filters:

  • The one you mention that uses IApiBehaviourMetadata on the Endpoint to determine if to show JSON (or use conneg etc)
  • The EF database error page filter (that would replace the database error page)

The EF one will show up only if the exception is of a specific type so it won't be greedy. Can you think of other things that might cause issues in practice?

If there are places the framework always throws a TIE or an AggyBoy we should treat those as bugs should being unwrapping for you.

Maybe, it means you lose the ability to do your own formatting of those types of exceptions. I wouldn't want to hide that type of information. I just threw that out as a possible example of a thing that would change the exception type. I don't know if anyone would ever need to do it though.

@Eilon
Copy link
Member

Eilon commented Apr 1, 2019

FYI here are previous bugs tracking related (similar/same) stuff:

If the 'very related' bugs are entirely covered by this, should close those.

@rynowak
Copy link
Member

rynowak commented Apr 1, 2019

Maybe, it means you lose the ability to do your own formatting of those types of exceptions. I wouldn't want to hide that type of information.

I'm referring to cases where the TIE or AggyBoy doesn't have any information. If you use reflection to invoke a method, there's no real downside to unwrapping the TIE you get back. Likewise for a Task.Result.

I realise this is just banter at this point 😆

@davidfowl
Copy link
Member

davidfowl commented Apr 12, 2019

Moving this to preview5, here's what I think we should do:

  • Support the plaintext option. I think it'll break a bunch of tests since lots of them use TestServer and assume HTML will come back (this should be easy to verify)
  • Try to implement
    • MVC JSON formatting
    • The EF page - This will return into code in ConfigureServices vs Configure since something will need to add the EF middleware (assuming it'll be application code for now).

@Eilon
Copy link
Member

Eilon commented Apr 12, 2019

So in the con-neg you want plaintext to be the fallback? Or should the real fallback still be text/html if no Accept-Type is sent?

@Tratcher
Copy link
Member

If you were doing real content negotiation you'd be honoring the client's Accept ordering preference. That doesn't match up with this pipeline model as well.

text/plain is the safest fallback, and the easiest to understand in the common dev/debug scenario. I always hate having to dig through HTML in the debugger, test logs, etc..

@Eilon
Copy link
Member

Eilon commented Apr 12, 2019

Con-neg doesn't require honoring anything! 😄 And it says nothing about the ultimate fallback in case no desired content types are supported. I agree that plaintext is simplest to see, but I wanted to understand if there's any consideration to "compatibility" or "doing what most people would expect."

@analogrelay
Copy link
Contributor

With #9342 merged, is this done? We now have a plain-text exception page.

@davidfowl
Copy link
Member

Yes I'll file another IIS for the other work items (MVC and EF)

@davidfowl davidfowl added Done This issue has been fixed and removed 2 - Working labels Apr 22, 2019
@davidfowl davidfowl removed the Needs: Design This issue requires design work before implementating. label Apr 22, 2019
@IanKemp
Copy link

IanKemp commented Nov 5, 2019

As far as I can see, this item should not be marked as done, as it only addresses the second point in the OP. The first is still very much a missing feature.

@davidfowl
Copy link
Member

Not sure what you mean.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 5, 2019
@amcasey amcasey added area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlewares and removed area-runtime labels Jun 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlewares Done This issue has been fixed enhancement This issue represents an ask for new feature or an enhancement to an existing one
Projects
None yet
Development

No branches or pull requests

9 participants