From 139c5ddffe9048cfe34c7a0f826d415ca33f4517 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:11:34 +0000 Subject: [PATCH 1/4] Initial plan From a99ded5ee5b24278196ba05c352883a18311738f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:20:28 +0000 Subject: [PATCH 2/4] Fix: Remove trailing slash from OpenAPI server URLs when pathBase is empty Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- .../src/Services/OpenApiDocumentService.cs | 5 + ...ifyOpenApiDocumentIsInvariant.received.txt | 2611 +++++++++++++++++ .../OpenApiDocumentServiceTests.Servers.cs | 37 +- 3 files changed, 2652 insertions(+), 1 deletion(-) create mode 100644 src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index f342f5b7943b..73fd3b91e1ec 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -209,6 +209,11 @@ internal List GetOpenApiServers(HttpRequest? httpRequest = null) if (httpRequest is not null) { var serverUrl = UriHelper.BuildAbsolute(httpRequest.Scheme, httpRequest.Host, httpRequest.PathBase); + // Remove trailing slash when pathBase is empty to align with OpenAPI specification + if (serverUrl.EndsWith('/') && !httpRequest.PathBase.HasValue) + { + serverUrl = serverUrl.TrimEnd('/'); + } return [new OpenApiServer { Url = serverUrl }]; } else diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt new file mode 100644 index 000000000000..df155fbc8d21 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt @@ -0,0 +1,2611 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "Sample | localized", + "description": "This is a localized OpenAPI document for français (France).", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/forms/form-file": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "resume" + ], + "type": "object", + "properties": { + "resume": { + "$ref": "#/components/schemas/IFormFile" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/forms/form-files": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "files" + ], + "type": "object", + "properties": { + "files": { + "$ref": "#/components/schemas/IFormFileCollection" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/forms/form-file-multiple": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "resume", + "files" + ], + "type": "object", + "allOf": [ + { + "type": "object", + "properties": { + "resume": { + "$ref": "#/components/schemas/IFormFile" + } + } + }, + { + "type": "object", + "properties": { + "files": { + "$ref": "#/components/schemas/IFormFileCollection" + } + } + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/forms/form-todo": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/forms/forms-pocos-and-files": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Todo" + }, + { + "type": "object", + "properties": { + "file": { + "$ref": "#/components/schemas/IFormFile" + } + } + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/v1/array-of-guids": { + "get": { + "tags": [ + "Sample" + ], + "parameters": [ + { + "name": "guids", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + } + } + } + } + }, + "/v1/todos": { + "post": { + "tags": [ + "Sample" + ], + "summary": "Creates a new todo item.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/v1/todos/{id}": { + "get": { + "tags": [ + "Sample" + ], + "description": "Returns a specific todo item.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TodoWithDueDate" + } + } + } + } + } + } + }, + "/v2/users": { + "get": { + "tags": [ + "users" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "post": { + "tags": [ + "Sample" + ], + "operationId": "CreateUser", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/xml/type-with-examples": { + "get": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypeWithExamples" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypeWithExamples" + } + } + } + } + } + } + }, + "/xml/todo": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TodoFomInterface" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/xml/project": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/xml/board": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BoardItem" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/xml/project-record": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectRecord" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/xml/todo-with-description": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TodoWithDescription" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/Xml": { + "get": { + "tags": [ + "Xml" + ], + "parameters": [ + { + "name": "name", + "in": "query", + "description": "The name of the person.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Returns the greeting.", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + }, + "text/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "post": { + "tags": [ + "Xml" + ], + "requestBody": { + "description": "The todo to insert into the database.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + }, + "text/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/schemas-by-ref/typed-results": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Triangle" + } + } + } + } + } + } + }, + "/schemas-by-ref/multiple-results": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Triangle" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/schemas-by-ref/iresult-no-produces": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/iresult-with-produces": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/xml": { + "schema": { + "$ref": "#/components/schemas/Triangle" + } + } + } + } + } + } + }, + "/schemas-by-ref/primitives": { + "get": { + "tags": [ + "Sample" + ], + "parameters": [ + { + "name": "id", + "in": "query", + "description": "The ID associated with the Todo item.", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "size", + "in": "query", + "description": "The number of Todos to fetch", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/product": { + "get": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/schemas-by-ref/account": { + "get": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Account" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Account" + } + } + } + } + } + } + }, + "/schemas-by-ref/array-of-ints": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + }, + "/schemas-by-ref/list-of-ints": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + }, + "/schemas-by-ref/ienumerable-of-ints": { + "post": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + }, + "/schemas-by-ref/dictionary-of-ints": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + } + }, + "/schemas-by-ref/frozen-dictionary-of-ints": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + } + }, + "/schemas-by-ref/shape": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Shape" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/weatherforecastbase": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WeatherForecastBase" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/person": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Person" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/category": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Category" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/container": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerType" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/root": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Root" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/location": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LocationContainer" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/parent": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ParentObject" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/child": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChildObject" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/json-patch": { + "patch": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatchDocument" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/json-patch-generic": { + "patch": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatchDocument" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/custom-iresult": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomIResultImplementor" + } + } + } + } + } + } + }, + "/schemas-by-ref/config-with-generic-lists": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Config" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/project-response": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectResponse" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/subscription": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Subscription" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/nullable-response": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NullableResponseModel" + } + } + } + } + } + } + }, + "/schemas-by-ref/nullable-return-type": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/NullableResponseModel" + } + ] + } + } + } + } + } + } + }, + "/schemas-by-ref/nullable-request": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/NullableRequestModel" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/complex-nullable-hierarchy": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ComplexHierarchyModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/nullable-array-elements": { + "post": { + "tags": [ + "Sample" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NullableArrayModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/schemas-by-ref/optional-with-default": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelWithDefaults" + } + } + } + } + } + } + }, + "/schemas-by-ref/nullable-enum-response": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnumNullableModel" + } + } + } + } + } + } + }, + "/responses/200-add-xml": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + }, + "text/xml": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + } + } + } + } + } + }, + "/responses/200-only-xml": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/xml": { + "schema": { + "$ref": "#/components/schemas/Todo" + } + } + } + } + } + } + }, + "/responses/triangle": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Triangle" + } + } + } + } + } + } + }, + "/responses/shape": { + "get": { + "tags": [ + "Sample" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Shape" + } + } + } + } + } + } + }, + "/getbyidandname/{id}/{name}": { + "get": { + "tags": [ + "Test" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "minLength": 5, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + }, + "text/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/gettypedresult": { + "get": { + "tags": [ + "Test" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MvcTodo" + } + } + } + } + } + } + }, + "/forms": { + "post": { + "tags": [ + "Test" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "Title": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "IsCompleted": { + "type": "boolean" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/getcultureinvariant": { + "get": { + "tags": [ + "Test" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentWeather" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Account": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + } + } + }, + "AddressDto": { + "required": [ + "relatedLocation" + ], + "type": "object", + "properties": { + "relatedLocation": { + "$ref": "#/components/schemas/LocationDto" + } + } + }, + "BoardItem": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "description": "An item on the board." + }, + "Category": { + "required": [ + "name", + "parent" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "parent": { + "$ref": "#/components/schemas/Category" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Tag" + } + } + } + }, + "ChildObject": { + "required": [ + "parent" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "parent": { + "$ref": "#/components/schemas/ParentObject" + } + } + }, + "CityResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "ComplexHierarchyModel": { + "required": [ + "id", + "requiredNested" + ], + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "optionalNested": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/NestedModel" + } + ] + }, + "requiredNested": { + "$ref": "#/components/schemas/NestedModel" + }, + "nullableListWithNullableItems": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/components/schemas/NestedModel" + } + } + } + }, + "ComplexType": { + "type": "object", + "properties": { + "description": { + "type": [ + "null", + "string" + ] + }, + "timestamp": { + "type": [ + "null", + "string" + ], + "format": "date-time" + } + } + }, + "Config": { + "type": "object", + "properties": { + "items1": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConfigItem" + } + }, + "items2": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConfigItem" + } + } + } + }, + "ConfigItem": { + "type": "object", + "properties": { + "id": { + "type": [ + "null", + "integer" + ], + "format": "int32" + }, + "lang": { + "type": [ + "null", + "string" + ] + }, + "words": { + "type": [ + "null", + "object" + ] + }, + "break": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "willBeGood": { + "type": [ + "null", + "string" + ] + } + } + }, + "ContainerType": { + "type": "object", + "properties": { + "seq1": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "seq2": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "CurrentWeather": { + "type": "object", + "properties": { + "temperature": { + "maximum": 100.5, + "minimum": -100.5, + "type": "number", + "format": "float", + "default": 0.1 + } + } + }, + "CustomIResultImplementor": { + "required": [ + "content" + ], + "type": "object", + "properties": { + "content": { + "type": "string" + } + } + }, + "EnumNullableModel": { + "required": [ + "requiredEnum" + ], + "type": "object", + "properties": { + "requiredEnum": { + "$ref": "#/components/schemas/TestEnum" + }, + "nullableEnum": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/TestEnum" + } + ] + }, + "listOfNullableEnums": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestEnum" + } + } + } + }, + "IFormFile": { + "type": "string", + "format": "binary" + }, + "IFormFileCollection": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IFormFile" + } + }, + "Item": { + "type": "object", + "properties": { + "name": { + "type": "array", + "items": { + "type": "string" + } + }, + "value": { + "type": "integer", + "format": "int32" + } + } + }, + "JsonPatchDocument": { + "type": "array", + "items": { + "type": "object", + "oneOf": [ + { + "required": [ + "op", + "path", + "value" + ], + "type": "object", + "properties": { + "op": { + "enum": [ + "add", + "replace", + "test" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "value": { } + }, + "additionalProperties": false + }, + { + "required": [ + "op", + "path", + "from" + ], + "type": "object", + "properties": { + "op": { + "enum": [ + "move", + "copy" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "from": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "required": [ + "op", + "path" + ], + "type": "object", + "properties": { + "op": { + "enum": [ + "remove" + ], + "type": "string" + }, + "path": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + }, + "LocationContainer": { + "required": [ + "location" + ], + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/LocationDto" + } + } + }, + "LocationDto": { + "required": [ + "address" + ], + "type": "object", + "properties": { + "address": { + "$ref": "#/components/schemas/AddressDto" + } + } + }, + "ModelWithDefaults": { + "type": "object", + "properties": { + "propertyWithDefault": { + "type": "string" + }, + "nullableWithNull": { + "type": [ + "null", + "string" + ] + }, + "numberWithDefault": { + "type": "integer", + "format": "int32" + }, + "boolWithDefault": { + "type": "boolean" + } + } + }, + "MvcTodo": { + "required": [ + "title", + "description", + "isCompleted" + ], + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "isCompleted": { + "type": "boolean" + } + } + }, + "NestedModel": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "optionalValue": { + "type": [ + "null", + "integer" + ], + "format": "int32" + }, + "deepNested": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComplexType" + } + ] + } + } + }, + "NullableArrayModel": { + "type": "object", + "properties": { + "nullableArray": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "listWithNullableElements": { + "type": "array", + "items": { + "type": "string" + } + }, + "nullableDictionaryWithNullableValues": { + "type": [ + "null", + "object" + ], + "additionalProperties": { + "type": "string" + } + } + } + }, + "NullableRequestModel": { + "required": [ + "requiredField" + ], + "type": "object", + "properties": { + "requiredField": { + "type": "string" + }, + "optionalField": { + "type": [ + "null", + "string" + ] + }, + "nullableList": { + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + }, + "nullableDictionary": { + "type": [ + "null", + "object" + ], + "additionalProperties": { + "type": "string" + } + } + } + }, + "NullableResponseModel": { + "required": [ + "requiredProperty" + ], + "type": "object", + "properties": { + "requiredProperty": { + "type": "string" + }, + "nullableProperty": { + "type": [ + "null", + "string" + ] + }, + "nullableComplexProperty": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/ComplexType" + } + ] + } + } + }, + "ParentObject": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChildObject" + } + } + } + }, + "Person": { + "required": [ + "discriminator" + ], + "type": "object", + "anyOf": [ + { + "$ref": "#/components/schemas/PersonStudent" + }, + { + "$ref": "#/components/schemas/PersonTeacher" + } + ], + "discriminator": { + "propertyName": "discriminator", + "mapping": { + "student": "#/components/schemas/PersonStudent", + "teacher": "#/components/schemas/PersonTeacher" + } + } + }, + "PersonStudent": { + "properties": { + "discriminator": { + "enum": [ + "student" + ], + "type": "string" + }, + "gpa": { + "type": "number", + "format": "double" + } + } + }, + "PersonTeacher": { + "required": [ + "subject" + ], + "properties": { + "discriminator": { + "enum": [ + "teacher" + ], + "type": "string" + }, + "subject": { + "type": "string" + } + } + }, + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "name": { + "type": "string" + } + } + }, + "Project": { + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "description": "The project that contains Todo items." + }, + "ProjectAddressResponse": { + "required": [ + "city" + ], + "type": "object", + "properties": { + "city": { + "$ref": "#/components/schemas/CityResponse" + } + } + }, + "ProjectBuilderResponse": { + "required": [ + "city" + ], + "type": "object", + "properties": { + "city": { + "$ref": "#/components/schemas/CityResponse" + } + } + }, + "ProjectRecord": { + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the project." + }, + "description": { + "type": "string", + "description": "The description of the project." + } + }, + "description": "The project that contains Todo items." + }, + "ProjectResponse": { + "required": [ + "address", + "builder" + ], + "type": "object", + "properties": { + "address": { + "$ref": "#/components/schemas/ProjectAddressResponse" + }, + "builder": { + "$ref": "#/components/schemas/ProjectBuilderResponse" + } + } + }, + "RefProfile": { + "required": [ + "user" + ], + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/RefUser" + } + } + }, + "RefUser": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + } + } + }, + "Root": { + "type": "object", + "properties": { + "item1": { + "$ref": "#/components/schemas/Item" + }, + "item2": { + "$ref": "#/components/schemas/Item" + } + } + }, + "Shape": { + "required": [ + "$type" + ], + "type": "object", + "anyOf": [ + { + "$ref": "#/components/schemas/ShapeTriangle" + }, + { + "$ref": "#/components/schemas/ShapeSquare" + } + ], + "discriminator": { + "propertyName": "$type", + "mapping": { + "triangle": "#/components/schemas/ShapeTriangle", + "square": "#/components/schemas/ShapeSquare" + } + } + }, + "ShapeSquare": { + "properties": { + "$type": { + "enum": [ + "square" + ], + "type": "string" + }, + "area": { + "type": "number", + "format": "double" + }, + "color": { + "type": "string" + }, + "sides": { + "type": "integer", + "format": "int32" + } + } + }, + "ShapeTriangle": { + "properties": { + "$type": { + "enum": [ + "triangle" + ], + "type": "string" + }, + "hypotenuse": { + "type": "number", + "format": "double" + }, + "color": { + "type": "string" + }, + "sides": { + "type": "integer", + "format": "int32" + } + } + }, + "Subscription": { + "required": [ + "id", + "primaryUser" + ], + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "primaryUser": { + "$ref": "#/components/schemas/RefProfile" + }, + "secondaryUser": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/RefProfile" + } + ] + } + } + }, + "Tag": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "TestEnum": { + "type": "integer" + }, + "Todo": { + "required": [ + "id", + "title", + "completed", + "createdAt" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The unique identifier of the to-do item.", + "format": "int32" + }, + "title": { + "type": "string", + "description": "The title of the to-do item." + }, + "completed": { + "type": "boolean", + "description": "Indicates whether the to-do item is completed." + }, + "createdAt": { + "type": "string", + "description": "The date and time when the to-do item was created.", + "format": "date-time" + } + }, + "description": "Represents a to-do item." + }, + "TodoFomInterface": { + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The identifier of the todo.", + "format": "int32" + }, + "name": { + "type": "string", + "description": "The name of the todo." + }, + "description": { + "type": "string", + "description": "A description of the todo." + } + }, + "description": "This is a todo item." + }, + "TodoWithDescription": { + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The identifier of the todo, overridden.", + "format": "int32" + }, + "name": { + "type": "string", + "description": "The name of the todo, overridden." + }, + "description": { + "type": "string", + "description": "A description of the the todo.\nAnother description of the todo." + } + } + }, + "TodoWithDueDate": { + "required": [ + "dueDate", + "id", + "title", + "completed", + "createdAt" + ], + "type": "object", + "properties": { + "dueDate": { + "type": "string", + "description": "The due date of the to-do item.", + "format": "date-time" + }, + "id": { + "type": "integer", + "description": "The unique identifier of the to-do item.", + "format": "int32" + }, + "title": { + "type": "string", + "description": "The title of the to-do item." + }, + "completed": { + "type": "boolean", + "description": "Indicates whether the to-do item is completed." + }, + "createdAt": { + "type": "string", + "description": "The date and time when the to-do item was created.", + "format": "date-time" + } + }, + "description": "Represents a to-do item with a due date." + }, + "Triangle": { + "type": "object", + "properties": { + "hypotenuse": { + "type": "number", + "format": "double" + }, + "color": { + "type": "string" + }, + "sides": { + "type": "integer", + "format": "int32" + } + } + }, + "TypeWithExamples": { + "type": "object", + "properties": { + "booleanType": { + "type": "boolean", + "example": true + }, + "integerType": { + "type": "integer", + "format": "int32", + "example": 42 + }, + "longType": { + "type": "integer", + "format": "int64", + "example": 1234567890123456789 + }, + "doubleType": { + "type": "number", + "format": "double", + "example": 3.14 + }, + "floatType": { + "type": "number", + "format": "float", + "example": 3.14 + }, + "dateTimeType": { + "type": "string", + "format": "date-time", + "example": "2022-01-01T00:00:00Z" + }, + "dateOnlyType": { + "type": "string", + "format": "date", + "example": "2022-01-01" + } + } + }, + "WeatherForecastBase": { + "required": [ + "$type" + ], + "type": "object", + "anyOf": [ + { + "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithCity" + }, + { + "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries" + }, + { + "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews" + } + ], + "discriminator": { + "propertyName": "$type", + "mapping": { + "0": "#/components/schemas/WeatherForecastBaseWeatherForecastWithCity", + "1": "#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries", + "2": "#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews" + } + } + }, + "WeatherForecastBaseWeatherForecastWithCity": { + "required": [ + "city" + ], + "properties": { + "$type": { + "enum": [ + 0 + ], + "type": "integer" + }, + "city": { + "type": "string" + } + } + }, + "WeatherForecastBaseWeatherForecastWithLocalNews": { + "required": [ + "news" + ], + "properties": { + "$type": { + "enum": [ + 2 + ], + "type": "integer" + }, + "news": { + "type": "string" + } + } + }, + "WeatherForecastBaseWeatherForecastWithTimeSeries": { + "required": [ + "summary" + ], + "properties": { + "$type": { + "enum": [ + 1 + ], + "type": "integer" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "temperatureC": { + "type": "integer", + "format": "int32" + }, + "summary": { + "type": "string" + } + } + } + } + }, + "tags": [ + { + "name": "Sample" + }, + { + "name": "users" + }, + { + "name": "Xml" + }, + { + "name": "Test" + } + ] +} \ No newline at end of file diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs index 967e84380d9d..e7aaea169e96 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs @@ -11,7 +11,7 @@ public partial class OpenApiDocumentServiceTests { [Theory] - [InlineData("Development", "localhost:5001", "", "http", "http://localhost:5001/")] + [InlineData("Development", "localhost:5001", "", "http", "http://localhost:5001")] [InlineData("Development", "example.com", "/api", "https", "https://example.com/api")] [InlineData("Staging", "localhost:5002", "/v1", "http", "http://localhost:5002/v1")] [InlineData("Staging", "api.example.com", "/base/path", "https", "https://api.example.com/base/path")] @@ -145,4 +145,39 @@ public void GetOpenApiServers_HandlesServerAddressFeatureWithNoValues() // Assert Assert.Empty(servers); } + + [Fact] + public void GetOpenApiServers_RemovesTrailingSlashWhenPathBaseIsEmpty() + { + // Arrange + var hostEnvironment = new HostingEnvironment + { + ApplicationName = "TestApplication", + EnvironmentName = "Development" + }; + var docService = new OpenApiDocumentService( + "v1", + new Mock().Object, + hostEnvironment, + GetMockOptionsMonitor(), + new Mock().Object, + new OpenApiTestServer(["http://localhost:5000"])); + var httpContext = new DefaultHttpContext() + { + Request = + { + Host = new HostString("example.com"), + PathBase = "", + Scheme = "https" + } + }; + + // Act + var servers = docService.GetOpenApiServers(httpContext.Request); + + // Assert + Assert.Single(servers); + Assert.Equal("https://example.com", servers[0].Url); + Assert.DoesNotContain("https://example.com/", servers.Select(s => s.Url)); + } } From 3a3091ce6f91fb2793e524426c20464fde8286f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:22:54 +0000 Subject: [PATCH 3/4] Update snapshot test for trailing slash fix Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- ...ifyOpenApiDocumentIsInvariant.received.txt | 2611 ----------------- ...ifyOpenApiDocumentIsInvariant.verified.txt | 2 +- 2 files changed, 1 insertion(+), 2612 deletions(-) delete mode 100644 src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt deleted file mode 100644 index df155fbc8d21..000000000000 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.received.txt +++ /dev/null @@ -1,2611 +0,0 @@ -{ - "openapi": "3.1.1", - "info": { - "title": "Sample | localized", - "description": "This is a localized OpenAPI document for français (France).", - "version": "1.0.0" - }, - "servers": [ - { - "url": "http://localhost" - } - ], - "paths": { - "/forms/form-file": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "resume" - ], - "type": "object", - "properties": { - "resume": { - "$ref": "#/components/schemas/IFormFile" - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/forms/form-files": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "files" - ], - "type": "object", - "properties": { - "files": { - "$ref": "#/components/schemas/IFormFileCollection" - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/forms/form-file-multiple": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "resume", - "files" - ], - "type": "object", - "allOf": [ - { - "type": "object", - "properties": { - "resume": { - "$ref": "#/components/schemas/IFormFile" - } - } - }, - { - "type": "object", - "properties": { - "files": { - "$ref": "#/components/schemas/IFormFileCollection" - } - } - } - ] - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/forms/form-todo": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/forms/forms-pocos-and-files": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "file" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/Todo" - }, - { - "type": "object", - "properties": { - "file": { - "$ref": "#/components/schemas/IFormFile" - } - } - } - ] - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/v1/array-of-guids": { - "get": { - "tags": [ - "Sample" - ], - "parameters": [ - { - "name": "guids", - "in": "query", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - } - } - } - } - } - } - }, - "/v1/todos": { - "post": { - "tags": [ - "Sample" - ], - "summary": "Creates a new todo item.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/v1/todos/{id}": { - "get": { - "tags": [ - "Sample" - ], - "description": "Returns a specific todo item.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TodoWithDueDate" - } - } - } - } - } - } - }, - "/v2/users": { - "get": { - "tags": [ - "users" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Sample" - ], - "operationId": "CreateUser", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/xml/type-with-examples": { - "get": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypeWithExamples" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypeWithExamples" - } - } - } - } - } - } - }, - "/xml/todo": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TodoFomInterface" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/xml/project": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Project" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/xml/board": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BoardItem" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/xml/project-record": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProjectRecord" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/xml/todo-with-description": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TodoWithDescription" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/Xml": { - "get": { - "tags": [ - "Xml" - ], - "parameters": [ - { - "name": "name", - "in": "query", - "description": "The name of the person.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Returns the greeting.", - "content": { - "text/plain": { - "schema": { - "type": "string" - } - }, - "application/json": { - "schema": { - "type": "string" - } - }, - "text/json": { - "schema": { - "type": "string" - } - } - } - } - } - }, - "post": { - "tags": [ - "Xml" - ], - "requestBody": { - "description": "The todo to insert into the database.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "text/plain": { - "schema": { - "type": "string" - } - }, - "application/json": { - "schema": { - "type": "string" - } - }, - "text/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/schemas-by-ref/typed-results": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Triangle" - } - } - } - } - } - } - }, - "/schemas-by-ref/multiple-results": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Triangle" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/schemas-by-ref/iresult-no-produces": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/iresult-with-produces": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "text/xml": { - "schema": { - "$ref": "#/components/schemas/Triangle" - } - } - } - } - } - } - }, - "/schemas-by-ref/primitives": { - "get": { - "tags": [ - "Sample" - ], - "parameters": [ - { - "name": "id", - "in": "query", - "description": "The ID associated with the Todo item.", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "size", - "in": "query", - "description": "The number of Todos to fetch", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/product": { - "get": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Product" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Product" - } - } - } - } - } - } - }, - "/schemas-by-ref/account": { - "get": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Account" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Account" - } - } - } - } - } - } - }, - "/schemas-by-ref/array-of-ints": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int32" - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/schemas-by-ref/list-of-ints": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "int32" - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/schemas-by-ref/ienumerable-of-ints": { - "post": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - }, - "/schemas-by-ref/dictionary-of-ints": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - } - }, - "/schemas-by-ref/frozen-dictionary-of-ints": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer", - "format": "int32" - } - } - } - } - } - } - } - }, - "/schemas-by-ref/shape": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Shape" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/weatherforecastbase": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WeatherForecastBase" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/person": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Person" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/category": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Category" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/container": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContainerType" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/root": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Root" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/location": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LocationContainer" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/parent": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ParentObject" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/child": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChildObject" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/json-patch": { - "patch": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/JsonPatchDocument" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/json-patch-generic": { - "patch": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/JsonPatchDocument" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/custom-iresult": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomIResultImplementor" - } - } - } - } - } - } - }, - "/schemas-by-ref/config-with-generic-lists": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Config" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/project-response": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProjectResponse" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/subscription": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Subscription" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/nullable-response": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NullableResponseModel" - } - } - } - } - } - } - }, - "/schemas-by-ref/nullable-return-type": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/NullableResponseModel" - } - ] - } - } - } - } - } - } - }, - "/schemas-by-ref/nullable-request": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/NullableRequestModel" - } - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/complex-nullable-hierarchy": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ComplexHierarchyModel" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/nullable-array-elements": { - "post": { - "tags": [ - "Sample" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NullableArrayModel" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/schemas-by-ref/optional-with-default": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModelWithDefaults" - } - } - } - } - } - } - }, - "/schemas-by-ref/nullable-enum-response": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EnumNullableModel" - } - } - } - } - } - } - }, - "/responses/200-add-xml": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - }, - "text/xml": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - } - } - } - } - } - }, - "/responses/200-only-xml": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "text/xml": { - "schema": { - "$ref": "#/components/schemas/Todo" - } - } - } - } - } - } - }, - "/responses/triangle": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Triangle" - } - } - } - } - } - } - }, - "/responses/shape": { - "get": { - "tags": [ - "Sample" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Shape" - } - } - } - } - } - } - }, - "/getbyidandname/{id}/{name}": { - "get": { - "tags": [ - "Test" - ], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "name", - "in": "path", - "required": true, - "schema": { - "minLength": 5, - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "text/plain": { - "schema": { - "type": "string" - } - }, - "application/json": { - "schema": { - "type": "string" - } - }, - "text/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/gettypedresult": { - "get": { - "tags": [ - "Test" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MvcTodo" - } - } - } - } - } - } - }, - "/forms": { - "post": { - "tags": [ - "Test" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "type": "object", - "properties": { - "Title": { - "type": "string" - }, - "Description": { - "type": "string" - }, - "IsCompleted": { - "type": "boolean" - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/getcultureinvariant": { - "get": { - "tags": [ - "Test" - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentWeather" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Account": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "name": { - "type": "string" - } - } - }, - "AddressDto": { - "required": [ - "relatedLocation" - ], - "type": "object", - "properties": { - "relatedLocation": { - "$ref": "#/components/schemas/LocationDto" - } - } - }, - "BoardItem": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "description": "An item on the board." - }, - "Category": { - "required": [ - "name", - "parent" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "parent": { - "$ref": "#/components/schemas/Category" - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "ChildObject": { - "required": [ - "parent" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "parent": { - "$ref": "#/components/schemas/ParentObject" - } - } - }, - "CityResponse": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "ComplexHierarchyModel": { - "required": [ - "id", - "requiredNested" - ], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "optionalNested": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/NestedModel" - } - ] - }, - "requiredNested": { - "$ref": "#/components/schemas/NestedModel" - }, - "nullableListWithNullableItems": { - "type": [ - "null", - "array" - ], - "items": { - "$ref": "#/components/schemas/NestedModel" - } - } - } - }, - "ComplexType": { - "type": "object", - "properties": { - "description": { - "type": [ - "null", - "string" - ] - }, - "timestamp": { - "type": [ - "null", - "string" - ], - "format": "date-time" - } - } - }, - "Config": { - "type": "object", - "properties": { - "items1": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConfigItem" - } - }, - "items2": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConfigItem" - } - } - } - }, - "ConfigItem": { - "type": "object", - "properties": { - "id": { - "type": [ - "null", - "integer" - ], - "format": "int32" - }, - "lang": { - "type": [ - "null", - "string" - ] - }, - "words": { - "type": [ - "null", - "object" - ] - }, - "break": { - "type": [ - "null", - "array" - ], - "items": { - "type": "string" - } - }, - "willBeGood": { - "type": [ - "null", - "string" - ] - } - } - }, - "ContainerType": { - "type": "object", - "properties": { - "seq1": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "seq2": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "CurrentWeather": { - "type": "object", - "properties": { - "temperature": { - "maximum": 100.5, - "minimum": -100.5, - "type": "number", - "format": "float", - "default": 0.1 - } - } - }, - "CustomIResultImplementor": { - "required": [ - "content" - ], - "type": "object", - "properties": { - "content": { - "type": "string" - } - } - }, - "EnumNullableModel": { - "required": [ - "requiredEnum" - ], - "type": "object", - "properties": { - "requiredEnum": { - "$ref": "#/components/schemas/TestEnum" - }, - "nullableEnum": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/TestEnum" - } - ] - }, - "listOfNullableEnums": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TestEnum" - } - } - } - }, - "IFormFile": { - "type": "string", - "format": "binary" - }, - "IFormFileCollection": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IFormFile" - } - }, - "Item": { - "type": "object", - "properties": { - "name": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "integer", - "format": "int32" - } - } - }, - "JsonPatchDocument": { - "type": "array", - "items": { - "type": "object", - "oneOf": [ - { - "required": [ - "op", - "path", - "value" - ], - "type": "object", - "properties": { - "op": { - "enum": [ - "add", - "replace", - "test" - ], - "type": "string" - }, - "path": { - "type": "string" - }, - "value": { } - }, - "additionalProperties": false - }, - { - "required": [ - "op", - "path", - "from" - ], - "type": "object", - "properties": { - "op": { - "enum": [ - "move", - "copy" - ], - "type": "string" - }, - "path": { - "type": "string" - }, - "from": { - "type": "string" - } - }, - "additionalProperties": false - }, - { - "required": [ - "op", - "path" - ], - "type": "object", - "properties": { - "op": { - "enum": [ - "remove" - ], - "type": "string" - }, - "path": { - "type": "string" - } - }, - "additionalProperties": false - } - ] - } - }, - "LocationContainer": { - "required": [ - "location" - ], - "type": "object", - "properties": { - "location": { - "$ref": "#/components/schemas/LocationDto" - } - } - }, - "LocationDto": { - "required": [ - "address" - ], - "type": "object", - "properties": { - "address": { - "$ref": "#/components/schemas/AddressDto" - } - } - }, - "ModelWithDefaults": { - "type": "object", - "properties": { - "propertyWithDefault": { - "type": "string" - }, - "nullableWithNull": { - "type": [ - "null", - "string" - ] - }, - "numberWithDefault": { - "type": "integer", - "format": "int32" - }, - "boolWithDefault": { - "type": "boolean" - } - } - }, - "MvcTodo": { - "required": [ - "title", - "description", - "isCompleted" - ], - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "isCompleted": { - "type": "boolean" - } - } - }, - "NestedModel": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "optionalValue": { - "type": [ - "null", - "integer" - ], - "format": "int32" - }, - "deepNested": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/ComplexType" - } - ] - } - } - }, - "NullableArrayModel": { - "type": "object", - "properties": { - "nullableArray": { - "type": [ - "null", - "array" - ], - "items": { - "type": "string" - } - }, - "listWithNullableElements": { - "type": "array", - "items": { - "type": "string" - } - }, - "nullableDictionaryWithNullableValues": { - "type": [ - "null", - "object" - ], - "additionalProperties": { - "type": "string" - } - } - } - }, - "NullableRequestModel": { - "required": [ - "requiredField" - ], - "type": "object", - "properties": { - "requiredField": { - "type": "string" - }, - "optionalField": { - "type": [ - "null", - "string" - ] - }, - "nullableList": { - "type": [ - "null", - "array" - ], - "items": { - "type": "string" - } - }, - "nullableDictionary": { - "type": [ - "null", - "object" - ], - "additionalProperties": { - "type": "string" - } - } - } - }, - "NullableResponseModel": { - "required": [ - "requiredProperty" - ], - "type": "object", - "properties": { - "requiredProperty": { - "type": "string" - }, - "nullableProperty": { - "type": [ - "null", - "string" - ] - }, - "nullableComplexProperty": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/ComplexType" - } - ] - } - } - }, - "ParentObject": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ChildObject" - } - } - } - }, - "Person": { - "required": [ - "discriminator" - ], - "type": "object", - "anyOf": [ - { - "$ref": "#/components/schemas/PersonStudent" - }, - { - "$ref": "#/components/schemas/PersonTeacher" - } - ], - "discriminator": { - "propertyName": "discriminator", - "mapping": { - "student": "#/components/schemas/PersonStudent", - "teacher": "#/components/schemas/PersonTeacher" - } - } - }, - "PersonStudent": { - "properties": { - "discriminator": { - "enum": [ - "student" - ], - "type": "string" - }, - "gpa": { - "type": "number", - "format": "double" - } - } - }, - "PersonTeacher": { - "required": [ - "subject" - ], - "properties": { - "discriminator": { - "enum": [ - "teacher" - ], - "type": "string" - }, - "subject": { - "type": "string" - } - } - }, - "Product": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "name": { - "type": "string" - } - } - }, - "Project": { - "required": [ - "name", - "description" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "description": "The project that contains Todo items." - }, - "ProjectAddressResponse": { - "required": [ - "city" - ], - "type": "object", - "properties": { - "city": { - "$ref": "#/components/schemas/CityResponse" - } - } - }, - "ProjectBuilderResponse": { - "required": [ - "city" - ], - "type": "object", - "properties": { - "city": { - "$ref": "#/components/schemas/CityResponse" - } - } - }, - "ProjectRecord": { - "required": [ - "name", - "description" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the project." - }, - "description": { - "type": "string", - "description": "The description of the project." - } - }, - "description": "The project that contains Todo items." - }, - "ProjectResponse": { - "required": [ - "address", - "builder" - ], - "type": "object", - "properties": { - "address": { - "$ref": "#/components/schemas/ProjectAddressResponse" - }, - "builder": { - "$ref": "#/components/schemas/ProjectBuilderResponse" - } - } - }, - "RefProfile": { - "required": [ - "user" - ], - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/RefUser" - } - } - }, - "RefUser": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string" - } - } - }, - "Root": { - "type": "object", - "properties": { - "item1": { - "$ref": "#/components/schemas/Item" - }, - "item2": { - "$ref": "#/components/schemas/Item" - } - } - }, - "Shape": { - "required": [ - "$type" - ], - "type": "object", - "anyOf": [ - { - "$ref": "#/components/schemas/ShapeTriangle" - }, - { - "$ref": "#/components/schemas/ShapeSquare" - } - ], - "discriminator": { - "propertyName": "$type", - "mapping": { - "triangle": "#/components/schemas/ShapeTriangle", - "square": "#/components/schemas/ShapeSquare" - } - } - }, - "ShapeSquare": { - "properties": { - "$type": { - "enum": [ - "square" - ], - "type": "string" - }, - "area": { - "type": "number", - "format": "double" - }, - "color": { - "type": "string" - }, - "sides": { - "type": "integer", - "format": "int32" - } - } - }, - "ShapeTriangle": { - "properties": { - "$type": { - "enum": [ - "triangle" - ], - "type": "string" - }, - "hypotenuse": { - "type": "number", - "format": "double" - }, - "color": { - "type": "string" - }, - "sides": { - "type": "integer", - "format": "int32" - } - } - }, - "Subscription": { - "required": [ - "id", - "primaryUser" - ], - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "primaryUser": { - "$ref": "#/components/schemas/RefProfile" - }, - "secondaryUser": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/RefProfile" - } - ] - } - } - }, - "Tag": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "TestEnum": { - "type": "integer" - }, - "Todo": { - "required": [ - "id", - "title", - "completed", - "createdAt" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The unique identifier of the to-do item.", - "format": "int32" - }, - "title": { - "type": "string", - "description": "The title of the to-do item." - }, - "completed": { - "type": "boolean", - "description": "Indicates whether the to-do item is completed." - }, - "createdAt": { - "type": "string", - "description": "The date and time when the to-do item was created.", - "format": "date-time" - } - }, - "description": "Represents a to-do item." - }, - "TodoFomInterface": { - "required": [ - "name", - "description" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The identifier of the todo.", - "format": "int32" - }, - "name": { - "type": "string", - "description": "The name of the todo." - }, - "description": { - "type": "string", - "description": "A description of the todo." - } - }, - "description": "This is a todo item." - }, - "TodoWithDescription": { - "required": [ - "name", - "description" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The identifier of the todo, overridden.", - "format": "int32" - }, - "name": { - "type": "string", - "description": "The name of the todo, overridden." - }, - "description": { - "type": "string", - "description": "A description of the the todo.\nAnother description of the todo." - } - } - }, - "TodoWithDueDate": { - "required": [ - "dueDate", - "id", - "title", - "completed", - "createdAt" - ], - "type": "object", - "properties": { - "dueDate": { - "type": "string", - "description": "The due date of the to-do item.", - "format": "date-time" - }, - "id": { - "type": "integer", - "description": "The unique identifier of the to-do item.", - "format": "int32" - }, - "title": { - "type": "string", - "description": "The title of the to-do item." - }, - "completed": { - "type": "boolean", - "description": "Indicates whether the to-do item is completed." - }, - "createdAt": { - "type": "string", - "description": "The date and time when the to-do item was created.", - "format": "date-time" - } - }, - "description": "Represents a to-do item with a due date." - }, - "Triangle": { - "type": "object", - "properties": { - "hypotenuse": { - "type": "number", - "format": "double" - }, - "color": { - "type": "string" - }, - "sides": { - "type": "integer", - "format": "int32" - } - } - }, - "TypeWithExamples": { - "type": "object", - "properties": { - "booleanType": { - "type": "boolean", - "example": true - }, - "integerType": { - "type": "integer", - "format": "int32", - "example": 42 - }, - "longType": { - "type": "integer", - "format": "int64", - "example": 1234567890123456789 - }, - "doubleType": { - "type": "number", - "format": "double", - "example": 3.14 - }, - "floatType": { - "type": "number", - "format": "float", - "example": 3.14 - }, - "dateTimeType": { - "type": "string", - "format": "date-time", - "example": "2022-01-01T00:00:00Z" - }, - "dateOnlyType": { - "type": "string", - "format": "date", - "example": "2022-01-01" - } - } - }, - "WeatherForecastBase": { - "required": [ - "$type" - ], - "type": "object", - "anyOf": [ - { - "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithCity" - }, - { - "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries" - }, - { - "$ref": "#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews" - } - ], - "discriminator": { - "propertyName": "$type", - "mapping": { - "0": "#/components/schemas/WeatherForecastBaseWeatherForecastWithCity", - "1": "#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries", - "2": "#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews" - } - } - }, - "WeatherForecastBaseWeatherForecastWithCity": { - "required": [ - "city" - ], - "properties": { - "$type": { - "enum": [ - 0 - ], - "type": "integer" - }, - "city": { - "type": "string" - } - } - }, - "WeatherForecastBaseWeatherForecastWithLocalNews": { - "required": [ - "news" - ], - "properties": { - "$type": { - "enum": [ - 2 - ], - "type": "integer" - }, - "news": { - "type": "string" - } - } - }, - "WeatherForecastBaseWeatherForecastWithTimeSeries": { - "required": [ - "summary" - ], - "properties": { - "$type": { - "enum": [ - 1 - ], - "type": "integer" - }, - "date": { - "type": "string", - "format": "date-time" - }, - "temperatureC": { - "type": "integer", - "format": "int32" - }, - "summary": { - "type": "string" - } - } - } - } - }, - "tags": [ - { - "name": "Sample" - }, - { - "name": "users" - }, - { - "name": "Xml" - }, - { - "name": "Test" - } - ] -} \ No newline at end of file diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt index eec2cfe16702..df155fbc8d21 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt @@ -7,7 +7,7 @@ }, "servers": [ { - "url": "http://localhost/" + "url": "http://localhost" } ], "paths": { From 8de4c500897ea62275adb793d11692c2c0e8002f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:28:14 +0000 Subject: [PATCH 4/4] Improve code comment for trailing slash removal logic Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- src/OpenApi/src/Services/OpenApiDocumentService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index 73fd3b91e1ec..a2b070ae7981 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -209,7 +209,8 @@ internal List GetOpenApiServers(HttpRequest? httpRequest = null) if (httpRequest is not null) { var serverUrl = UriHelper.BuildAbsolute(httpRequest.Scheme, httpRequest.Host, httpRequest.PathBase); - // Remove trailing slash when pathBase is empty to align with OpenAPI specification + // Remove trailing slash when pathBase is empty to align with OpenAPI specification. + // Keep the trailing slash if pathBase explicitly contains "/" to preserve intentional path structure. if (serverUrl.EndsWith('/') && !httpRequest.PathBase.HasValue) { serverUrl = serverUrl.TrimEnd('/');