Пример #1
0
        // Shared with EndpointMetadataApiDescriptionProvider
        internal static List <ApiResponseType> ReadResponseMetadata(
            IReadOnlyList <IApiResponseMetadataProvider> responseMetadataAttributes,
            Type?type,
            Type defaultErrorType,
            MediaTypeCollection contentTypes)
        {
            var results = new Dictionary <int, ApiResponseType>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    metadataAttribute.SetContentTypes(contentTypes);

                    var statusCode = metadataAttribute.StatusCode;

                    var apiResponseType = new ApiResponseType
                    {
                        Type              = metadataAttribute.Type,
                        StatusCode        = statusCode,
                        IsDefaultResponse = metadataAttribute is IApiDefaultResponseMetadataProvider,
                    };

                    if (apiResponseType.Type == typeof(void))
                    {
                        if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created))
                        {
                            // ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
                            // In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
                            // [ProducesResponseType(201)] instead of [ProducesResponseType(typeof(Person), 201] when typeof(Person) can be inferred
                            // from the return type.
                            apiResponseType.Type = type;
                        }
                        else if (IsClientError(statusCode))
                        {
                            // Determine whether or not the type was provided by the user. If so, favor it over the default
                            // error type for 4xx client errors if no response type is specified..
                            var setByDefault = metadataAttribute is ProducesResponseTypeAttribute {
                                IsResponseTypeSetByDefault : true
                            };
                            apiResponseType.Type = setByDefault ? defaultErrorType : apiResponseType.Type;
                        }
                        else if (apiResponseType.IsDefaultResponse)
                        {
                            apiResponseType.Type = defaultErrorType;
                        }
                    }

                    if (apiResponseType.Type != null)
                    {
                        results[apiResponseType.StatusCode] = apiResponseType;
                    }
                }
            }

            return(results.Values.ToList());
        }
        internal static ApiResponseType Clone(this ApiResponseType responseType)
        {
            var clone = new ApiResponseType()
            {
                ModelMetadata = responseType.ModelMetadata,
                StatusCode    = responseType.StatusCode,
                Type          = responseType.Type,
            };

            foreach (var responseFormat in responseType.ApiResponseFormats)
            {
                clone.ApiResponseFormats.Add(responseFormat.Clone());
            }

            return(clone);
        }
        internal static ApiResponseType Clone(this ApiResponseType responseType)
        {
            Contract.Requires(responseType != null);
            Contract.Ensures(Contract.Result <ApiResponseType>() != null);

            var clone = new ApiResponseType()
            {
                ModelMetadata = responseType.ModelMetadata,
                StatusCode    = responseType.StatusCode,
                Type          = responseType.Type,
            };

            foreach (var responseFormat in responseType.ApiResponseFormats)
            {
                clone.ApiResponseFormats.Add(responseFormat.Clone());
            }

            return(clone);
        }
        private IReadOnlyList <ApiResponseType> GetApiResponseTypes(
            IApiResponseMetadataProvider[] responseMetadataAttributes,
            Type type)
        {
            var results = new List <ApiResponseType>();

            // Build list of all possible return types (and status codes) for an action.
            var objectTypes = new Dictionary <int, Type>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            var contentTypes = new MediaTypeCollection();

            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    metadataAttribute.SetContentTypes(contentTypes);

                    if (metadataAttribute.Type != null)
                    {
                        objectTypes[metadataAttribute.StatusCode] = metadataAttribute.Type;
                    }
                }
            }

            // Set the default status only when no status has already been set explicitly
            if (objectTypes.Count == 0 &&
                type != null)
            {
                objectTypes[StatusCodes.Status200OK] = type;
            }

            if (contentTypes.Count == 0)
            {
                contentTypes.Add((string)null);
            }

            var responseTypeMetadataProviders = _outputFormatters.OfType <IApiResponseTypeMetadataProvider>();

            foreach (var objectType in objectTypes)
            {
                if (objectType.Value == typeof(void))
                {
                    results.Add(new ApiResponseType()
                    {
                        StatusCode = objectType.Key,
                        Type       = objectType.Value
                    });

                    continue;
                }

                var apiResponseType = new ApiResponseType()
                {
                    Type          = objectType.Value,
                    StatusCode    = objectType.Key,
                    ModelMetadata = _modelMetadataProvider.GetMetadataForType(objectType.Value)
                };

                foreach (var contentType in contentTypes)
                {
                    foreach (var responseTypeMetadataProvider in responseTypeMetadataProviders)
                    {
                        var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(
                            contentType,
                            objectType.Value);

                        if (formatterSupportedContentTypes == null)
                        {
                            continue;
                        }

                        foreach (var formatterSupportedContentType in formatterSupportedContentTypes)
                        {
                            apiResponseType.ApiResponseFormats.Add(new ApiResponseFormat()
                            {
                                Formatter = (IOutputFormatter)responseTypeMetadataProvider,
                                MediaType = formatterSupportedContentType,
                            });
                        }
                    }
                }

                results.Add(apiResponseType);
            }

            return(results);
        }
Пример #5
0
        // Shared with EndpointMetadataApiDescriptionProvider
        internal static void CalculateResponseFormatForType(ApiResponseType apiResponse, MediaTypeCollection declaredContentTypes, IEnumerable <IApiResponseTypeMetadataProvider>?responseTypeMetadataProviders, IModelMetadataProvider?modelMetadataProvider)
        {
            // If response formats have already been calculate for this type,
            // then exit early. This avoids populating the ApiResponseFormat for
            // types that have already been handled, specifically ProducesResponseTypes.
            if (apiResponse.ApiResponseFormats.Count > 0)
            {
                return;
            }

            // Given the content-types that were declared for this action, determine the formatters that support the content-type for the given
            // response type.
            // 1. Responses that do not specify an type do not have any associated content-type. This usually is meant for status-code only responses such
            // as return NotFound();
            // 2. When a type is specified, use GetSupportedContentTypes to expand wildcards and get the range of content-types formatters support.
            // 3. When no formatter supports the specified content-type, use the user specified value as is. This is useful in actions where the user
            // dictates the content-type.
            // e.g. [Produces("application/pdf")] Action() => FileStream("somefile.pdf", "application/pdf");
            var responseType = apiResponse.Type;

            if (responseType == null || responseType == typeof(void))
            {
                return;
            }

            apiResponse.ModelMetadata = modelMetadataProvider?.GetMetadataForType(responseType);

            foreach (var contentType in declaredContentTypes)
            {
                var isSupportedContentType = false;

                if (responseTypeMetadataProviders != null)
                {
                    foreach (var responseTypeMetadataProvider in responseTypeMetadataProviders)
                    {
                        var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(
                            contentType,
                            responseType);

                        if (formatterSupportedContentTypes == null)
                        {
                            continue;
                        }

                        isSupportedContentType = true;

                        foreach (var formatterSupportedContentType in formatterSupportedContentTypes)
                        {
                            apiResponse.ApiResponseFormats.Add(new ApiResponseFormat
                            {
                                Formatter = (IOutputFormatter)responseTypeMetadataProvider,
                                MediaType = formatterSupportedContentType,
                            });
                        }
                    }
                }



                if (!isSupportedContentType && contentType != null)
                {
                    // No output formatter was found that supports this content type. Add the user specified content type as-is to the result.
                    apiResponse.ApiResponseFormats.Add(new ApiResponseFormat
                    {
                        MediaType = contentType,
                    });
                }
            }
        }
Пример #6
0
        // Shared with EndpointMetadataApiDescriptionProvider
        internal static List <ApiResponseType> ReadResponseMetadata(
            IReadOnlyList <IApiResponseMetadataProvider> responseMetadataAttributes,
            Type?type,
            Type defaultErrorType,
            MediaTypeCollection contentTypes,
            IEnumerable <IApiResponseTypeMetadataProvider>?responseTypeMetadataProviders = null,
            IModelMetadataProvider?modelMetadataProvider = null)
        {
            var results = new Dictionary <int, ApiResponseType>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    // All ProducesXAttributes, except for ProducesResponseTypeAttribute do
                    // not allow multiple instances on the same method/class/etc. For those
                    // scenarios, the `SetContentTypes` method on the attribute continuously
                    // clears out more general content types in favor of more specific ones
                    // since we iterate through the attributes in order. For example, if a
                    // Produces exists on both a controller and an action within the controller,
                    // we favor the definition in the action. This is a semantic that does not
                    // apply to ProducesResponseType, which allows multiple instances on an target.
                    if (metadataAttribute is not ProducesResponseTypeAttribute)
                    {
                        metadataAttribute.SetContentTypes(contentTypes);
                    }

                    var statusCode = metadataAttribute.StatusCode;

                    var apiResponseType = new ApiResponseType
                    {
                        Type              = metadataAttribute.Type,
                        StatusCode        = statusCode,
                        IsDefaultResponse = metadataAttribute is IApiDefaultResponseMetadataProvider,
                    };

                    if (apiResponseType.Type == typeof(void))
                    {
                        if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created))
                        {
                            // ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
                            // In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
                            // [ProducesResponseType(201)] instead of [ProducesResponseType(typeof(Person), 201] when typeof(Person) can be inferred
                            // from the return type.
                            apiResponseType.Type = type;
                        }
                        else if (IsClientError(statusCode))
                        {
                            // Determine whether or not the type was provided by the user. If so, favor it over the default
                            // error type for 4xx client errors if no response type is specified..
                            var setByDefault = metadataAttribute is ProducesResponseTypeAttribute {
                                IsResponseTypeSetByDefault : true
                            };
                            apiResponseType.Type = setByDefault ? defaultErrorType : apiResponseType.Type;
                        }
                        else if (apiResponseType.IsDefaultResponse)
                        {
                            apiResponseType.Type = defaultErrorType;
                        }
                    }

                    // We special case the handling of ProcuesResponseTypeAttributes since
                    // multiple ProducesResponseTypeAttributes are permitted on a single
                    // action/controller/etc. In that scenario, instead of picking the most-specific
                    // set of content types (like we do with the Produces attribute above) we process
                    // the content types for each attribute independently.
                    if (metadataAttribute is ProducesResponseTypeAttribute)
                    {
                        var attributeContentTypes = new MediaTypeCollection();
                        metadataAttribute.SetContentTypes(attributeContentTypes);
                        CalculateResponseFormatForType(apiResponseType, attributeContentTypes, responseTypeMetadataProviders, modelMetadataProvider);
                    }

                    if (apiResponseType.Type != null)
                    {
                        results[apiResponseType.StatusCode] = apiResponseType;
                    }
                }
            }

            return(results.Values.ToList());
        }
Пример #7
0
        private ICollection <ApiResponseType> GetApiResponseTypes(
            IReadOnlyList <IApiResponseMetadataProvider> responseMetadataAttributes,
            Type type,
            Type defaultErrorType)
        {
            var results = new Dictionary <int, ApiResponseType>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            var contentTypes = new MediaTypeCollection();

            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    metadataAttribute.SetContentTypes(contentTypes);

                    var statusCode = metadataAttribute.StatusCode;

                    var apiResponseType = new ApiResponseType
                    {
                        Type              = metadataAttribute.Type,
                        StatusCode        = statusCode,
                        IsDefaultResponse = metadataAttribute is IApiDefaultResponseMetadataProvider,
                    };

                    if (apiResponseType.Type == typeof(void))
                    {
                        if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created))
                        {
                            // ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
                            // In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
                            // [ProducesResponseType(201)] instead of [ProducesResponseType(201, typeof(Person)] when typeof(Person) can be inferred
                            // from the return type.
                            apiResponseType.Type = type;
                        }
                        else if (IsClientError(statusCode) || apiResponseType.IsDefaultResponse)
                        {
                            // Use the default error type for "default" responses or 4xx client errors if no response type is specified.
                            apiResponseType.Type = defaultErrorType;
                        }
                    }

                    if (apiResponseType.Type != null)
                    {
                        results[apiResponseType.StatusCode] = apiResponseType;
                    }
                }
            }

            // Set the default status only when no status has already been set explicitly
            if (results.Count == 0 && type != null)
            {
                results[StatusCodes.Status200OK] = new ApiResponseType
                {
                    StatusCode = StatusCodes.Status200OK,
                    Type       = type,
                };
            }

            if (contentTypes.Count == 0)
            {
                contentTypes.Add((string)null);
            }

            var responseTypeMetadataProviders = _mvcOptions.OutputFormatters.OfType <IApiResponseTypeMetadataProvider>();

            foreach (var apiResponse in results.Values)
            {
                var responseType = apiResponse.Type;
                if (responseType == null || responseType == typeof(void))
                {
                    continue;
                }

                apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(responseType);

                foreach (var contentType in contentTypes)
                {
                    foreach (var responseTypeMetadataProvider in responseTypeMetadataProviders)
                    {
                        var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(
                            contentType,
                            responseType);

                        if (formatterSupportedContentTypes == null)
                        {
                            continue;
                        }

                        foreach (var formatterSupportedContentType in formatterSupportedContentTypes)
                        {
                            apiResponse.ApiResponseFormats.Add(new ApiResponseFormat
                            {
                                Formatter = (IOutputFormatter)responseTypeMetadataProvider,
                                MediaType = formatterSupportedContentType,
                            });
                        }
                    }
                }
            }

            return(results.Values);
        }
Пример #8
0
        private ICollection <ApiResponseType> GetApiResponseTypes(
            IReadOnlyList <IApiResponseMetadataProvider> responseMetadataAttributes,
            Type type,
            Type defaultErrorType)
        {
            var results = new Dictionary <int, ApiResponseType>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            var contentTypes = new MediaTypeCollection();

            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    metadataAttribute.SetContentTypes(contentTypes);

                    var statusCode = metadataAttribute.StatusCode;

                    var apiResponseType = new ApiResponseType
                    {
                        Type              = metadataAttribute.Type,
                        StatusCode        = statusCode,
                        IsDefaultResponse = metadataAttribute is IApiDefaultResponseMetadataProvider,
                    };

                    if (apiResponseType.Type == typeof(void))
                    {
                        if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created))
                        {
                            // ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
                            // In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
                            // [ProducesResponseType(201)] instead of [ProducesResponseType(201, typeof(Person)] when typeof(Person) can be inferred
                            // from the return type.
                            apiResponseType.Type = type;
                        }
                        else if (IsClientError(statusCode) || apiResponseType.IsDefaultResponse)
                        {
                            // Use the default error type for "default" responses or 4xx client errors if no response type is specified.
                            apiResponseType.Type = defaultErrorType;
                        }
                    }

                    if (apiResponseType.Type != null)
                    {
                        results[apiResponseType.StatusCode] = apiResponseType;
                    }
                }
            }

            // Set the default status only when no status has already been set explicitly
            if (results.Count == 0 && type != null)
            {
                results[StatusCodes.Status200OK] = new ApiResponseType
                {
                    StatusCode = StatusCodes.Status200OK,
                    Type       = type,
                };
            }

            if (contentTypes.Count == 0)
            {
                // None of the IApiResponseMetadataProvider specified a content type. This is common for actions that
                // specify one or more ProducesResponseType but no ProducesAttribute. In this case, formatters will participate in conneg
                // and respond to the incoming request.
                // Querying IApiResponseTypeMetadataProvider.GetSupportedContentTypes with "null" should retrieve all supported
                // content types that each formatter may respond in.
                contentTypes.Add((string)null);
            }

            var responseTypes = results.Values;

            CalculateResponseFormats(responseTypes, contentTypes);
            return(responseTypes);
        }
Пример #9
0
        private IList <ApiResponseType> GetApiResponseTypes(
            IReadOnlyList <IApiResponseMetadataProvider> responseMetadataAttributes,
            Type type)
        {
            var results = new List <ApiResponseType>();

            // Build list of all possible return types (and status codes) for an action.
            var objectTypes = new Dictionary <int, Type>();

            // Get the content type that the action explicitly set to support.
            // Walk through all 'filter' attributes in order, and allow each one to see or override
            // the results of the previous ones. This is similar to the execution path for content-negotiation.
            var contentTypes = new MediaTypeCollection();

            if (responseMetadataAttributes != null)
            {
                foreach (var metadataAttribute in responseMetadataAttributes)
                {
                    metadataAttribute.SetContentTypes(contentTypes);

                    if (metadataAttribute.Type == typeof(void) &&
                        type != null &&
                        (metadataAttribute.StatusCode == StatusCodes.Status200OK || metadataAttribute.StatusCode == StatusCodes.Status201Created))
                    {
                        // ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
                        // In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
                        // [ProducesResponseType(201)] instead of [ProducesResponseType(201, typeof(Person)] when typeof(Person) can be inferred
                        // from the return type.
                        objectTypes[metadataAttribute.StatusCode] = type;
                    }
                    else if (metadataAttribute.Type != null)
                    {
                        objectTypes[metadataAttribute.StatusCode] = metadataAttribute.Type;
                    }
                }
            }


            // Set the default status only when no status has already been set explicitly
            if (objectTypes.Count == 0 && type != null)
            {
                objectTypes[StatusCodes.Status200OK] = type;
            }

            if (contentTypes.Count == 0)
            {
                contentTypes.Add((string)null);
            }

            var responseTypeMetadataProviders = _mvcOptions.OutputFormatters.OfType <IApiResponseTypeMetadataProvider>();

            foreach (var objectType in objectTypes)
            {
                if (objectType.Value == null || objectType.Value == typeof(void))
                {
                    results.Add(new ApiResponseType()
                    {
                        StatusCode = objectType.Key,
                        Type       = objectType.Value
                    });

                    continue;
                }

                var apiResponseType = new ApiResponseType()
                {
                    Type          = objectType.Value,
                    StatusCode    = objectType.Key,
                    ModelMetadata = _modelMetadataProvider.GetMetadataForType(objectType.Value)
                };

                foreach (var contentType in contentTypes)
                {
                    foreach (var responseTypeMetadataProvider in responseTypeMetadataProviders)
                    {
                        var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(
                            contentType,
                            objectType.Value);

                        if (formatterSupportedContentTypes == null)
                        {
                            continue;
                        }

                        foreach (var formatterSupportedContentType in formatterSupportedContentTypes)
                        {
                            apiResponseType.ApiResponseFormats.Add(new ApiResponseFormat()
                            {
                                Formatter = (IOutputFormatter)responseTypeMetadataProvider,
                                MediaType = formatterSupportedContentType,
                            });
                        }
                    }
                }

                results.Add(apiResponseType);
            }

            return(results);
        }
 private static IEnumerable <string> GetSortedMediaTypes(ApiResponseType apiResponseType)
 {
     return(apiResponseType.ApiResponseFormats
            .OrderBy(format => format.MediaType)
            .Select(format => format.MediaType));
 }