Repository: dotnet/aspnetcore
File: src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs (C#)

Code before:

            supportedResponseTypes.Add(defaultApiResponseType);
        }

        static string? GetMatchingResponseTypeDescription(IEnumerable<ApiResponseType> responseMetadataTypes, ApiResponseType apiResponseType)
        {
            // We set the Description to the LAST non-null value we find that matches the status code.
            string? matchingDescription = null;
            foreach (var metadata in responseMetadataTypes)
            {
                if (metadata.StatusCode == apiResponseType.StatusCode &&
                    TypesAreCompatible(apiResponseType?.Type, metadata?.Type) &&
                    metadata.Description is not null)
                {
                    matchingDescription = metadata.Description;
                }
            }
            return matchingDescription;
        }

        static bool TypesAreCompatible(Type? apiResponseType, Type? metadaType)
        {
            // We need to a special check for cases where the inferred type is different than the one specified in attributes.
            // For example, an endpoint that defines [ProducesResponseType<IEnumerable<WeatherForecast>>],
            // but the endpoint returns weatherForecasts.ToList(). Because List<> is a different type than IEnumerable<>, it would incorrectly return false.
            // Currently, we do a "simple" biderectional check to see if the types are assignable to each other.
            // This isn't very thorough, but it works for most cases.
            // For more information, check the related bug: https://github.com/dotnet/aspnetcore/issues/60518
            return apiResponseType == metadaType ||
                metadaType?.IsAssignableFrom(apiResponseType) == true ||
                apiResponseType?.IsAssignableFrom(metadaType) == true;
        }
    }

    private static ApiResponseType CreateDefaultApiResponseType(Type responseType)
    {
        var apiResponseType = new ApiResponseType
        {
            ModelMetadata = CreateModelMetadata(responseType),
            StatusCode = 200,
            Type = responseType,
        };

        if (CreateDefaultApiResponseFormat(responseType) is { } responseFormat)
        {
            apiResponseType.ApiResponseFormats.Add(responseFormat);
        }

        return apiResponseType;
    }


Review comment (suggestion):
Correct the spelling of "biderectional" to "bidirectional" in the comment.
```suggestion
            // Currently, we do a "simple" bidirectional check to see if the types are assignable to each other.
```

Code after:
            supportedResponseTypes.Add(defaultApiResponseType);
        }

        static string? GetMatchingResponseTypeDescription(IEnumerable<ApiResponseType> responseMetadataTypes, ApiResponseType apiResponseType)
        {
            // We set the Description to the LAST non-null value we find that matches the status code.
            string? matchingDescription = null;
            foreach (var metadata in responseMetadataTypes)
            {
                if (metadata.StatusCode == apiResponseType.StatusCode &&
                    TypesAreCompatible(apiResponseType.Type, metadata.Type) &&
                    metadata.Description is not null)
                {
                    matchingDescription = metadata.Description;
                }
            }
            return matchingDescription;
        }

        static bool TypesAreCompatible(Type? apiResponseType, Type? metadataType)
        {
            // We need to a special check for cases where the inferred type is different than the one specified in attributes.
            // For example, an endpoint that defines [ProducesResponseType<IEnumerable<WeatherForecast>>],
            // but the endpoint returns weatherForecasts.ToList(). Because List<> is a different type than IEnumerable<>, it would incorrectly set OpenAPI metadata incorrectly.
            // We use a conservative unidirectional check where the attribute type must be assignable from the inferred type.
            // This handles inheritance (BaseClass ← DerivedClass) and interface implementation (IEnumerable<T> ← List<T>).
            // This should be sufficient, as it's more common to specify an interface or base class type in the attribute and a concrete type in the endpoint implementation,
            // compared to doing the opposite.
            // For more information, check the related bug: https://github.com/dotnet/aspnetcore/issues/60518
            return apiResponseType == metadataType ||
                metadataType?.IsAssignableFrom(apiResponseType) == true;
        }
    }

    private static ApiResponseType CreateDefaultApiResponseType(Type responseType)
    {
        var apiResponseType = new ApiResponseType
        {
            ModelMetadata = CreateModelMetadata(responseType),
            StatusCode = 200,
            Type = responseType,
        };

        if (CreateDefaultApiResponseFormat(responseType) is { } responseFormat)
        {
            apiResponseType.ApiResponseFormats.Add(responseFormat);
        }

        return apiResponseType;
    }