aspnetcore/src/Microsoft.AspNetCore.Mvc.Ap.../ApiBehaviorApiDescriptionPr...

117 lines
4.0 KiB
C#

// 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.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
{
public class ApiBehaviorApiDescriptionProvider : IApiDescriptionProvider
{
private readonly IModelMetadataProvider _modelMetadaProvider;
public ApiBehaviorApiDescriptionProvider(IModelMetadataProvider modelMetadataProvider)
{
_modelMetadaProvider = modelMetadataProvider;
}
/// <remarks>
/// The order is set to execute after the default provider.
/// </remarks>
public int Order => -1000 + 10;
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
{
}
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
{
foreach (var description in context.Results)
{
if (!AppliesTo(description))
{
continue;
}
foreach (var responseType in CreateProblemResponseTypes(description))
{
description.SupportedResponseTypes.Add(responseType);
}
}
}
public bool AppliesTo(ApiDescription description)
{
return description.ActionDescriptor.FilterDescriptors.Any(f => f.Filter is IApiBehaviorMetadata);
}
// Check if the parameter is named "id" (e.g. int id) or ends in Id (e.g. personId)
public bool IsIdParameter(ParameterDescriptor parameter)
{
if (parameter.Name == null)
{
return false;
}
if (string.Equals("id", parameter.Name, StringComparison.Ordinal))
{
return true;
}
// We're looking for a name ending with Id, but preceded by a lower case letter. This should match
// the normal PascalCase naming conventions.
if (parameter.Name.Length >= 3 &&
parameter.Name.EndsWith("Id", StringComparison.Ordinal) &&
char.IsLower(parameter.Name, parameter.Name.Length - 3))
{
return true;
}
return false;
}
public IEnumerable<ApiResponseType> CreateProblemResponseTypes(ApiDescription description)
{
if (description.ActionDescriptor.Parameters.Any() || description.ActionDescriptor.BoundProperties.Any())
{
// For validation errors.
yield return CreateProblemResponse(StatusCodes.Status400BadRequest);
if (description.ActionDescriptor.Parameters.Any(p => IsIdParameter(p)))
{
yield return CreateProblemResponse(StatusCodes.Status404NotFound);
}
}
yield return CreateProblemResponse(statusCode: 0, isDefaultResponse: true);
}
private ApiResponseType CreateProblemResponse(int statusCode, bool isDefaultResponse = false)
{
return new ApiResponseType
{
ApiResponseFormats = new List<ApiResponseFormat>
{
new ApiResponseFormat
{
MediaType = "application/problem+json",
},
new ApiResponseFormat
{
MediaType = "application/problem+xml",
},
},
IsDefaultResponse = isDefaultResponse,
ModelMetadata = _modelMetadaProvider.GetMetadataForType(typeof(ProblemDetails)),
StatusCode = statusCode,
Type = typeof(ProblemDetails),
};
}
}
}