{"id":19448444,"url":"https://github.com/metaljase/problemdetailsexamples","last_synced_at":"2026-05-20T07:32:02.147Z","repository":{"id":243143967,"uuid":"810991090","full_name":"metaljase/ProblemDetailsExamples","owner":"metaljase","description":"Examples of how ASP.NET Core APIs can generate error responses using the Problem Details RFC 9457 specification. ","archived":false,"fork":false,"pushed_at":"2024-06-16T23:48:42.000Z","size":44,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-25T08:54:27.348Z","etag":null,"topics":["api","asp-net-core","csharp","dotnet","dotnet-core","error-handling","exception-handling","minimal-api","problem-details","problemdetails","serilog","web-api"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metaljase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-05T18:23:53.000Z","updated_at":"2025-02-03T04:03:42.000Z","dependencies_parsed_at":"2024-11-10T16:29:37.433Z","dependency_job_id":"76b979ca-7e5c-47a1-9776-30b61704d447","html_url":"https://github.com/metaljase/ProblemDetailsExamples","commit_stats":null,"previous_names":["metaljase/problemdetailsexamples"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metaljase%2FProblemDetailsExamples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metaljase%2FProblemDetailsExamples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metaljase%2FProblemDetailsExamples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metaljase%2FProblemDetailsExamples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metaljase","download_url":"https://codeload.github.com/metaljase/ProblemDetailsExamples/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metaljase%2FProblemDetailsExamples/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259184670,"owners_count":22818263,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","asp-net-core","csharp","dotnet","dotnet-core","error-handling","exception-handling","minimal-api","problem-details","problemdetails","serilog","web-api"],"created_at":"2024-11-10T16:26:50.785Z","updated_at":"2026-05-20T07:32:02.140Z","avatar_url":"https://github.com/metaljase.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# What is ProblemDetailsExamples?\n\n`ProblemDetailsExamples` contains three ASP.NET Core 8 projects, each demonstrating a different technique how HTTP APIs can generate HTTP error responses using the [Problem Details RFC 9457 specification](https://datatracker.ietf.org/doc/html/rfc9457).\n\nProjects in the solution:\n- `Metalhead.Examples.ProblemDetails.Problem.Api` Writes problem details using [`Microsoft.AspNetCore.Http.Results.Problem`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.results.problem?view=aspnetcore-8.0).\n- `Metalhead.Examples.ProblemDetails.Service.Api` Writes problem details using [`Microsoft.AspNetCore.Http.IProblemDetailsService`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iproblemdetailsservice?view=aspnetcore-8.0).\n- `Metalhead.Examples.ProblemDetails.Writer.Api` Writes problem details using a custom [`Microsoft.AspNetCore.Http.IProblemDetailsWriter`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iproblemdetailswriter?view=aspnetcore-8.0).\n\n# How does it work?\n\nA problem details object can be written to the HTTP response in various contexts such as exception handlers, middleware, and within the actual endpoint methods themselves.  All three projects write problem details in their exception handlers and middleware.\n\n\u003e [!IMPORTANT]\n\u003e Actually, each project contains two middleware components that write problem details, but they do the same thing for demonstration purposes, so don't use them simultaneously!  To switch between them, comment out either `app.UseMiddleware\u003cProblemDetailsMiddleware\u003e();` or `app.UseStatusCodePages(ProblemDetailsStatusCodePages.HandleStatusCodeAsync);` in `Program.cs`.\n\n\u003e [!IMPORTANT]\n\u003e Also, all projects contain a controller endpoint (`/api/v1/math/workaroundresponse`) that writes problem details in the actual endpoint itself using [`Microsoft.AspNetCore.Mvc.ControllerBase.Problem`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.problem?view=aspnetcore-8.0).  This is a workaround for the issue where returning a `BadRequestResult` in a controller endpoint (`/api/v1/math/defectiveresponse`) prevents a custom problem details object being written to the response, due to `BadRequestResult` writing to the response.\n\nThe projects write log events to the console and file(s) using [Serilog](https://serilog.net/).  App settings contains configuration for Serilog, which can be adjusted if necessary.  Trace IDs are written to the logs, which can be used to correlate with the Trace IDs that are written to problem details.\n\nSwagger can be used in the development environment to test the endpoints and view the problem details that are written to the response.  However, there are many examples in the project's `\u003cproject name\u003e.http` file that demonstrate the various response outputs from the endpoints.\n\n# Endpoint examples\n\nAll three projects contain the following endpoints.  The input used in the endpoint examples below will result in an error response.\n\n- `/api/v1/divide?numerator=1\u0026denominator=0` \u0026 `/api/v1/math/divide/1/0` - Returns 400 Bad Request with 'feature' details.\n- `/api/v1/squareroot?radicand=-1` \u0026 `/api/v1/math/squareroot/-1` - Returns 400 Bad Request without 'feature' details.\n- `/api/v1/throwex` \u0026 `/api/v1/math/throwex` - Returns exception stack trace in development, otherwise 500 Internal Server Error.\n- `/api/v1/math/defectiveresponse` - Returns 400 Bad Request with missing 'feature' details.\n- `/api/v1/math/workaroundresponse` - Returns 400 Bad Request with 'feature' details.\n\n# Response output\n\nWhen an exception is thrown, it's bad practice to make the stack trace publicly available, even the exception message in some cases.  Therefore, in all three projects, the stack trace is only written to the response when running in the development environment.\n\nThe response output varies depending on the technique used in each project, which may help you decide which technique is best suited for your project.\n\nSome techniques require the request Accept header to contain a media type that is a subset of `application/problem+json` or `application/json` to write a problem details object to the response, as shown below:\n\n| Problem details writer | Subset of application/json or application/problem+json required in request Accept header to generate problem details object |\n|-|-|\n| [`Microsoft.AspNetCore.Http.Results.Problem`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.results.problem?view=aspnetcore-8.0) | No |\n| [`Microsoft.AspNetCore.Mvc.ControllerBase.Problem`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.problem?view=aspnetcore-8.0) | No |\n| [`Microsoft.AspNetCore.Http.IProblemDetailsService`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iproblemdetailsservice?view=aspnetcore-8.0) via controller endpoints\u003cbr\u003eUses\u0026nbsp;`Microsoft.AspNetCore.Mvc.Infrastructure.DefaultApiProblemDetailsWriter` | No |\n| [`Microsoft.AspNetCore.Http.IProblemDetailsService`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iproblemdetailsservice?view=aspnetcore-8.0) via minimal API endpoints\u003cbr\u003e Uses\u0026nbsp;`Microsoft.AspNetCore.Http.DefaultProblemDetailsWriter` | Yes |\n\n\u003e [!NOTE]\n\u003e [`Microsoft.AspNetCore.Http.IProblemDetailsWriter`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iproblemdetailswriter?view=aspnetcore-8.0) is not included in the table above because it's used to write a custom implementation that can be written to generate problem details objects using your own requirements.\n\nConsidering the table above, the following table shows whether the response is written as a problem details object or a fallback in plain text:\n\u003ctable\u003e\u003cthead\u003e\n  \u003ctr\u003e\n    \u003cth rowspan=\"3\"\u003eProject\u003c/th\u003e\n    \u003cth colspan=\"4\"\u003eAccept Header: application/json\u003c/th\u003e\n    \u003cth colspan=\"4\"\u003eAccept Header: image/jpeg\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"2\"\u003eStatus Codes\u003cbr\u003e400-599\u003c/th\u003e\n    \u003cth colspan=\"2\"\u003eUnhandled\u003cbr\u003eException\u003c/th\u003e\n    \u003cth colspan=\"2\"\u003eStatus Codes\u003cbr\u003e400-599\u003cbr\u003e\u003c/th\u003e\n    \u003cth colspan=\"2\"\u003eUnhandled\u003cbr\u003eException\u003cbr\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth\u003eDev\u003c/th\u003e\u003cth\u003eNon-Dev\u003cbr\u003e\u003c/th\u003e\u003cth\u003eDev\u003c/th\u003e\u003cth\u003eNon-Dev\u003c/th\u003e\u003cth\u003eDev\u003c/th\u003e\u003cth\u003eNon-Dev\u003c/th\u003e\u003cth\u003eDev\u003c/th\u003e\u003cth\u003eNon-Dev\u003c/th\u003e\n  \u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eProblemDetails.Problem\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eProblemDetails.Service\u003cbr\u003evia minimal API endpoints\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eProblemDetails.Service\u003cbr\u003evia controller endpoints\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003cbr\u003e\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003cbr\u003e\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eProblemDetails.Writer\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003ePD\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\u003ctd\u003eText\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\n### Example output\nProblem details object, with 'math' feature details:\n```json\n{\n  \"type\": \"https://example.com/probs/division-by-zero\",\n  \"title\": \"Bad Request\",\n  \"status\": 400,\n  \"detail\": \"Division by zero is not allowed.\",\n  \"instance\": \"/api/v1/divide?numerator=1\u0026denominator=0\",\n  \"traceId\": \"00-0effaf938421c56593664b6dd3365e20-f6fc2f7949ba33ed-00\"\n}\n```\nProblem details object, without any feature details:\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc9110#section-15.5.1\",\n  \"title\": \"Bad Request\",\n  \"status\": 400,\n  \"instance\": \"/api/v1/squareroot?radicand=-1\",\n  \"traceId\": \"00-83d9c1c2af136692bd97845571c3e41b-e37be8b0a1a3aa8f-00\"\n}\n```\nPlain text, when the problem details writer cannot write a problem details object:\n```text\ntype: https://example.com/probs/division-by-zero\ntitle: Bad Request\nstatus: 400\ndetail: Division by zero is not allowed.\ninstance: /api/v1/divide?numerator=1\u0026denominator=0\ntraceId: 00-170f229e1e396fbd0ca16460c1abe7f8-2aab82e2ee7cdee4-00\n```\nProblem details object, resulting from an unhandled exception in a non-development environment:\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc9110#section-15.6.1\",\n  \"title\": \"An error occurred while processing your request.\",\n  \"status\": 500,\n  \"detail\": \"Sample Exception\",\n  \"traceId\": \"00-2ecff895699e7612e8ba8e1c9def6f74-b222cca5baa69266-00\"\n}\n```\nProblem details object, resulting from an unhandled exception in the development environment:\n```json\n{\n  \"type\": \"https://tools.ietf.org/html/rfc9110#section-15.6.1\",\n  \"title\": \"System.InvalidOperationException\",\n  \"status\": 500,\n  \"detail\": \"Sample Exception\",\n  \"exception\": {\n    \"details\": \"System.InvalidOperationException: Sample Exception\\r\\n   at Metalhead.Examples.ProblemDetailsService.Api.MathEndpoints.ThrowException() in C:\\\\Users\\\\jason.keeler\\\\source\\\\repos\\\\ProblemDetailsExamples\\\\Metalhead.Examples.ProblemDetails.Service.Api\\\\MathEndpoints.cs:line 217\\r\\n   at lambda_method31(Closure, Object, HttpContext)\\r\\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)\\r\\n   at Metalhead.Examples.ProblemDetailsService.Api.ProblemDetailsMiddleware.InvokeAsync(HttpContext httpContext) in C:\\\\Users\\\\jason.keeler\\\\source\\\\repos\\\\ProblemDetailsExamples\\\\Metalhead.Examples.ProblemDetails.Service.Api\\\\ProblemDetailsMiddleware.cs:line 11\\r\\n   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\\r\\n   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)\\r\\n   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)\\r\\n   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\\r\\n   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)\",\n    \"headers\": {\n      \"Accept\": [\n        \"application/json\"\n      ],\n      \"Host\": [\n        \"localhost:7181\"\n      ],\n      \"traceparent\": [\n        \"00-8d90050944aa1c4e2b82d31ae01c5e1c-57b093803ae0c94c-00\"\n      ]\n    },\n    \"path\": \"/api/v1/throwex\",\n    \"endpoint\": \"HTTP: GET /api/v1/throwex =\u003e ThrowException\",\n    \"routeValues\": {}\n  },\n  \"traceId\": \"00-8d90050944aa1c4e2b82d31ae01c5e1c-ccf6b96113b14761-00\"\n}\n```\nPlain text, when the problem details writer cannot write an unhandled exception as a problem details object in the development environment:\n```text\nSystem.InvalidOperationException: Sample Exception\n   at Metalhead.Examples.ProblemDetailsWriter.Api.MathEndpoints.ThrowException() in C:\\Users\\jason.keeler\\source\\repos\\ProblemDetailsExamples\\Metalhead.Examples.ProblemDetails.Writer.Api\\MathEndpoints.cs:line 197\n   at lambda_method29(Closure, Object, HttpContext)\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)\n   at Metalhead.Examples.ProblemDetailsWriter.Api.ProblemDetailsMiddleware.InvokeAsync(HttpContext httpContext) in C:\\Users\\jason.keeler\\source\\repos\\ProblemDetailsExamples\\Metalhead.Examples.ProblemDetails.Writer.Api\\ProblemDetailsMiddleware.cs:line 11\n   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)\n   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)\n   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\n   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)\n\nHEADERS\n=======\nAccept: application/json\nHost: localhost:7192\ntraceparent: 00-6c1d13332cda70eccdd60f4190759bf9-e4fe08cf15084344-00\n```\n\n# Setup instructions\n1. Clone the `ProblemDetailsExamples` repository.\n2. Open the .NET solution in Visual Studio 2026 (or a compatible alternative).\n3. Update `appsettings.json` and `appsettings.Development.json` if necessary, e.g. path to log file.\n4. Set one of the above projects as the startup project.\n5. Build the solution and run!\n6. Use Swagger to test the endpoints in the development environment, or use the `\u003cproject name\u003e.http` files to test the endpoints.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetaljase%2Fproblemdetailsexamples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetaljase%2Fproblemdetailsexamples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetaljase%2Fproblemdetailsexamples/lists"}