Skip to content

Commit

Permalink
Add support for HttpResults domaindrivendev#2595 for #3
Browse files Browse the repository at this point in the history
  • Loading branch information
Havunen committed Feb 25, 2024
1 parent 0eead17 commit 25ea78e
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlTypes;
using System.Globalization;
using System.Linq;
using System.Reflection;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.OpenApi.Models;
using DotSwashbuckle.AspNetCore.Swagger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc.Controllers;

namespace DotSwashbuckle.AspNetCore.SwaggerGen
{
Expand Down Expand Up @@ -416,7 +419,7 @@ private OpenApiParameter GenerateParameter(
apiParameter.PropertyInfo(),
apiParameter.ParameterInfo(),
apiParameter.RouteInfo
) : new OpenApiSchema() {Type = "string"};
) : new OpenApiSchema() { Type = "string" };

var parameter = new OpenApiParameter
{
Expand Down Expand Up @@ -616,11 +619,51 @@ private OpenApiSchema GenerateSchemaFromFormParameters(
};
}

private IList<ApiResponseType> GetResponseTypes(
ApiDescription apiDescription
)
{
var supportedResponseTypes = new List<ApiResponseType>(apiDescription.SupportedResponseTypes);

if (apiDescription.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var returnType = UnwrapTask(controllerActionDescriptor.MethodInfo.ReturnType);

if (typeof(IEndpointMetadataProvider).IsAssignableFrom(returnType))
{
var populateMetadataMethod = returnType.GetMethod("Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider.PopulateMetadata", BindingFlags.Static | BindingFlags.NonPublic);

if (populateMetadataMethod != null)
{
var endpointBuilder = new MetadataEndpointBuilder();
populateMetadataMethod.Invoke(null, [controllerActionDescriptor.MethodInfo, endpointBuilder]);

var responseTypes = endpointBuilder.Metadata.Cast<IProducesResponseTypeMetadata>().ToList();

foreach (var responseType in responseTypes)
{
supportedResponseTypes.Add(
new ApiResponseType()
{
IsDefaultResponse = false,
Type = responseType.Type,
StatusCode = responseType.StatusCode,
ApiResponseFormats = responseType.ContentTypes.Select(contentType => new ApiResponseFormat { MediaType = contentType }).ToList()
});
}
}
}

}

return supportedResponseTypes;
}

private OpenApiResponses GenerateResponses(
ApiDescription apiDescription,
SchemaRepository schemaRepository)
{
var supportedResponseTypes = apiDescription.SupportedResponseTypes
var supportedResponseTypes = GetResponseTypes(apiDescription)
.DefaultIfEmpty(new ApiResponseType { StatusCode = 200 });

var responses = new OpenApiResponses();
Expand Down Expand Up @@ -657,7 +700,7 @@ private OpenApiResponse GenerateResponse(
private IEnumerable<string> InferResponseContentTypes(ApiDescription apiDescription, ApiResponseType apiResponseType)
{
// If there's no associated model, return an empty list (i.e. no content)
if (apiResponseType.ModelMetadata == null) return Enumerable.Empty<string>();
if (apiResponseType.Type == null || apiResponseType.Type == typeof(void)) return Enumerable.Empty<string>();

// If there's content types explicitly specified via ProducesAttribute, use them
var explicitContentTypes = apiDescription.CustomAttributes().OfType<ProducesAttribute>()
Expand Down Expand Up @@ -727,5 +770,15 @@ private OpenApiMediaType CreateResponseMediaType(Type type, SchemaRepository sch
new KeyValuePair<string, string>("5\\d{2}", "Server Error"),
new KeyValuePair<string, string>("default", "Error")
};

private static Type UnwrapTask(Type type)
=> type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>)
? type.GetGenericArguments()[0]
: type;

private sealed class MetadataEndpointBuilder : EndpointBuilder
{
public override Endpoint Build() => null!;
}
}
}
43 changes: 42 additions & 1 deletion test/WebSites/Basic/Controllers/CrudActionsController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;

namespace Basic.Controllers
Expand Down Expand Up @@ -106,6 +110,43 @@ public IActionResult GetDoc([FromQuery] IChild query)
{
return null;
}

[HttpPost("GetDoc2")]
public async Task<ActionResult<IEnumerable<string>>> GetAll(GetData.Query query)
{
return null;
}

/// <summary> Request the forecast of a specific day. </summary>
/// <param name="date" example="2023-01-24">Date of the requested forecast. </param>
[HttpGet("weather/{date}")]
public Results<Ok<WeatherForecast>, NotFound> GetWeather(DateOnly date)
{
return TypedResults.Ok(new WeatherForecast());
}

/// <summary> Request the forecast of a specific day. </summary>
/// <param name="date" example="2023-01-24">Date of the requested forecast. </param>
[HttpGet("weather/async/{date}")]
public async Task<Results<Ok<WeatherForecast>, NotFound>> GetWeatherAsync(DateOnly date)
{
return TypedResults.Ok(await Task.FromResult(new WeatherForecast()));
}
}

public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
}

public class GetData
{
public class Query
{
public int MyParameter { get; set; }
}
}

/// <summary>The parent.</summary>
Expand Down

0 comments on commit 25ea78e

Please sign in to comment.