从 swashbuckle 自动生成的 swagger.json 是否应该在 swagger 编辑器中有效?
Should auto generated swagger.json from swashbuckle be valid in swagger editor?
我有一个 .NET core 2.1 web API,它使用 Swashbuckle.AspNetCore v3.0.0 生成 swagger 文档。我可以从 API 访问 swagger UI,它运行良好,并且在从 API 使用它时我没有收到任何错误。但是,我无法使用 swagger.json 将此 API 发布到 Azure API 管理。当我在 swagger 编辑器中加载 swagger.json 进行验证时,它会抱怨一堆错误(与 Azure 抱怨的错误相同)。由于 swagger.json 是从我的 API 中配置的 swagger 生成的(它是我的 API 中 swagger UI 顶部的 link,为什么无法成功加载到 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"}}}}}
这是一个示例函数,其中 swagger 在此函数中分解了 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);
}
该函数从 swashbuckle
生成以下 swagger.json
/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);
}
根据您分享的代码,我可以告诉您以下内容:
{
"name": "Claims",
"in": "query",
"required": false,
"type": "object"
}
不遵循开放Api规范:
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-7
If in is any value other than "body"...
Since the parameter is not located at the request body, it is limited to simple types (that is, not an object). The value MUST be one of "string", "number", "integer", "boolean", "array" or "file".
如果您在编辑器中手动将这些 "type": "object"
更改为 "type": "string" 错误将会消失。
更改您的操作以包含 FromBody:
public async Task<IActionResult> Exchange([FromBody] OpenIdConnectRequest request)
不知何故 swashbuckle 变得困惑并认为它是一个查询参数,而它应该在正文中
我有一个 .NET core 2.1 web API,它使用 Swashbuckle.AspNetCore v3.0.0 生成 swagger 文档。我可以从 API 访问 swagger UI,它运行良好,并且在从 API 使用它时我没有收到任何错误。但是,我无法使用 swagger.json 将此 API 发布到 Azure API 管理。当我在 swagger 编辑器中加载 swagger.json 进行验证时,它会抱怨一堆错误(与 Azure 抱怨的错误相同)。由于 swagger.json 是从我的 API 中配置的 swagger 生成的(它是我的 API 中 swagger UI 顶部的 link,为什么无法成功加载到 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"}}}}}
这是一个示例函数,其中 swagger 在此函数中分解了 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);
}
该函数从 swashbuckle
生成以下 swagger.json/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);
}
根据您分享的代码,我可以告诉您以下内容:
{
"name": "Claims",
"in": "query",
"required": false,
"type": "object"
}
不遵循开放Api规范:
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-7
If in is any value other than "body"...
Since the parameter is not located at the request body, it is limited to simple types (that is, not an object). The value MUST be one of "string", "number", "integer", "boolean", "array" or "file".
如果您在编辑器中手动将这些 "type": "object"
更改为 "type": "string" 错误将会消失。
更改您的操作以包含 FromBody:
public async Task<IActionResult> Exchange([FromBody] OpenIdConnectRequest request)
不知何故 swashbuckle 变得困惑并认为它是一个查询参数,而它应该在正文中