-
Notifications
You must be signed in to change notification settings - Fork 711
InvalidOperationException when using the API Explorer with MediaTypeApiVersionReader #156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is happening due to limitations in Web API. When an ApiDescription is created by the API explorer, it contains the MediaTypeFormatter instances for that description and API version. The API version support using media types automatically and dynamically adds the configured media type parameter for the API version. By default, this would be This all seems reasonable, but unfortunately, the design in Web API uses the MediaTypeFormatter instances directly from the configuration rather than a surrogate or separate description for input and output formats (as is now the case in ASP.NET Core). To help understand the problem. Consider that you have API version 1.0 and 2.0 with the following configuration: configuration.Formatters.Clear();
configuration.Formatters.Add( new JsonMediaTypeFormatter() ); As the API explorer builds descriptions for each action, it only makes a copy of the pointer to the MediaTypeFormatter instance. It must also add the media type parameter for the API version as it goes along. Since there is only a single instance, it won't work. As every API version is enumerated the same MediaTypeFormatter instance would be changed over and over again. Ultimately, you'd end up with an ApiDescription for both 1.0 and 2.0 reporting that their supported media types are The only way I know to overcome that is to clone the MediaTypeFormatter instance. This is easier said than done. It might be achievable using Reflection, but it would be really complex. The simplest solution is to require MediaTypeFormatter instances to support ICloneable. Unfortunately, none of them do out-of-the-box. I made an observation that most (but apparently not all) MediaTypeFormatter types have a copy constructor, which can be used as a way to clone an object. That was meant to alleviate the most common pain to requiring a cloneable MediaTypeFormatter. I suppose the support can be expanded to parameterless constructors when no copy constructor exists. There's a real possibility that doesn't do anything useful though and produces the wrong output. For example, no media types may be registered by default. In your particular case, I'm willing to entertain this enhancement, but be aware, things still make not always come out correct in Swagger. Creating and supporting cloneable MediaTypeFormatter types is the only guaranteed way to get the correct media type results in the API explorer. This limitation is constrained to API versioning by media type; again, due to the design of Web API itself. I'm confused why you would want API versioning for a UI and to further document them in Swagger, but I guess you have your reasons. It shouldn't be a big effort to add support for cloning MediaTypeFormatter types with parameterless constructors. In the meantime, you can unblock yourself, by creating and registering the required, cloneable MediaTypeFormatter instances. |
I've updated the package. FormUrlEncodedMediaTypeFormatter and JQueryMvcFormUrlEncodedFormatter should now be handled automatically. Let me know if you run into other issues. |
Uh oh!
There was an error while loading. Please reload this page.
I'm trying to use the API explorer to show multiple API versions in swagger and am following the sample app but am getting an exception about the JQueryMvcFormUrlEncodedFormatter not implementing IClonable. I replicated the issue in the SwaggerWebApiSample sample app by setting the MediaTypeApiVersionReader on the ApiVersionReader property on the ApiVersioningOptions object. Here is the exception details:
System.InvalidOperationException occurred HResult=0x80131509 Message=The media type formatter JQueryMvcFormUrlEncodedFormatter could not be cloned. The type must either implement ICloneable or define a copy constructor with the signature 'JQueryMvcFormUrlEncodedFormatter(JQueryMvcFormUrlEncodedFormatter formatter)'. Source=Microsoft.AspNet.WebApi.Versioning.ApiExplorer StackTrace: at System.Web.Http.MediaTypeFormatterAdapterFactory.ResolveConstructor(Type type) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\System.Web.Http\MediaTypeFormatterAdapterFactory.cs:line 89 at System.Web.Http.MediaTypeFormatterAdapterFactory.NewActivator(Type type) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\System.Web.Http\MediaTypeFormatterAdapterFactory.cs:line 54 at System.Web.Http.MediaTypeFormatterAdapterFactory.NewCloneFunction(Type type) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\System.Web.Http\MediaTypeFormatterAdapterFactory.cs:line 45 at System.Collections.Concurrent.ConcurrentDictionary
2.GetOrAdd(TKey key, Func2 valueFactory) at System.Web.Http.MediaTypeFormatterAdapterFactory.GetOrCreateCloneFunction(MediaTypeFormatter formatter) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\System.Web.Http\MediaTypeFormatterAdapterFactory.cs:line 29 at System.Web.Http.MediaTypeFormatterExtensions.Clone(MediaTypeFormatter formatter) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\System.Web.Http\MediaTypeFormatterExtensions.cs:line 13 at Microsoft.Web.Http.Description.ApiVersionParameterDescriptionContext.CloneFormattersAndAddMediaTypeParameter(NameValueHeaderValue parameter, ICollection
1 formatters) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\ApiVersionParameterDescriptionContext.cs:line 221at Microsoft.Web.Http.Description.ApiVersionParameterDescriptionContext.AddMediaTypeParameter(String name) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\ApiVersionParameterDescriptionContext.cs:line 159
at Microsoft.Web.Http.Description.ApiVersionParameterDescriptionContext.AddParameter(String name, ApiVersionParameterLocation location) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\ApiVersionParameterDescriptionContext.cs:line 93
at Microsoft.Web.Http.Versioning.MediaTypeApiVersionReader.AddParmeters(IApiVersionParameterDescriptionContext context) in C:\Work\External\aspnet-api-versioning\src\Common\Versioning\MediaTypeApiVersionReader.cs:line 129
at Microsoft.Web.Http.Description.VersionedApiExplorer.PopulateApiVersionParameters(ApiDescription apiDescription, ApiVersion apiVersion) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\VersionedApiExplorer.cs:line 641
at Microsoft.Web.Http.Description.VersionedApiExplorer.PopulateActionDescriptions(HttpActionDescriptor actionDescriptor, IHttpRoute route, String localPath, Collection
1 apiDescriptions, ApiVersion apiVersion) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\VersionedApiExplorer.cs:line 777 at Microsoft.Web.Http.Description.VersionedApiExplorer.ExploreDirectRouteControllers(HttpControllerDescriptor controllerDescriptor, IReadOnlyList
1 candidateActionDescriptors, IHttpRoute route, ApiVersion apiVersion) in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\VersionedApiExplorer.cs:line 568at Microsoft.Web.Http.Description.VersionedApiExplorer.InitializeApiDescriptions() in C:\Work\External\aspnet-api-versioning\src\Microsoft.AspNet.WebApi.Versioning.ApiExplorer\Description\VersionedApiExplorer.cs:line 228
at System.Lazy
1.CreateValue()
Probably also worth mentioning that I'm not doing anything special with the formatters, the JQueryMvcFormUrlEncodedFormatter is added by default to handle form-url-encoded requests
The text was updated successfully, but these errors were encountered: