diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiDescription.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiDescription.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiDescription.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiDescription.cs
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiDescriptionProviderContext.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiDescriptionProviderContext.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiDescriptionProviderContext.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiDescriptionProviderContext.cs
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiParameterDescription.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiParameterDescription.cs
similarity index 84%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiParameterDescription.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiParameterDescription.cs
index 272fb69e95..1f07d2b7ab 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiParameterDescription.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiParameterDescription.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
@@ -35,5 +36,10 @@ public class ApiParameterDescription
/// Gets or sets the parameter type.
///
public Type Type { get; set; }
+
+ ///
+ /// Gets or sets the parameter descriptor.
+ ///
+ public ParameterDescriptor ParameterDescriptor { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiParameterRouteInfo.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiParameterRouteInfo.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiParameterRouteInfo.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiParameterRouteInfo.cs
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiRequestFormat.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiRequestFormat.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiRequestFormat.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiRequestFormat.cs
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiResponseFormat.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiResponseFormat.cs
similarity index 100%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiResponseFormat.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiResponseFormat.cs
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiResponseType.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiResponseType.cs
similarity index 91%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiResponseType.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiResponseType.cs
index 37d34b9af6..4e3307c0b5 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/ApiResponseType.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/ApiResponseType.cs
@@ -30,7 +30,7 @@ public class ApiResponseType
///
///
/// Will be null if the action returns no response, or if the response type is unclear. Use
- /// or on an action method
+ /// Microsoft.AspNetCore.Mvc.ProducesAttribute or Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute on an action method
/// to specify a response type.
///
public Type Type { get; set; }
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/IApiDescriptionProvider.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/IApiDescriptionProvider.cs
similarity index 77%
rename from src/Microsoft.AspNetCore.Mvc.ApiExplorer/IApiDescriptionProvider.cs
rename to src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/IApiDescriptionProvider.cs
index 5129b7d5ec..3f3ab9c595 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/IApiDescriptionProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ApiExplorer/IApiDescriptionProvider.cs
@@ -26,8 +26,16 @@ public interface IApiDescriptionProvider
///
int Order { get; }
+ ///
+ /// Creates or modifies s.
+ ///
+ /// The .
void OnProvidersExecuting(ApiDescriptionProviderContext context);
+ ///
+ /// Called after implementations with higher values have been called.
+ ///
+ /// The .
void OnProvidersExecuted(ApiDescriptionProviderContext context);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
index 54eb9a4c3a..6997bd16ae 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
@@ -688,6 +688,7 @@ private ApiParameterDescription CreateResult(
Name = GetName(containerName, bindingContext),
Source = source,
Type = bindingContext.ModelMetadata.ModelType,
+ ParameterDescriptor = Parameter,
};
}
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Properties/AssemblyInfo.cs
index 76feceeff0..1e28eb8ed4 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Properties/AssemblyInfo.cs
@@ -3,9 +3,18 @@
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]
[assembly: AssemblyCompany("Microsoft Corporation.")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat))]
+[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType))]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.net45.json b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.net45.json
new file mode 100644
index 0000000000..3e84534357
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.net45.json
@@ -0,0 +1,34 @@
+[
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider",
+ "Kind": "Removal"
+ }
+]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.netcore.json b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.netcore.json
new file mode 100644
index 0000000000..3e84534357
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.netcore.json
@@ -0,0 +1,34 @@
+[
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType",
+ "Kind": "Removal"
+ },
+ {
+ "OldTypeId": "public interface Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider",
+ "Kind": "Removal"
+ }
+]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/DependencyInjection/MvcJsonMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/DependencyInjection/MvcJsonMvcCoreBuilderExtensions.cs
index 2471c1af9c..86349d0807 100644
--- a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/DependencyInjection/MvcJsonMvcCoreBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/DependencyInjection/MvcJsonMvcCoreBuilderExtensions.cs
@@ -3,6 +3,8 @@
using System;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.Formatters.Json;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
@@ -73,6 +75,8 @@ internal static void AddJsonFormatterServices(IServiceCollection services)
{
services.TryAddEnumerable(
ServiceDescriptor.Transient, MvcJsonMvcOptionsSetup>());
+ services.TryAddEnumerable(
+ ServiceDescriptor.Transient());
services.TryAddSingleton();
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchOperationsArrayProvider.cs b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchOperationsArrayProvider.cs
new file mode 100644
index 0000000000..49e0ff4003
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonPatchOperationsArrayProvider.cs
@@ -0,0 +1,65 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Reflection;
+using Microsoft.AspNetCore.JsonPatch;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Microsoft.AspNetCore.Mvc.Formatters.Json
+{
+ ///
+ /// Implements a provider of to change parameters of
+ /// type to an array of .
+ ///
+ public class JsonPatchOperationsArrayProvider : IApiDescriptionProvider
+ {
+ private readonly IModelMetadataProvider _modelMetadataProvider;
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The .
+ public JsonPatchOperationsArrayProvider(IModelMetadataProvider modelMetadataProvider)
+ {
+ _modelMetadataProvider = modelMetadataProvider;
+ }
+
+ ///
+ ///
+ /// The order -999 ensures that this provider is executed right after the Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider.
+ ///
+ public int Order
+ {
+ get { return -999; }
+ }
+
+ ///
+ public void OnProvidersExecuting(ApiDescriptionProviderContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ foreach (var result in context.Results)
+ {
+ foreach (var parameterDescription in result.ParameterDescriptions)
+ {
+ if (typeof(IJsonPatchDocument).GetTypeInfo().IsAssignableFrom(parameterDescription.Type))
+ {
+ parameterDescription.Type = typeof(Operation[]);
+ parameterDescription.ModelMetadata = _modelMetadataProvider.GetMetadataForType(typeof(Operation[]));
+ }
+ }
+ }
+ }
+
+ ///
+ public void OnProvidersExecuted(ApiDescriptionProviderContext context)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
index 07a84637a7..5fee3d347d 100644
--- a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
@@ -216,6 +216,22 @@ public void GetApiDescription_PopulatesParametersThatAppearOnRouteTemplate_AndHa
}
}
+ [Fact]
+ public void GetApiDescription_ParameterDescription_IncludesParameterDescriptor()
+ {
+ // Arrange
+ var action = CreateActionDescriptor(nameof(FromBody));
+
+ // Act
+ var descriptions = GetApiDescriptions(action);
+
+ // Assert
+ var description = Assert.Single(descriptions);
+ var parameterDescription = Assert.Single(description.ParameterDescriptions);
+ var actionParameterDescriptor = Assert.Single(action.Parameters);
+ Assert.Equal(actionParameterDescriptor, parameterDescription.ParameterDescriptor);
+ }
+
// Only a parameter which comes from a route or model binding or unknown should
// include route info.
[Theory]
diff --git a/test/Microsoft.AspNetCore.Mvc.Formatters.Json.Test/JsonPatchOperationsArrayProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Formatters.Json.Test/JsonPatchOperationsArrayProviderTests.cs
new file mode 100644
index 0000000000..e3c9c4215b
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Formatters.Json.Test/JsonPatchOperationsArrayProviderTests.cs
@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.AspNetCore.JsonPatch;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.Formatters.Json
+{
+ public class JsonPatchOperationsArrayProviderTests
+ {
+ [Fact]
+ public void OnProvidersExecuting_FindsJsonPatchDocuments_ProvidesOperationsArray()
+ {
+ // Arrange
+ var metadataprovider = new TestModelMetadataProvider();
+ var provider = new JsonPatchOperationsArrayProvider(metadataprovider);
+ var jsonPatchParameterDescription = new ApiParameterDescription
+ {
+ Type = typeof(JsonPatchDocument)
+ };
+
+ var stringParameterDescription = new ApiParameterDescription
+ {
+ Type = typeof(string),
+ };
+
+ var apiDescription = new ApiDescription();
+ apiDescription.ParameterDescriptions.Add(jsonPatchParameterDescription);
+ apiDescription.ParameterDescriptions.Add(stringParameterDescription);
+
+ var actionDescriptorList = new List();
+ var apiDescriptionProviderContext = new ApiDescriptionProviderContext(actionDescriptorList);
+ apiDescriptionProviderContext.Results.Add(apiDescription);
+
+ // Act
+ provider.OnProvidersExecuting(apiDescriptionProviderContext);
+
+ // Assert
+ Assert.Collection(apiDescription.ParameterDescriptions,
+ description =>
+ {
+ Assert.Equal(typeof(Operation[]), description.Type);
+ Assert.Equal(typeof(Operation[]), description.ModelMetadata.ModelType);
+ },
+ description =>
+ {
+ Assert.Equal(typeof(string), description.Type);
+ });
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
index db7f76d6e9..9ab606e2a1 100644
--- a/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
@@ -16,6 +16,7 @@
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.Formatters.Json;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor;
@@ -411,6 +412,7 @@ private Dictionary MutliRegistrationServiceTypes
new Type[]
{
typeof(DefaultApiDescriptionProvider),
+ typeof(JsonPatchOperationsArrayProvider),
}
},
};