Как добавить несколько действий в путь документа Swashbuckle, указывая на одну конечную точку? - PullRequest
0 голосов
/ 04 сентября 2018

Я пытаюсь описать свою конечную точку OAuth веб-API asp.net в swagger, используя Swashbuckle 5.6.0, и попробовал это решение:

Как отобразить конечную точку токена WebApi OAuth в Swagger

Моя проблема заключается в том, что URL-адрес получения токена доступа и получения нового с помощью токена обновления совпадает на сервере авторизации OAuth asp.net. Добавление второго URL-адреса в путь документа Swagger завершается неудачно из-за того, что «paths» является IDictionary<string, PathItem>.

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

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Наконец-то намек на «добавление чего-то бессмысленного» сработал для нашего варианта использования.

Я дополнительно добавил новый класс модели 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 теперь работает хорошо.

0 голосов
/ 05 сентября 2018

Пути - это словарь в коде:

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

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

, поэтому исключение "ключ уже добавлен в коллекцию"

На swagger мы следуем Спецификации 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 обходят это?

Вот несколько интересных выводов

Версия swagger-ui, которую они используют в gettyimages, сильно настроена, я думаю, что они вводят дополнительные пути, используя JS
https://api.gettyimages.com/swagger/ui/ext/GettyImages-Resources-OAuthGrant-js

Вы тоже можете это сделать, это будет намного больше работы, чем просто добавление чего-либо к конечным точкам

...