{"id":26807767,"url":"https://github.com/captainsafia/aspnet-openapi-xml","last_synced_at":"2025-04-23T13:46:24.371Z","repository":{"id":282351013,"uuid":"948224585","full_name":"captainsafia/aspnet-openapi-xml","owner":"captainsafia","description":"Sample repo for XML comment support in ASP.NET Core 📜","archived":false,"fork":false,"pushed_at":"2025-03-29T00:34:00.000Z","size":545,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T01:27:26.601Z","etag":null,"topics":["aspnetcore","minimal-api","openapi"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/captainsafia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2025-03-14T00:41:13.000Z","updated_at":"2025-03-29T00:34:04.000Z","dependencies_parsed_at":"2025-03-14T05:39:11.730Z","dependency_job_id":null,"html_url":"https://github.com/captainsafia/aspnet-openapi-xml","commit_stats":null,"previous_names":["captainsafia/aspnet-openapi-xml"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/captainsafia%2Faspnet-openapi-xml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/captainsafia%2Faspnet-openapi-xml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/captainsafia%2Faspnet-openapi-xml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/captainsafia%2Faspnet-openapi-xml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/captainsafia","download_url":"https://codeload.github.com/captainsafia/aspnet-openapi-xml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250442370,"owners_count":21431313,"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":["aspnetcore","minimal-api","openapi"],"created_at":"2025-03-30T00:17:01.065Z","updated_at":"2025-04-23T13:46:24.328Z","avatar_url":"https://github.com/captainsafia.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XML Documentation Comment Support for OpenAPI in ASP.NET Core\n\nThis app demonstrates the `Microsoft.AspNetCore.OpenApi` package's ability to integration XML documentation comments on types into OpenAPI documents.\n\n![screenshot of app with XML comments in sclaar UI](./screenshot.png)\n\n## Run Sample App\n\nTo run the API, navigate to the `api` directory and execute `dotnet run`.\n\n```\n$ cd api\n$ dotnet run\nBuilding...\ninfo: Microsoft.Hosting.Lifetime[14]\n      Now listening on: http://localhost:5052\ninfo: Microsoft.Hosting.Lifetime[0]\n      Application started. Press Ctrl+C to shut down.\ninfo: Microsoft.Hosting.Lifetime[0]\n      Hosting environment: Development\ninfo: Microsoft.Hosting.Lifetime[0]\n      Content root path: ~/git/aspnet-openapi-xml/api\n```\n\nNavigate to http://localhost:5052/ to view the Scalar UI for interacting with the application. Note that the Scalar UI includes summaries and descriptions on various elements sourced from XML documentation comments.\n\n## Customizing XML Documentation Behavior\n\n#### Adding XML Documentation Sources\n\nThe `Microsoft.AspNetCore.OpenApi` package will automatically resolve XML documentation comments from the application assembly (the `API` project in this case) and any projects referenced via `ProjectReferences` (like the `Models` project) if these projects have the `GenerateDocumentationFile` property set.\n\n```\n\u003cPropertyGroup\u003e\n  \u003cGenerateDocumentationFile\u003etrue\u003c/GenerateDocumentationFile\u003e\n\u003c/PropertyGroup\u003e\n```\n\nSince the implementation discovers XML files statically at compile-time, you can indicate additional sources for XML files by setting the `AdditionalFiles` item group. For example, to include XML documentation from a package reference.\n\n```xml\n\u003cItemGroup\u003e\n    \u003cPackageReference Include=\"Some.Package\" Version=\"10.0.0\" GeneratePathProperty=\"true\" /\u003e\n\u003c/ItemGroup\u003e\n\u003cItemGroup\u003e\n    \u003cAdditionalFiles Include=\"$(PkgSome_Package)/lib/net10.0/Some.Package.xml\"\u003e\n\u003c/ItemGroup\u003e\n```\n\n#### Disabling XML Documentation Support\n\nSince the functionality is implemented as a source generator, to turn off XML documentation integration, you can remove the source generator from the `Analyzers` item group to prevent it from being used in the compilation.\n\n```xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"Microsoft.AspNetCore.OpenApi\" Version=\"10.0.0-preview.2.*\" GeneratePathProperty=\"true\" /\u003e\n\u003c/ItemGroup\u003e\n\n\u003cTarget Name=\"DisableCompileTimeOpenApiXmlGenerator\" BeforeTargets=\"CoreCompile\"\u003e\n  \u003cItemGroup\u003e\n    \u003cAnalyzer Remove=\"$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Target\u003e\n```\n\n## Implementation Notes\n\n\u003e [!NOTE]\n\u003e The implementation described here is open-source and can be found in the [ASP.NET Core repo](https://github.com/dotnet/aspnetcore/tree/f3555640d3b0d049856947c4f2bd0b869adf5c5e/src/OpenApi/gen).\n\nSeveral times in this document, I've mentioned that the XML documentation feature is implemented as a source generator. How does all this work?\n\nThe `XmlCommentGenerator` extracts XML comments from two sources:\n\n* XML documentation files passed as `AdditionalFiles` via a `ParseXmlFile` implementation\n* XML comments from the target assembly's own code via a `ParseCompilation` implementation\n\nThe distinction between these two sources is important. XML documentation files passed as `AdditionalFiles` are static. XML comments from the target assembly come from Roslyn's `XmlDocumentationCommentProvider` which provides enhanced functionality for connecting an XML comment to the compilation symbol's that it is associated with. This has implications for the way `\u003cinheritdoc /\u003e` resolution happens in the implementation. We'll get more into this later.\n\n\nXML comments are parsed into structured `XmlComment` objects with:\n* Summary, description, remarks, returns, value sections\n* Parameter documentation with name, description, examples\n* Response documentation with status codes and descriptions\n* Support for examples and deprecated markers\n\nThe `XmlComment` class processes XML documentation tags like: `\u003cc\u003e`, `\u003ccode\u003e`, `\u003clist\u003e`, `\u003cpara\u003e`, `\u003cparamref\u003e`, `\u003ctypeparamref\u003e`, `\u003csee\u003e`, and `\u003cseealso\u003e`. For XML documentation tags that use references to other elements, like `\u003csee cref=\"SomeOtherType\"\u003e`, the implementation strips out the XML tag and maps the reference to plain text for inclusion in the OpenAPI document.\n\n### Support for `\u003cinheritdoc/\u003e`\n\n`\u003cinheritdoc /\u003e` tags present a unique oppurtunity because they indicate that comments must be resolved from a base class or implemented interface. The source generator uses its knowledge of the symbol's present in the compilation to discover base classes and interfaces associated with the symbol a given `\u003cinheritdoc /\u003e` is placed on and supports resolving them automatically.\n\nThis automatic resolution behavior is currently available for XML documentation comments that exist in the assembly under compilation, and _not_ XML documentation tags that are in referenced projects or packages. In the later scenario, XML documentation comments are only presented as text and there is no trivial strategy for associating the text content to compilation symbols or developing an understanding of the inheritance hierarchy associated with the types.\n\n### Member Identification\n\nThe source generator discovers XML comments statically and emits code that will apply them to the document dynamically at runtime. The `MemberKey` class acts as a bridge between compile-time and runtime representations of the same concept. It is a unique identifier for types, methods, and properties that works across:\n\n- Different compilation environments\n- Generic types with proper handling of open generics\n- Method overloads with parameter signature matching\n\nThe `MemberKey` defintion attempts to encode as much information as possible to map a compile-time symbol to its runtime counterpart.\n\n```csharp\ninternal sealed record MemberKey(\n    string? DeclaringType,\n    MemberType MemberKind,\n    string? Name,\n    string? ReturnType,\n    string[]? Parameters) : IEquatable\u003cMemberKey\u003e\n```\n\n### Code Generation\n\nThe generator emits code that contains:\n\n1. A cache of XML comments mapped to member identifiers:\n   ```csharp\n   _cache.Add(new MemberKey(/*...*/), new XmlComment(/*...*/));\n   ```\n\n2. OpenAPI transformer implementations:\n   - `XmlCommentOperationTransformer` - Applies comments to API operations (methods)\n   - `XmlCommentSchemaTransformer` - Applies comments to data models (types)\n\n3. Extension methods that intercept `AddOpenApi()` calls to inject the transformers:\n   ```csharp\n   public static IServiceCollection AddOpenApi(this IServiceCollection services)\n   {\n       return services.AddOpenApi(\"v1\", options =\u003e\n       {\n           options.AddSchemaTransformer(new XmlCommentSchemaTransformer());\n           options.AddOperationTransformer(new XmlCommentOperationTransformer());\n       });\n   }\n   ```\n\n### Interception Mechanism\n\nThe generator uses the C# compiler's interceptor feature to intercept calls to the `AddOpenApi` method. It:\n1. Detects different `AddOpenApi` overloads\n2. Generates appropriate interceptor methods for each variant\n3. Adds the XML comment transformers to the OpenAPI options\n\nWhen the interceptor runs, it adds the XML-specific transformers first then applies any transformers the user has registered in their application code. This allows user transformers to inspect metadata that has been automatically set by the source generated transformer implementations.\n\n### Runtime Behavior\n\nWhen the generated code runs:\n\n1. The XML comment cache is populated on first use with structured comment data\n2. The intercepted `AddOpenApi` methods add the transformers to the OpenAPI options\n3. During API documentation generation, the transformers:\n   - Look up documentation for API methods and apply summaries, descriptions, etc.\n   - Apply parameter documentation to OpenAPI parameters\n   - Set response descriptions based on XML documentation\n   - Include examples when provided in the XML\n\nThis allows developers to write standard XML comments in their code and have them automatically appear in the generated OpenAPI documentation without any additional configuration code.\n\n## Frequently Asked Questions\n\n### What is the OpenAPI XML documentation support feature?\n\nThe feature automatically extracts XML documentation comments from your code and uses them to populate OpenAPI documentation. This means your API documentation is generated directly from your code comments, keeping them in sync.\n\n### How does the XML documentation support work?\n\nIt uses a C# source generator (`XmlCommentGenerator`) that analyzes XML documentation at compile time and injects code that translates these comments into OpenAPI specification metadata.\n\n### Why is this implemented as a source generator?\n\nSource generators allow us to implement AoT-compatible resolution of inheritdoc references in XML comments.\n\n### How do I enable XML documentation in my ASP.NET Core API project?\n\n1. Enable XML documentation in your project file:\n   ```xml\n   \u003cPropertyGroup\u003e\n     \u003cGenerateDocumentationFile\u003etrue\u003c/GenerateDocumentationFile\u003e\n   \u003c/PropertyGroup\u003e\n   ```\n\n2. Use the `AddOpenApi()` method in your service configuration (no special configuration needed - the source generator handles the rest)\n\n### Do I need to include XML documentation files from referenced assemblies?\n\nYes, for referenced assemblies with API types. Add them as AdditionalFiles in your project:\n\n```xml\n\u003cItemGroup\u003e\n  \u003cAdditionalFiles Include=\"Path\\To\\AssemblyDoc.xml\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\nXML documentation comments from `ProjectReferences` are automatically resolved and don't require additional configuration.\n\n### What XML documentation tags are supported?\n\n- `\u003csummary\u003e` - Used for operation and type descriptions\n- `\u003cremarks\u003e` - Used for additional operation details\n- `\u003cparam\u003e` - Used for parameter descriptions\n- `\u003creturns\u003e` - Used for return value descriptions\n- `\u003cresponse\u003e` - Used for HTTP response documentation\n- `\u003cexample\u003e` - Used for examples in documentation\n- `\u003cdeprecated\u003e` - Marks operations as deprecated\n- `\u003cinheritdoc\u003e` - Inherits documentation from base classes/interfaces\n\n### How do I document HTTP responses?\n\nUse the `\u003cresponse\u003e` tag with a `code` attribute:\n```csharp\n/// \u003cresponse code=\"200\"\u003eSuccess response with data\u003c/response\u003e\n/// \u003cresponse code=\"404\"\u003eResource not found\u003c/response\u003e\n```\n\n### How do I add examples to documentation?\n\nUse the `\u003cexample\u003e` tag for types or the `example` attribute for parameters:\n```csharp\n/// \u003cexample\u003e{\"name\":\"Sample\",\"value\":42}\u003c/example\u003e\n/// \u003cparam name=\"id\" example=\"42\"\u003eThe unique identifier\u003c/param\u003e\n```\n\n### Does the generator support `\u003cinheritdoc/\u003e` tags?\n\nYes, it fully supports inheriting documentation from *as long as they exist in the compilation assembly*:\n- Base classes\n- Implemented interfaces\n- Base methods for overrides\n\n### How are generic type parameters handled when inheriting documentation?\n\nThe source generator substitutes generic type parameters in inherited documentation comments, preserving type references across inheritance boundaries.\n\n### How does the source generator identify and track API members?\n\nIt uses the `MemberKey` class to create a unique identifier for each API member that encodes:\n- Declaring and property types\n- Method names\n- Generic types with proper handling of open generics\n- Method overloads with parameter signature matching\n\n### Will the XML documentation processing impact my application's runtime performance?\n\nNo. The source generator processes XML documentation at compile time and caches the results, with minimal runtime overhead when rendering the OpenAPI documentation. Furthermore, the OpenAPI document can be cache at runtime using output-caching to further optimize performance.\n\n### How does the source generator intercept `AddOpenApi()` calls?\n\nIt uses the C# compiler's interceptor feature to detect calls to `AddOpenApi()` and automatically injects the XML documentation transformers.\n\n### What happens when I use different overloads of `AddOpenApi()`?\n\nThe source generator detects all standard overloads:\n- `AddOpenApi()`\n- `AddOpenApi(\"v1\")`\n- `AddOpenApi(options =\u003e {})`\n- `AddOpenApi(\"v1\", options =\u003e {})`\n\nEach is intercepted to automatically include the XML documentation transformers. The source generator does not handle overloads where the `documentName` parameter is not a literal string expression. For example, the transformer is not registered in the following scenarios:\n\n```csharp\nvar documentName = \"v1\";\nbuilder.Services.AddOpenApi(documentName); // No XML support here\n```\n\n## License\n[MIT](https://choosealicense.com/licenses/mit/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaptainsafia%2Faspnet-openapi-xml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaptainsafia%2Faspnet-openapi-xml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaptainsafia%2Faspnet-openapi-xml/lists"}