У меня есть веб-API .NET core 2.1, который использует Swashbuckle.AspNetCore v3.0.0 для генерации документации. Я могу получить доступ к Swagger UI из API, который работает отлично, и я не получаю никаких ошибок при использовании его из API. Однако я не могу использовать swagger.json для публикации этого API в Azure API Management. Когда я загружаю файл swagger.json в редактор swagger для проверки, он жалуется на кучу ошибок (те же, на которые жалуется Azure). Поскольку swagger.json генерируется из swagger, настроенного в моем API (это ссылка прямо вверху интерфейса swagger в моем API), почему его нельзя успешно загрузить в редактор swagger или в Azure?
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = settings.AppSettings.ApplicationName + " API", Version = "v1" });
var commentPath = String.Format(@"{0}\{1}.xml", AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName);
c.IncludeXmlComments(commentPath);
});
{"swagger":"2.0","info":{"version":"v1","title":"Route Manager API"},"paths":{"/api/account/Register":{"post":{"tags":["Account"],"summary":"Allows for registering a customer or vendor.","operationId":"ApiAccountRegisterPost","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"UserName","in":"query","description":"Unique name of user being registered.","required":true,"type":"string"},{"name":"Password","in":"query","description":"Password for new user.","required":true,"type":"string"},{"name":"ConfirmPassword","in":"query","description":"Confirmation of password that must match.","required":true,"type":"string"},{"name":"Email","in":"query","description":"Email address of registring user.","required":true,"type":"string"}],"responses":{"200":{"description":"Successfully registered customer or vendor.","schema":{"$ref":"#/definitions/IdentityResult"}},"400":{"description":"The RegisterUserDto model was invalid.","schema":{"$ref":"#/definitions/RegisterUserDto"}}}}},"/api/account/ChangePassword":{"post":{"tags":["Account"],"summary":"Allows for changing the users password.","operationId":"ApiAccountChangePasswordPost","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"OldPassword","in":"query","required":true,"type":"string","format":"password"},{"name":"NewPassword","in":"query","required":true,"type":"string","format":"password","maxLength":100,"minLength":6},{"name":"ConfirmPassword","in":"query","required":true,"type":"string","format":"password"},{"name":"StatusMessage","in":"query","required":false,"type":"string"}],"responses":{"400":{"description":"The ChangePasswordDto model was invalid.","schema":{"$ref":"#/definitions/ChangePasswordDto"}},"200":{"description":"Password successfully changed"}}}},"/api/account/Logout":{"post":{"tags":["Account"],"summary":"Logs the current user out of the system.","operationId":"ApiAccountLogoutPost","consumes":[],"produces":[],"parameters":[],"responses":{"200":{"description":"Logout successfully performed"}}}},"/api/account/ExternalLogin":{"get":{"tags":["Account"],"summary":"Initiates the external login process for the specified authentication provider.","description":"When an external login is not already associated with a local account, an attempt is made to find one local account with the same email \r\naddress. If one is found, an account is created and associated with the external login. If an account is not found, this method will \r\nreturn the external login email to the caller so they can decide if they want to present an option for the user to provide an \r\nexisting email account or create a new one based off the external login email. If we always created a new account with the external \r\nemail then that would mean we could only associate external logins that have the same email address of an account stored in our system\r\nwhich would essentially allow duplicate accounts to be created for the same physical person.","operationId":"ApiAccountExternalLoginGet","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"provider","in":"query","description":"The code for the external login provider that is being used to login.","required":false,"type":"string"},{"name":"returnUrl","in":"query","description":"The return URL that will be called upon a successfully external login.","required":false,"type":"string"}],"responses":{"400":{"description":"Error occured during the external login process. Response should contain error description.","schema":{"$ref":"#/definitions/ExternalLoginConfirmationDto"}},"200":{"description":"Indicate a token was issued and the user has been signed in."},"202":{"description":"Indicates the user was successfully signed in but an account does not exist in our system in which case the email \r\n of the external login is returned in a ExternalLoginModel that should be used to call the ExternalLoginConfirmation action method."}}}},"/api/account/ExternalLoginCallback":{"get":{"tags":["Account"],"summary":"Attempts to sign in a user based on an external sign-in attempt.","description":"This action is called from an external login provider after a client calls ExternalLogin. The results of this call are \r\ndocumented in the ExternalLogin method since the client never calls this method directly.","operationId":"ApiAccountExternalLoginCallbackGet","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"returnUrl","in":"query","required":false,"type":"string"},{"name":"remoteError","in":"query","required":false,"type":"string"}],"responses":{"202":{"description":"Indicates the user was successfully signed in but an account does not exist in our system in which case the email \r\n of the external login is returned in a ExternalLoginModel that should be used to call the ExternalLoginConfirmation action method.","schema":{"$ref":"#/definitions/ExternalLoginConfirmationDto"}},"400":{"description":"Error occured during the external login process. Response should contain error description.","schema":{"$ref":"#/definitions/ExternalLoginConfirmationDto"}},"200":{"description":"Indicate a token was issued and the user has been signed in."}}}},"/api/account/ExternalLoginConfirmation":{"post":{"tags":["Account"],"summary":"Associates an external login with an existing user or new user.","description":"In order to successfully associate an email for an existing user to an external credentials such as \r\n facebook, the user must already be authenticated via the external login provider.","operationId":"ApiAccountExternalLoginConfirmationPost","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"Email","in":"query","description":"The external email address associated with the external provider login.","required":true,"type":"string"},{"name":"LoginProvider","in":"query","description":"The external provider that was used to authenticate.","required":false,"type":"string"},{"name":"ReturnUrl","in":"query","description":"The return URL that was provided by the client application when requesting an external login request.","required":false,"type":"string"}],"responses":{"400":{"description":"The model was found to be invalid. Response should contain model errors.","schema":{"$ref":"#/definitions/ExternalLoginConfirmationDto"}},"200":{"description":"Successfully associates the account to the external login and redirects to the client return URL."}}}},"/connect/authorize":{"get":{"tags":["Authorization"],"summary":"Provides the authorization endpoint for supporting openid connect code flow","operationId":"ConnectAuthorizeGet","consumes":[],"produces":["text/plain","application/json","text/json"],"parameters":[{"name":"AccessToken","in":"query","required":false,"type":"string"},{"name":"AcrValues","in":"query","required":false,"type":"string"},{"name":"Assertion","in":"query","required":false,"type":"string"},{"name":"Claims","in":"query","required":false,"type":"object"},{"name":"ClaimsLocales","in":"query","required":false,"type":"string"},{"name":"ClientAssertion","in":"query","required":false,"type":"string"},{"name":"ClientAssertionType","in":"query","required":false,"type":"string"},{"name":"ClientId","in":"query","required":false,"type":"string"},{"name":"ClientSecret","in":"query","required":false,"type":"string"},{"name":"Code","in":"query","required":false,"type":"string"},{"name":"CodeChallenge","in":"query","required":false,"type":"string"},{"name":"CodeChallengeMethod","in":"query","required":false,"type":"string"},{"name":"CodeVerifier","in":"query","required":false,"type":"string"},{"name":"Display","in":"query","required":false,"type":"string"},{"name":"GrantType","in":"query","required":false,"type":"string"},{"name":"IdentityProvider","in":"query","required":false,"type":"string"},{"name":"IdTokenHint","in":"query","required":false,"type":"string"},{"name":"LoginHint","in":"query","required":false,"type":"string"},{"name":"MaxAge","in":"query","required":false,"type":"integer","format":"int64"},{"name":"Nonce","in":"query","required":false,"type":"string"},{"name":"Password","in":"query","required":false,"type":"string"},{"name":"PostLogoutRedirectUri","in":"query","required":false,"type":"string"},{"name":"Prompt","in":"query","required":false,"type":"string"},{"name":"RedirectUri","in":"query","required":false,"type":"string"},{"name":"RefreshToken","in":"query","required":false,"type":"string"},{"name":"Request","in":"query","required":false,"type":"string"},{"name":"RequestId","in":"query","required":false,"type":"string"},{"name":"RequestUri","in":"query","required":false,"type":"string"},{"name":"Resource","in":"query","required":false,"type":"string"},{"name":"ResponseMode","in":"query","required":false,"type":"string"},{"name":"ResponseType","in":"query","required":false,"type":"string"},{"name":"Scope","in":"query","required":false,"type":"string"},{"name":"State","in":"query","required":false,"type":"string"},{"name":"Token","in":"query","required":false,"type":"string"},{"name":"TokenTypeHint","in":"query","required":false,"type":"string"},{"name":"Registration","in":"query","required":false,"type":"object"},{"name":"UiLocales","in":"query","required":false,"type":"string"},{"name":"Username","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Successfully authenticated via the authorize flow and returns the appropriate access/identity tokens.","schema":{"$ref":"#/definitions/SignInResult"}},"400":{"description":"Indicates the user was authenticated, but the user manager was unable to obtain the user due to a server error."},"403":{"description":"Indicates the authorization request is not allowed because the user is not authenticated."}}}},"/connect/token":{"post":{"tags":["Authorization"],"summary":"Provides the token endpoint for supporting openidconnect token generation.","operationId":"ConnectTokenPost","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"AccessToken","in":"query","required":false,"type":"string"},{"name":"AcrValues","in":"query","required":false,"type":"string"},{"name":"Assertion","in":"query","required":false,"type":"string"},{"name":"Claims","in":"query","required":false,"type":"object"},{"name":"ClaimsLocales","in":"query","required":false,"type":"string"},{"name":"ClientAssertion","in":"query","required":false,"type":"string"},{"name":"ClientAssertionType","in":"query","required":false,"type":"string"},{"name":"ClientId","in":"query","required":false,"type":"string"},{"name":"ClientSecret","in":"query","required":false,"type":"string"},{"name":"Code","in":"query","required":false,"type":"string"},{"name":"CodeChallenge","in":"query","required":false,"type":"string"},{"name":"CodeChallengeMethod","in":"query","required":false,"type":"string"},{"name":"CodeVerifier","in":"query","required":false,"type":"string"},{"name":"Display","in":"query","required":false,"type":"string"},{"name":"GrantType","in":"query","required":false,"type":"string"},{"name":"IdentityProvider","in":"query","required":false,"type":"string"},{"name":"IdTokenHint","in":"query","required":false,"type":"string"},{"name":"LoginHint","in":"query","required":false,"type":"string"},{"name":"MaxAge","in":"query","required":false,"type":"integer","format":"int64"},{"name":"Nonce","in":"query","required":false,"type":"string"},{"name":"Password","in":"query","required":false,"type":"string"},{"name":"PostLogoutRedirectUri","in":"query","required":false,"type":"string"},{"name":"Prompt","in":"query","required":false,"type":"string"},{"name":"RedirectUri","in":"query","required":false,"type":"string"},{"name":"RefreshToken","in":"query","required":false,"type":"string"},{"name":"Request","in":"query","required":false,"type":"string"},{"name":"RequestId","in":"query","required":false,"type":"string"},{"name":"RequestUri","in":"query","required":false,"type":"string"},{"name":"Resource","in":"query","required":false,"type":"string"},{"name":"ResponseMode","in":"query","required":false,"type":"string"},{"name":"ResponseType","in":"query","required":false,"type":"string"},{"name":"Scope","in":"query","required":false,"type":"string"},{"name":"State","in":"query","required":false,"type":"string"},{"name":"Token","in":"query","required":false,"type":"string"},{"name":"TokenTypeHint","in":"query","required":false,"type":"string"},{"name":"Registration","in":"query","required":false,"type":"object"},{"name":"UiLocales","in":"query","required":false,"type":"string"},{"name":"Username","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Successfully generates the appropriate access/identity tokens.","schema":{"$ref":"#/definitions/SignInResult"}},"400":{"description":"Indicates an error while attempting to generate an authentication token.","schema":{"$ref":"#/definitions/OpenIdConnectResponse"}},"403":{"description":"Indicates the authorization request is not allowed because the user is not authenticated."}}}},"/api/EntityActions":{"get":{"tags":["EntityAction"],"summary":"List endpoints for all enabled entity actions.","operationId":"ApiEntityActionsGet","consumes":[],"produces":[],"parameters":[],"responses":{"200":{"description":"Success"}}}},"/api/Environments":{"get":{"tags":["Environments"],"summary":"Returns all available environments.","description":"This method return all public environments. If the caller has been authenticated, additional environments configured\r\nfor the identity will also be returned.","operationId":"ApiEnvironmentsGet","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[],"responses":{"200":{"description":"Environments successfully returned","schema":{"uniqueItems":false,"type":"array","items":{"$ref":"#/definitions/ApplicationEnvironmentDto"}}},"500":{"description":"Unexpected error while retrieving available environments"}}}},"/api/Environments/{clientID}":{"get":{"tags":["Environments"],"summary":"Returns the set of environment configured for a given clientID","description":"This method return all public environments. If the caller has been authenticated, additional environments configured\r\nfor the identity will also be returned.","operationId":"ApiEnvironmentsByClientIDGet","consumes":[],"produces":["text/plain","application/json","text/json","application/xml","text/xml"],"parameters":[{"name":"clientID","in":"path","description":"The clientID assigned to the application requesting environments.","required":true,"type":"string"}],"responses":{"200":{"description":"Environments successfully returned","schema":{"uniqueItems":false,"type":"array","items":{"$ref":"#/definitions/ApplicationEnvironmentDto"}}},"400":{"description":"The client ID was not provided."},"500":{"description":"Unexpected error while retrieving available environments"}}}}},"definitions":{"IdentityResult":{"type":"object","properties":{"succeeded":{"type":"boolean","readOnly":true},"errors":{"uniqueItems":false,"type":"array","items":{"$ref":"#/definitions/IdentityError"},"readOnly":true}}},"IdentityError":{"type":"object","properties":{"code":{"type":"string"},"description":{"type":"string"}}},"RegisterUserDto":{"required":["userName","password","confirmPassword","email"],"type":"object","properties":{"userName":{"description":"Unique name of user being registered.","type":"string"},"password":{"description":"Password for new user.","type":"string"},"confirmPassword":{"description":"Confirmation of password that must match.","type":"string"},"email":{"description":"Email address of registring user.","type":"string"}}},"ChangePasswordDto":{"required":["oldPassword","newPassword","confirmPassword"],"type":"object","properties":{"oldPassword":{"format":"password","type":"string"},"newPassword":{"format":"password","maxLength":100,"minLength":6,"type":"string"},"confirmPassword":{"format":"password","type":"string"},"statusMessage":{"type":"string"}}},"ExternalLoginConfirmationDto":{"required":["email"],"type":"object","properties":{"email":{"description":"The external email address associated with the external provider login.","type":"string"},"loginProvider":{"description":"The external provider that was used to authenticate.","type":"string"},"returnUrl":{"description":"The return URL that was provided by the client application when requesting an external login request.","type":"string"}}},"SignInResult":{"type":"object","properties":{"succeeded":{"type":"boolean","readOnly":true},"isLockedOut":{"type":"boolean","readOnly":true},"isNotAllowed":{"type":"boolean","readOnly":true},"requiresTwoFactor":{"type":"boolean","readOnly":true}}},"OpenIdConnectResponse":{"type":"object","properties":{"accessToken":{"type":"string"},"code":{"type":"string"},"error":{"type":"string"},"errorDescription":{"type":"string"},"errorUri":{"type":"string"},"expiresIn":{"format":"int64","type":"integer"},"idToken":{"type":"string"},"refreshToken":{"type":"string"},"resource":{"type":"string"},"scope":{"type":"string"},"state":{"type":"string"},"tokenType":{"type":"string"}}},"ApplicationEnvironmentDto":{"type":"object","properties":{"applicationDisplayName":{"type":"string"},"environmentName":{"type":"string"},"baseUrl":{"type":"string"}}}}}
Вот пример функции, в которой сваггер разбивает свойства типа Openiddict внутри этой функции.
[HttpPost("~/connect/token")]
[ProducesResponseType(typeof(Microsoft.AspNetCore.Identity.SignInResult), 200)]
[ProducesResponseType(typeof(OpenIdConnectResponse), 400)]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
return Ok(null);
}
Эта функция генерирует следующий swagger.json из swashbuckle
/connect/token:
post:
tags:
- Authorization
summary: Provides the token endpoint for supporting openidconnect token generation.
operationId: ConnectTokenPost
consumes: []
produces:
- text/plain
- application/json
- text/json
- application/xml
- text/xml
parameters:
- name: AccessToken
in: query
required: false
type: string
- name: AcrValues
in: query
required: false
type: string
- name: Assertion
in: query
required: false
type: string
- name: Claims
in: query
required: false
type: object
- name: ClaimsLocales
in: query
required: false
type: string
- name: ClientAssertion
in: query
required: false
type: string
- name: ClientAssertionType
in: query
required: false
type: string
- name: ClientId
in: query
required: false
type: string
- name: ClientSecret
in: query
required: false
type: string
- name: Code
in: query
required: false
type: string
- name: CodeChallenge
in: query
required: false
type: string
- name: CodeChallengeMethod
in: query
required: false
type: string
- name: CodeVerifier
in: query
required: false
type: string
- name: Display
in: query
required: false
type: string
- name: GrantType
in: query
required: false
type: string
- name: IdentityProvider
in: query
required: false
type: string
- name: IdTokenHint
in: query
required: false
type: string
- name: LoginHint
in: query
required: false
type: string
- name: MaxAge
in: query
required: false
type: integer
format: int64
- name: Nonce
in: query
required: false
type: string
- name: Password
in: query
required: false
type: string
- name: PostLogoutRedirectUri
in: query
required: false
type: string
- name: Prompt
in: query
required: false
type: string
- name: RedirectUri
in: query
required: false
type: string
- name: RefreshToken
in: query
required: false
type: string
- name: Request
in: query
required: false
type: string
- name: RequestId
in: query
required: false
type: string
- name: RequestUri
in: query
required: false
type: string
- name: Resource
in: query
required: false
type: string
- name: ResponseMode
in: query
required: false
type: string
- name: ResponseType
in: query
required: false
type: string
- name: Scope
in: query
required: false
type: string
- name: State
in: query
required: false
type: string
- name: Token
in: query
required: false
type: string
- name: TokenTypeHint
in: query
required: false
type: string
- name: Registration
in: query
required: false
type: object
- name: UiLocales
in: query
required: false
type: string
- name: Username
in: query
required: false
type: string
responses:
'200':
description: Successfully generates the appropriate access/identity tokens.
schema:
$ref: '#/definitions/SignInResult'
'400':
description: Indicates an error while attempting to generate an authentication token.
schema:
$ref: '#/definitions/OpenIdConnectResponse'
'403':
description: Indicates the authorization request is not allowed because the user is not authenticated.
[HttpGet("~/connect/authorize")]
[ProducesResponseType(typeof(Microsoft.AspNetCore.Identity.SignInResult), 200)]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
return Ok(null);
}