Skip to content
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

openAPI Document Generation doesn't include endpoints outside of Program.cs #59570

Open
wolfgang-hartl opened this issue Dec 19, 2024 · 2 comments
Assignees
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-openapi

Comments

@wolfgang-hartl
Copy link

wolfgang-hartl commented Dec 19, 2024

Hey there,

I've already mentioned it here (For reference). I'm facing an Issue where Endpoints, which are registered outside of the Program.cs don't get included in the generated .json file.

I've created a Repo for reproduction, you can find it here on Github.

Dotnet-Version: 9.0.101

The generated .json File looks like this, which is missing the endpoints in the Todo.csfile.

{
  "openapi": "3.0.1",
  "info": {
    "title": "api-dotnet | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:5190"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "api-dotnet"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/hello/{id}": {
      "get": {
        "tags": [
          "api-dotnet"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": { },
  "tags": [
    {
      "name": "api-dotnet"
    }
  ]
}
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Dec 19, 2024
@gfoidl gfoidl added feature-openapi area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Dec 19, 2024
@mikekistler
Copy link
Contributor

@wolfgang-hartl Thanks for opening this issue and providing the details for reproducing it.

I looked into this and what I found was that the location of the endpoint definition was not the key factor -- it was how the endpoint was declared. I added the following endpoint in my Program.cs file and found that it was not produced in the generation OpenAPI document:

app.MapGet("/goodbye", async context =>
{
    await context.Response.WriteAsJsonAsync(new { Message = "Goodbye!" });
}).WithDescription("Goodbye");

Digging into this further, I think the problem is specific the overload of MapGet that accepts a RequestDelegate.

The MapGet extension method has two overloads which differ only in the type of the third parameter and return type

Your repro and the code above are using the second overload -- the one that accepts a RequestDelegate.

I'm not sure why this second overload does not provide metadata for the endpoint that would make it show up in the generated OpenAPI document, but I think there is a pretty simple workaround - define the delegate to accept an HttpRequest rather than the HttpContext, and then just access the HttpContext from the request. Here's an example:

app.MapGet("/howdy", async (HttpRequest request) =>
{
    await request.HttpContext.Response.WriteAsJsonAsync(new { Message = "Howdy y'all!" });
}).WithDescription("Texan greeting");

This uses the first overload of MapGet and does produce an operation in the generated OpenAPI document.

  "paths": {
    "/howdy": {
      "get": {
        "tags": [
          "issue-59570"
        ],
        "description": "Texan greeting",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }

I'll follow up with engineering to determine if there is some reason the second overload doesn't / shouldn't produce an operation in the OpenAPI document, but for now I hope this workaround is helpful.

@mikekistler mikekistler self-assigned this Dec 30, 2024
@wolfgang-hartl
Copy link
Author

Hi @mikekistler,

thanks a lot for your detailed answer and the insights. That helps a lot!!
In the meantime I also found another solution which seems to always work with the openapi generation.

Using the TypedResults factory always produced the correct openapi generation for me.

For example doing something like this:

app.MapGet("/articles", async () =>
{
    // Some logic here
    return TypedResults.Ok(data);
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-openapi
Projects
None yet
Development

No branches or pull requests

3 participants