【问题标题】:How to add multiple actions to Swashbuckle Document Path pointing to one endpoint?如何向指向一个端点的 Swashbuckle 文档路径添加多个操作?
【发布时间】:2019-02-09 11:15:11
【问题描述】:

我尝试使用 Swashbuckle 5.6.0 大摇大摆地描述我的 asp.net Web API OAuth 端点并尝试了这个解决方案:

How to show WebApi OAuth token endpoint in Swagger

我的问题是,接收访问令牌和通过刷新令牌获取新令牌的 URL 在 asp.net OAuth 授权服务器中是相同的。 由于“路径”是IDictionary<string, PathItem>这一事实,将第二个 URL 添加到 Swagger 文档路径失败。

public class AuthTokenOperation : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        // get the Token Endpoint from Config
        var endpoint = Helpers.GetAppSetting("TokenEndPoint");

        // Access Token
        swaggerDoc.paths.Add(endpoint, new PathItem
        {
            post = new Operation
            {
                tags = new List<string> { "AccessToken" },
                consumes = new string[] { "application/x-www-form-url-encoded" },
                produces = new string[] { "application/json" },
                parameters = new List<Parameter>
                {
                    new Parameter
                    {
                        type = "string",
                        name = "username",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "password",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "grant_type",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_id",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_secret",
                        required = true,
                        @in = "formData"
                    }
                }
            }
        });

        // Refresh Token
        swaggerDoc.paths.Add(endpoint, new PathItem
        {
            post = new Operation
            {
                tags = new List<string> { "AccessToken" },
                consumes = new string[] { "application/x-www-form-url-encoded" },
                produces = new string[] { "application/json" },
                parameters = new List<Parameter>
                {
                    new Parameter
                    {
                        type = "string",
                        name = "grant_type",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_id",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_secret",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "refresh_token",
                        required = true,
                        @in = "formData"
                    }
                }
            }
        });
    }
}    

有没有可能描述两个指向同一个端点的api方法,只使用不同的参数?

如下所示:https://api.gettyimages.com/swagger/ui/index#!/OAuth

【问题讨论】:

  • 是的 - 我得到一个例外,具有该键的元素已添加到集合中:因为:// Access TokenswaggerDoc.paths.Add(endpoint, new PathItem{`// ... ... 。 ..` }// Refresh Token swaggerDoc.paths.Add(endpoint, new PathItem { // ... ... ... } 指向同一个端点。但我无法更改它们,因为它们是在 asp.net Identity 中实现的。
  • 我看到问题路径是一个字典:github.com/domaindrivendev/Swashbuckle/blob/master/… 不允许重复键....我正在查看 gettyimages,让我们看看我们能做什么
  • 对不起 - 没有帮助:(
  • 我今天会测试它并让你更新
  • 它现在可以工作 - 似乎有点“被黑”但它可以工作:) 我稍后会提供解决方案作为单独的答案。

标签: asp.net oauth swagger owin swashbuckle


【解决方案1】:

最后提示“添加一些无意义的东西”对我们的用例有用。

我还添加了一个新的模型类AuthServerResponseModel,其中映射了身份验证请求的响应。

public class AuthServerResponseModel
{
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
    public string refresh_token { get; set; }
    public string audience { get; set; }
}

要在 Swagger 中识别此对象,必须将类添加到 SchemaRegistry

之后我可以在响应模式中使用“@ref”标签来声明我的身份验证请求的响应类型。

public class AuthTokenOperation : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        schemaRegistry.GetOrRegister(typeof(AuthServerResponseModel));            

        // get the Token Endpoint from Config
        string endpoint = "URL-To-The-OAuth-Endpoint";

        // Access Token
        swaggerDoc.paths.Add(endpoint + "#AccessToken", new PathItem
        {
            post = new Operation
            {
                operationId = "AccessToken",
                tags = new List<string> { "Token" },
                consumes = new string[] { "application/x-www-form-url-encoded" },
                produces = new string[] { "application/json" },
                parameters = new List<Parameter>
                {
                    new Parameter
                    {
                        type = "string",
                        name = "username",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "password",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "grant_type",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_id",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_secret",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "DeviceId",
                        required = false,
                        @in = "formData"
                    }
                },
                responses = new Dictionary<string, Response>()
                {
                    { "200", new Response() { description = "Ok", schema = new Schema() { type = "object", @ref = "#/definitions/AuthServerResponseModel" } } },
                    { "400", new Response() { description = "BadRequest" } },
                    { "404", new Response() { description = "NotFound" } }
                }
            }
        });

        // Refresh Token
        swaggerDoc.paths.Add(endpoint + "#RefreshToken", new PathItem
        {
            post = new Operation
            {
                operationId = "RefreshToken",
                tags = new List<string> { "Token" },
                consumes = new string[] { "application/x-www-form-url-encoded" },
                produces = new string[] { "application/json" },
                parameters = new List<Parameter>
                {
                    new Parameter
                    {
                        type = "string",
                        name = "grant_type",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_id",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "client_secret",
                        required = true,
                        @in = "formData"
                    },
                    new Parameter
                    {
                        type = "string",
                        name = "refresh_token",
                        required = true,
                        @in = "formData"
                    }
                },
                responses = new Dictionary<string, Response>()
                {
                    { "200", new Response() { description = "Ok", schema = new Schema() { type = "object", @ref = "#/definitions/AuthServerResponseModel"  } } },
                    { "400", new Response() { description = "BadRequest" } },
                    { "404", new Response() { description = "NotFound" } }
                }
            }
        });
    }
}    

现在使用 Swagger Codegen 自动生成客户端效果很好。

【讨论】:

  • 我明白为什么你的评论 ` 似乎有点“黑客” ` DummyTextForSwagger 绝对是一个胖黑客,...看看schemaRegistry.GetOrRegister(
  • 更好,但仍然可以改进重复的代码实例化参数
【解决方案2】:

paths 是 swashbuckle 中的字典:

  public class SwaggerDocument
  {
    public readonly string swagger = "2.0";
    public Info info;
    ...

    public IDictionary<string, PathItem> paths;
    ...
  }

这就是为什么异常“key has been added to the collection”

我们大张旗鼓地遵循 Open Api 规范,并且该路径是一个带图案的字段:
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#patterned-fields

并且他们明确指出,对于那些带图案的字段,重复是不可以的:

模式化的字段可以多次出现,只要每个都有唯一的名称。

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#specification



最好的选择是将一些无意义的东西(如哈希)附加到端点以使它们不同,它可能是这样的:

swaggerDoc.paths.Add(endpoint + "#Access", new PathItem ...
swaggerDoc.paths.Add(endpoint + "#Refresh", new PathItem ...



gettyimages 的人如何解决这个问题?

这里有一些有趣的发现

他们在 gettyimages 使用的 swagger-ui 版本是高度定制的,我认为他们正在使用 JS 注入额外的路径
https://api.gettyimages.com/swagger/ui/ext/GettyImages-Resources-OAuthGrant-js

你也可以这样做,这比在端点上附加一些东西要多得多

【讨论】:

    猜你喜欢
    • 2020-07-03
    • 1970-01-01
    • 2017-05-19
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多