[Fixes #885] API Explorer: Link Generation
1) Expose the simplified relative path template by cleaning up constraints, optional and catch all tokens from the template. 2) Expose the parameters on the route template as API parameters. 3) Combine parameters from the route and the action descriptor when the parameter doesn't come from the body. #886 will refine this. 4) Expose optionality and constraints for path parameters. Open question: Should we explicitly expose IsCatchAll?
This commit is contained in:
parent
a633ef4f97
commit
3f54492930
|
|
@ -9,7 +9,7 @@ namespace MvcSample.Web.ApiExplorerSamples
|
|||
[Route("api/Products")]
|
||||
public class ProductsController : Controller
|
||||
{
|
||||
[HttpGet("{id}")]
|
||||
[HttpGet("{id:int}")]
|
||||
public Product GetById(int id)
|
||||
{
|
||||
return null;
|
||||
|
|
@ -22,7 +22,7 @@ namespace MvcSample.Web.ApiExplorerSamples
|
|||
}
|
||||
|
||||
[Produces("application/json", Type = typeof(ProductOrderConfirmation))]
|
||||
[HttpPut("{id}/Buy")]
|
||||
[HttpPut("{id:int}/Buy")]
|
||||
public IActionResult Buy(int projectId, int quantity = 1)
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@
|
|||
<ul>
|
||||
@foreach (var parameter in Model.ParameterDescriptions)
|
||||
{
|
||||
<li>@parameter.Name - @parameter.Type.FullName - @parameter.Source.ToString()</li>
|
||||
<li>
|
||||
@parameter.Name - @(parameter?.Type?.FullName ?? "Unknown") - @parameter.Source.ToString()
|
||||
- Constraint: @(parameter?.Constraint?.GetType()?.Name?.Replace("RouteConstraint", "") ?? " none")
|
||||
- Default value: @(parameter?.DefaultValue ?? " none")
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
|
@ -22,7 +26,7 @@
|
|||
{
|
||||
<p>Response Formats:</p>
|
||||
<ul>
|
||||
@foreach(var response in Model.SupportedResponseFormats)
|
||||
@foreach (var response in Model.SupportedResponseFormats)
|
||||
{
|
||||
<li>@response.MediaType.RawValue - @response.Formatter.GetType().Name</li>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Routing;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Description
|
||||
{
|
||||
|
|
@ -18,6 +19,10 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
|
||||
public ApiParameterSource Source { get; set; }
|
||||
|
||||
public IRouteConstraint Constraint { get; set; }
|
||||
|
||||
public object DefaultValue { get; set; }
|
||||
|
||||
public Type Type { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,6 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
{
|
||||
Body,
|
||||
Query,
|
||||
Path
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Description
|
||||
|
|
@ -19,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
{
|
||||
private readonly IOutputFormattersProvider _formattersProvider;
|
||||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||
private readonly IInlineConstraintResolver _constraintResolver;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DefaultApiDescriptionProvider"/>.
|
||||
|
|
@ -27,10 +31,12 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
public DefaultApiDescriptionProvider(
|
||||
IOutputFormattersProvider formattersProvider,
|
||||
IInlineConstraintResolver constraintResolver,
|
||||
IModelMetadataProvider modelMetadataProvider)
|
||||
{
|
||||
_formattersProvider = formattersProvider;
|
||||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_constraintResolver = constraintResolver;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -60,25 +66,23 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
}
|
||||
|
||||
private ApiDescription CreateApiDescription(
|
||||
ControllerActionDescriptor action,
|
||||
string httpMethod,
|
||||
ControllerActionDescriptor action,
|
||||
string httpMethod,
|
||||
string groupName)
|
||||
{
|
||||
var parsedTemplate = ParseTemplate(action);
|
||||
|
||||
var apiDescription = new ApiDescription()
|
||||
{
|
||||
ActionDescriptor = action,
|
||||
GroupName = groupName,
|
||||
HttpMethod = httpMethod,
|
||||
RelativePath = GetRelativePath(action),
|
||||
RelativePath = GetRelativePath(parsedTemplate),
|
||||
};
|
||||
|
||||
if (action.Parameters != null)
|
||||
{
|
||||
foreach (var parameter in action.Parameters)
|
||||
{
|
||||
apiDescription.ParameterDescriptions.Add(GetParameter(parameter));
|
||||
}
|
||||
}
|
||||
var templateParameters = parsedTemplate?.Parameters?.ToList() ?? new List<TemplatePart>();
|
||||
|
||||
GetParameters(apiDescription, action.Parameters, templateParameters);
|
||||
|
||||
var responseMetadataAttributes = GetResponseMetadataAttributes(action);
|
||||
|
||||
|
|
@ -103,13 +107,13 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
apiDescription.ResponseType = runtimeReturnType;
|
||||
|
||||
apiDescription.ResponseModelMetadata = _modelMetadataProvider.GetMetadataForType(
|
||||
modelAccessor: null,
|
||||
modelAccessor: null,
|
||||
modelType: runtimeReturnType);
|
||||
|
||||
var formats = GetResponseFormats(
|
||||
action,
|
||||
responseMetadataAttributes,
|
||||
declaredReturnType,
|
||||
action,
|
||||
responseMetadataAttributes,
|
||||
declaredReturnType,
|
||||
runtimeReturnType);
|
||||
|
||||
foreach (var format in formats)
|
||||
|
|
@ -121,6 +125,44 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
return apiDescription;
|
||||
}
|
||||
|
||||
private void GetParameters(
|
||||
ApiDescription apiDescription,
|
||||
IList<ParameterDescriptor> parameterDescriptors,
|
||||
IList<TemplatePart> templateParameters)
|
||||
{
|
||||
if (parameterDescriptors != null)
|
||||
{
|
||||
foreach (var parameter in parameterDescriptors)
|
||||
{
|
||||
// Process together parameters that appear on the path template and on the
|
||||
// action descriptor and do not come from the body.
|
||||
TemplatePart templateParameter = null;
|
||||
if (parameter.BodyParameterInfo == null)
|
||||
{
|
||||
templateParameter = templateParameters
|
||||
.FirstOrDefault(p => p.Name.Equals(parameter.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (templateParameter != null)
|
||||
{
|
||||
templateParameters.Remove(templateParameter);
|
||||
}
|
||||
}
|
||||
|
||||
apiDescription.ParameterDescriptions.Add(GetParameter(parameter, templateParameter));
|
||||
}
|
||||
}
|
||||
|
||||
if (templateParameters.Count > 0)
|
||||
{
|
||||
// Process parameters that only appear on the path template if any.
|
||||
foreach (var templateParameter in templateParameters)
|
||||
{
|
||||
var parameterDescription = GetParameter(parameterDescriptor: null, templateParameter: templateParameter);
|
||||
apiDescription.ParameterDescriptions.Add(parameterDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetHttpMethods(ControllerActionDescriptor action)
|
||||
{
|
||||
if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
|
||||
|
|
@ -133,23 +175,93 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
}
|
||||
}
|
||||
|
||||
private string GetRelativePath(ControllerActionDescriptor action)
|
||||
private RouteTemplate ParseTemplate(ControllerActionDescriptor action)
|
||||
{
|
||||
// This is a placeholder for functionality which will correctly generate the relative path
|
||||
// stub of an action. See: #885
|
||||
if (action.AttributeRouteInfo != null &&
|
||||
action.AttributeRouteInfo.Template != null)
|
||||
{
|
||||
return action.AttributeRouteInfo.Template;
|
||||
return TemplateParser.Parse(action.AttributeRouteInfo.Template, _constraintResolver);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ApiParameterDescription GetParameter(ParameterDescriptor parameter)
|
||||
private string GetRelativePath(RouteTemplate parsedTemplate)
|
||||
{
|
||||
if (parsedTemplate == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var segments = new List<string>();
|
||||
|
||||
foreach (var segment in parsedTemplate.Segments)
|
||||
{
|
||||
var currentSegment = "";
|
||||
foreach (var part in segment.Parts)
|
||||
{
|
||||
if (part.IsLiteral)
|
||||
{
|
||||
currentSegment += part.Text;
|
||||
}
|
||||
else if (part.IsParameter)
|
||||
{
|
||||
currentSegment += "{" + part.Name + "}";
|
||||
}
|
||||
}
|
||||
|
||||
segments.Add(currentSegment);
|
||||
}
|
||||
|
||||
return string.Join("/", segments);
|
||||
}
|
||||
|
||||
private ApiParameterDescription GetParameter(
|
||||
ParameterDescriptor parameterDescriptor,
|
||||
TemplatePart templateParameter)
|
||||
{
|
||||
// This is a placeholder based on currently available functionality for parameters. See #886.
|
||||
var resourceParameter = new ApiParameterDescription()
|
||||
ApiParameterDescription parameterDescription = null;
|
||||
|
||||
if (templateParameter != null && parameterDescriptor == null)
|
||||
{
|
||||
// The parameter is part of the route template but not part of the ActionDescriptor.
|
||||
|
||||
// For now if a parameter is part of the template we will asume its value comes from the path.
|
||||
// We will be more accurate when we implement #886.
|
||||
parameterDescription = CreateParameterFromTemplate(templateParameter);
|
||||
}
|
||||
else if (templateParameter != null && parameterDescriptor != null)
|
||||
{
|
||||
// The parameter is part of the route template and part of the ActionDescriptor.
|
||||
parameterDescription = CreateParameterFromTemplateAndParameterDescriptor(
|
||||
templateParameter,
|
||||
parameterDescriptor);
|
||||
}
|
||||
else if(templateParameter == null && parameterDescriptor != null)
|
||||
{
|
||||
// The parameter is part of the ActionDescriptor but is not part of the route template.
|
||||
parameterDescription = CreateParameterFromParameterDescriptor(parameterDescriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We will never call this method with templateParameter == null && parameterDescriptor == null
|
||||
Contract.Assert(parameterDescriptor != null);
|
||||
}
|
||||
|
||||
if (parameterDescription.Type != null)
|
||||
{
|
||||
parameterDescription.ModelMetadata = _modelMetadataProvider.GetMetadataForType(
|
||||
modelAccessor: null,
|
||||
modelType: parameterDescription.Type);
|
||||
}
|
||||
|
||||
return parameterDescription;
|
||||
}
|
||||
|
||||
private static ApiParameterDescription CreateParameterFromParameterDescriptor(ParameterDescriptor parameter)
|
||||
{
|
||||
var resourceParameter = new ApiParameterDescription
|
||||
{
|
||||
IsOptional = parameter.IsOptional,
|
||||
Name = parameter.Name,
|
||||
|
|
@ -158,8 +270,8 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
|
||||
if (parameter.ParameterBindingInfo != null)
|
||||
{
|
||||
resourceParameter.Type = parameter.ParameterBindingInfo.ParameterType;
|
||||
resourceParameter.Source = ApiParameterSource.Query;
|
||||
resourceParameter.Type = parameter.ParameterBindingInfo.ParameterType;
|
||||
}
|
||||
|
||||
if (parameter.BodyParameterInfo != null)
|
||||
|
|
@ -168,16 +280,49 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
resourceParameter.Source = ApiParameterSource.Body;
|
||||
}
|
||||
|
||||
if (resourceParameter.Type != null)
|
||||
return resourceParameter;
|
||||
}
|
||||
|
||||
private static ApiParameterDescription CreateParameterFromTemplateAndParameterDescriptor(
|
||||
TemplatePart templateParameter,
|
||||
ParameterDescriptor parameter)
|
||||
{
|
||||
var resourceParameter = new ApiParameterDescription
|
||||
{
|
||||
resourceParameter.ModelMetadata = _modelMetadataProvider.GetMetadataForType(
|
||||
modelAccessor: null,
|
||||
modelType: resourceParameter.Type);
|
||||
Source = ApiParameterSource.Path,
|
||||
IsOptional = parameter.IsOptional && IsOptionalParameter(templateParameter),
|
||||
Name = parameter.Name,
|
||||
ParameterDescriptor = parameter,
|
||||
Constraint = templateParameter.InlineConstraint,
|
||||
DefaultValue = templateParameter.DefaultValue,
|
||||
};
|
||||
|
||||
if (parameter.ParameterBindingInfo != null)
|
||||
{
|
||||
resourceParameter.Type = parameter.ParameterBindingInfo.ParameterType;
|
||||
}
|
||||
|
||||
return resourceParameter;
|
||||
}
|
||||
|
||||
private static bool IsOptionalParameter(TemplatePart templateParameter)
|
||||
{
|
||||
return templateParameter.IsOptional || templateParameter.DefaultValue != null;
|
||||
}
|
||||
|
||||
private static ApiParameterDescription CreateParameterFromTemplate(TemplatePart templateParameter)
|
||||
{
|
||||
return new ApiParameterDescription
|
||||
{
|
||||
Source = ApiParameterSource.Path,
|
||||
IsOptional = IsOptionalParameter(templateParameter),
|
||||
Name = templateParameter.Name,
|
||||
ParameterDescriptor = null,
|
||||
Constraint = templateParameter.InlineConstraint,
|
||||
DefaultValue = templateParameter.DefaultValue,
|
||||
};
|
||||
}
|
||||
|
||||
private IReadOnlyList<ApiResponseFormat> GetResponseFormats(
|
||||
ControllerActionDescriptor action,
|
||||
IApiResponseMetadataProvider[] responseMetadataAttributes,
|
||||
|
|
@ -220,7 +365,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -151,25 +153,269 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
Assert.Equal(typeof(string), username.Type);
|
||||
}
|
||||
|
||||
// This is a placeholder based on current functionality - see #885
|
||||
[Fact]
|
||||
public void GetApiDescription_PopluatesRelativePath()
|
||||
[Theory]
|
||||
[InlineData("api/products/{id}", false, null, null)]
|
||||
[InlineData("api/products/{id?}", true, null, null)]
|
||||
[InlineData("api/products/{id=5}", true, null, "5")]
|
||||
[InlineData("api/products/{id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int?}", true, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int=5}", true, null, "5")]
|
||||
[InlineData("api/products/{*id}", false, null, null)]
|
||||
[InlineData("api/products/{*id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{*id:int=5}", true, typeof(IntRouteConstraint), "5")]
|
||||
public void GetApiDescription_PopulatesParameters_ThatAppearOnlyOnRouteTemplate(
|
||||
string template,
|
||||
bool isOptional,
|
||||
Type constraintType,
|
||||
object defaultValue)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo();
|
||||
action.AttributeRouteInfo.Template = "api/Products/{id}";
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo { Template = template };
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
Assert.Equal("api/Products/{id}", description.RelativePath);
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal(ApiParameterSource.Path, parameter.Source);
|
||||
Assert.Equal(isOptional, parameter.IsOptional);
|
||||
Assert.Equal("id", parameter.Name);
|
||||
Assert.Null(parameter.ParameterDescriptor);
|
||||
|
||||
if (constraintType != null)
|
||||
{
|
||||
Assert.IsType(constraintType, parameter.Constraint);
|
||||
}
|
||||
|
||||
if (defaultValue != null)
|
||||
{
|
||||
Assert.Equal(defaultValue, parameter.DefaultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(parameter.DefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("api/products/{id}", false, null, null)]
|
||||
[InlineData("api/products/{id?}", true, null, null)]
|
||||
[InlineData("api/products/{id=5}", true, null, "5")]
|
||||
[InlineData("api/products/{id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int?}", true, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int=5}", true, typeof(IntRouteConstraint), "5")]
|
||||
[InlineData("api/products/{*id}", false, null, null)]
|
||||
[InlineData("api/products/{*id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{*id:int=5}", true, typeof(IntRouteConstraint), "5")]
|
||||
public void GetApiDescription_PopulatesParametersThatAppearOnRouteTemplate_AndHaveAssociatedParameterDescriptor(
|
||||
string template,
|
||||
bool isOptional,
|
||||
Type constraintType,
|
||||
object defaultValue)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo { Template = template };
|
||||
|
||||
var parameterDescriptor = new ParameterDescriptor
|
||||
{
|
||||
Name = "id",
|
||||
IsOptional = true,
|
||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int))
|
||||
};
|
||||
action.Parameters = new List<ParameterDescriptor> { parameterDescriptor };
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal(ApiParameterSource.Path, parameter.Source);
|
||||
Assert.Equal(isOptional, parameter.IsOptional);
|
||||
Assert.Equal("id", parameter.Name);
|
||||
Assert.Equal(parameterDescriptor, parameter.ParameterDescriptor);
|
||||
|
||||
if (constraintType != null)
|
||||
{
|
||||
Assert.IsType(constraintType, parameter.Constraint);
|
||||
}
|
||||
|
||||
if (defaultValue != null)
|
||||
{
|
||||
Assert.Equal(defaultValue, parameter.DefaultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(parameter.DefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("api/products/{id}", false, null, null)]
|
||||
[InlineData("api/products/{id?}", true, null, null)]
|
||||
[InlineData("api/products/{id=5}", true, null, "5")]
|
||||
[InlineData("api/products/{id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int?}", true, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{id:int=5}", true, typeof(IntRouteConstraint), "5")]
|
||||
[InlineData("api/products/{*id}", false, null, null)]
|
||||
[InlineData("api/products/{*id:int}", false, typeof(IntRouteConstraint), null)]
|
||||
[InlineData("api/products/{*id:int=5}", true, typeof(IntRouteConstraint), "5")]
|
||||
public void GetApiDescription_CreatesDifferentParameters_IfParameterDescriptorIsFromBody(
|
||||
string template,
|
||||
bool isOptional,
|
||||
Type constraintType,
|
||||
object defaultValue)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo { Template = template };
|
||||
|
||||
var parameterDescriptor = new ParameterDescriptor
|
||||
{
|
||||
Name = "id",
|
||||
IsOptional = false,
|
||||
BodyParameterInfo = new BodyParameterInfo(typeof(int))
|
||||
};
|
||||
action.Parameters = new List<ParameterDescriptor> { parameterDescriptor };
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
|
||||
var bodyParameter = Assert.Single(description.ParameterDescriptions, p => p.Source == ApiParameterSource.Body);
|
||||
Assert.False(bodyParameter.IsOptional);
|
||||
Assert.Equal("id", bodyParameter.Name);
|
||||
Assert.Equal(parameterDescriptor, bodyParameter.ParameterDescriptor);
|
||||
|
||||
var pathParameter = Assert.Single(description.ParameterDescriptions, p => p.Source == ApiParameterSource.Path);
|
||||
Assert.Equal(isOptional, pathParameter.IsOptional);
|
||||
Assert.Equal("id", pathParameter.Name);
|
||||
Assert.Null(pathParameter.ParameterDescriptor);
|
||||
|
||||
if (constraintType != null)
|
||||
{
|
||||
Assert.IsType(constraintType, pathParameter.Constraint);
|
||||
}
|
||||
|
||||
if (defaultValue != null)
|
||||
{
|
||||
Assert.Equal(defaultValue, pathParameter.DefaultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(pathParameter.DefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("api/products/{id}", false, false)]
|
||||
[InlineData("api/products/{id}", true, false)]
|
||||
[InlineData("api/products/{id?}", false, false)]
|
||||
[InlineData("api/products/{id?}", true, true)]
|
||||
[InlineData("api/products/{id=5}", false, false)]
|
||||
[InlineData("api/products/{id=5}", true, true)]
|
||||
public void GetApiDescription_ParameterFromPathAndDescriptor_IsOptionalOnly_IfBothAreOptional(
|
||||
string template,
|
||||
bool isDescriptorParameterOptional,
|
||||
bool expectedOptional)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo { Template = template };
|
||||
|
||||
var parameterDescriptor = new ParameterDescriptor
|
||||
{
|
||||
Name = "id",
|
||||
IsOptional = isDescriptorParameterOptional,
|
||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int))
|
||||
};
|
||||
action.Parameters = new List<ParameterDescriptor> { parameterDescriptor };
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal(expectedOptional, parameter.IsOptional);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("api/Products/{id}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{id?}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{id:int}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{id:int?}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{*id}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{*id:int}", "api/Products/{id}")]
|
||||
[InlineData("api/Products/{id1}-{id2:int}", "api/Products/{id1}-{id2}")]
|
||||
[InlineData("api/{id1}/{id2?}/{id3:int}/{id4:int?}/{*id5:int}", "api/{id1}/{id2}/{id3}/{id4}/{id5}")]
|
||||
public void GetApiDescription_PopulatesRelativePath(string template, string relativePath)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo();
|
||||
action.AttributeRouteInfo.Template = template;
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
Assert.Equal(relativePath, description.RelativePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiDescription_PopluatesResponseType_WithProduct()
|
||||
public void GetApiDescription_DetectsMultipleParameters_OnTheSameSegment()
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo();
|
||||
action.AttributeRouteInfo.Template = "api/Products/{id1}-{id2:int}";
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
var id1 = Assert.Single(description.ParameterDescriptions, p => p.Name == "id1");
|
||||
Assert.Equal(ApiParameterSource.Path, id1.Source);
|
||||
Assert.Null(id1.Constraint);
|
||||
|
||||
var id2 = Assert.Single(description.ParameterDescriptions, p => p.Name == "id2");
|
||||
Assert.Equal(ApiParameterSource.Path, id2.Source);
|
||||
Assert.IsType<IntRouteConstraint>(id2.Constraint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiDescription_DetectsMultipleParameters_OnDifferentSegments()
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor();
|
||||
action.AttributeRouteInfo = new AttributeRouteInfo();
|
||||
action.AttributeRouteInfo.Template = "api/Products/{id1}-{id2}/{id3:int}/{id4:int?}/{*id5:int}";
|
||||
|
||||
// Act
|
||||
var descriptions = GetApiDescriptions(action);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(descriptions);
|
||||
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "id1");
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "id2");
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "id3");
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "id4");
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "id5");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiDescription_PopulatesResponseType_WithProduct()
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor(nameof(ReturnsProduct));
|
||||
|
|
@ -184,7 +430,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApiDescription_PopluatesResponseType_WithTaskOfProduct()
|
||||
public void GetApiDescription_PopulatesResponseType_WithTaskOfProduct()
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor(nameof(ReturnsTaskOfProduct));
|
||||
|
|
@ -205,7 +451,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
[InlineData(nameof(ReturnsTaskOfObject))]
|
||||
[InlineData(nameof(ReturnsTaskOfActionResult))]
|
||||
[InlineData(nameof(ReturnsTaskOfJsonResult))]
|
||||
public void GetApiDescription_DoesNotPopluatesResponseInformation_WhenUnknown(string methodName)
|
||||
public void GetApiDescription_DoesNotPopulatesResponseInformation_WhenUnknown(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor(methodName);
|
||||
|
|
@ -223,7 +469,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
[Theory]
|
||||
[InlineData(nameof(ReturnsVoid))]
|
||||
[InlineData(nameof(ReturnsTask))]
|
||||
public void GetApiDescription_DoesNotPopluatesResponseInformation_WhenVoid(string methodName)
|
||||
public void GetApiDescription_DoesNotPopulatesResponseInformation_WhenVoid(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor(methodName);
|
||||
|
|
@ -247,7 +493,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
[InlineData(nameof(ReturnsTask))]
|
||||
[InlineData(nameof(ReturnsTaskOfActionResult))]
|
||||
[InlineData(nameof(ReturnsTaskOfJsonResult))]
|
||||
public void GetApiDescription_PopluatesResponseInformation_WhenSetByFilter(string methodName)
|
||||
public void GetApiDescription_PopulatesResponseInformation_WhenSetByFilter(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var action = CreateActionDescriptor(methodName);
|
||||
|
|
@ -323,7 +569,7 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
action.FilterDescriptors.Add(new FilterDescriptor(filter, FilterScope.Action));
|
||||
|
||||
var formatters = CreateFormatters();
|
||||
|
||||
|
||||
// This will just format Order
|
||||
formatters[0].SupportedTypes.Add(typeof(Order));
|
||||
|
||||
|
|
@ -349,13 +595,19 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
return GetApiDescriptions(action, CreateFormatters());
|
||||
}
|
||||
|
||||
private IReadOnlyList<ApiDescription> GetApiDescriptions(ActionDescriptor action, List<MockFormatter> formatters)
|
||||
private IReadOnlyList<ApiDescription> GetApiDescriptions(
|
||||
ActionDescriptor action,
|
||||
List<MockFormatter> formatters)
|
||||
{
|
||||
var context = new ApiDescriptionProviderContext(new ActionDescriptor[] { action });
|
||||
|
||||
var formattersProvider = new Mock<IOutputFormattersProvider>(MockBehavior.Strict);
|
||||
formattersProvider.Setup(fp => fp.OutputFormatters).Returns(formatters);
|
||||
|
||||
var constraintResolver = new Mock<IInlineConstraintResolver>();
|
||||
constraintResolver.Setup(c => c.ResolveConstraint("int"))
|
||||
.Returns(new IntRouteConstraint());
|
||||
|
||||
var modelMetadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
|
||||
modelMetadataProvider
|
||||
.Setup(mmp => mmp.GetMetadataForType(null, It.IsAny<Type>()))
|
||||
|
|
@ -364,7 +616,11 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
return new ModelMetadata(modelMetadataProvider.Object, null, accessor, type, null);
|
||||
});
|
||||
|
||||
var provider = new DefaultApiDescriptionProvider(formattersProvider.Object, modelMetadataProvider.Object);
|
||||
var provider = new DefaultApiDescriptionProvider(
|
||||
formattersProvider.Object,
|
||||
constraintResolver.Object,
|
||||
modelMetadataProvider.Object);
|
||||
|
||||
provider.Invoke(context, () => { });
|
||||
return context.Results;
|
||||
}
|
||||
|
|
@ -396,7 +652,6 @@ namespace Microsoft.AspNet.Mvc.Description
|
|||
methodName ?? "ReturnsObject",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Xunit;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
|
|
@ -139,6 +139,254 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(description.GroupName, "SetOnAction");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_DisplaysFixedRoute()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("http://localhost/ApiExplorerRouteAndPathParametersInformation");
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(description.RelativePath, "ApiExplorerRouteAndPathParametersInformation");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_DisplaysRouteWithParameters()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("http://localhost/ApiExplorerRouteAndPathParametersInformation/5");
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(description.RelativePath, "ApiExplorerRouteAndPathParametersInformation/{id}");
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal("id", parameter.Name);
|
||||
Assert.False(parameter.IsOptional);
|
||||
Assert.Equal("Path", parameter.Source);
|
||||
Assert.Null(parameter.ConstraintType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_StripsInlineConstraintsFromThePath()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/Constraint/5";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal("ApiExplorerRouteAndPathParametersInformation/Constraint/{integer}", description.RelativePath);
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal("integer", parameter.Name);
|
||||
Assert.False(parameter.IsOptional);
|
||||
Assert.Equal("Path", parameter.Source);
|
||||
Assert.Equal("IntRouteConstraint", parameter.ConstraintType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_StripsCatchAllsFromThePath()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/CatchAll/5";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal("ApiExplorerRouteAndPathParametersInformation/CatchAll/{parameter}", description.RelativePath);
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal("parameter", parameter.Name);
|
||||
Assert.False(parameter.IsOptional);
|
||||
Assert.Equal("Path", parameter.Source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_StripsCatchAllsWithConstraintsFromThePath()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/CatchAllAndConstraint/5";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(
|
||||
"ApiExplorerRouteAndPathParametersInformation/CatchAllAndConstraint/{integer}",
|
||||
description.RelativePath);
|
||||
|
||||
var parameter = Assert.Single(description.ParameterDescriptions);
|
||||
Assert.Equal("integer", parameter.Name);
|
||||
Assert.False(parameter.IsOptional);
|
||||
Assert.Equal("Path", parameter.Source);
|
||||
Assert.Equal("IntRouteConstraint", parameter.ConstraintType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplateStripsMultipleConstraints_OnTheSamePathSegment()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/"
|
||||
+ "MultipleParametersInSegment/12-01-1987";
|
||||
|
||||
var expectedRelativePath = "ApiExplorerRouteAndPathParametersInformation/"
|
||||
+ "MultipleParametersInSegment/{month}-{day}-{year}";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(expectedRelativePath, description.RelativePath);
|
||||
|
||||
var month = Assert.Single(description.ParameterDescriptions, p => p.Name == "month");
|
||||
Assert.False(month.IsOptional);
|
||||
Assert.Equal("Path", month.Source);
|
||||
Assert.Equal("RangeRouteConstraint", month.ConstraintType);
|
||||
|
||||
var day = Assert.Single(description.ParameterDescriptions, p => p.Name == "day");
|
||||
Assert.False(day.IsOptional);
|
||||
Assert.Equal("Path", day.Source);
|
||||
Assert.Equal("IntRouteConstraint", day.ConstraintType);
|
||||
|
||||
var year = Assert.Single(description.ParameterDescriptions, p => p.Name == "year");
|
||||
Assert.False(year.IsOptional);
|
||||
Assert.Equal("Path", year.Source);
|
||||
Assert.Equal("IntRouteConstraint", year.ConstraintType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplateStripsMultipleConstraints_InMultipleSegments()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/"
|
||||
+ "MultipleParametersInMultipleSegments/12/01/1987";
|
||||
|
||||
var expectedRelativePath = "ApiExplorerRouteAndPathParametersInformation/"
|
||||
+ "MultipleParametersInMultipleSegments/{month}/{day}/{year}";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(expectedRelativePath, description.RelativePath);
|
||||
|
||||
var month = Assert.Single(description.ParameterDescriptions, p => p.Name == "month");
|
||||
Assert.False(month.IsOptional);
|
||||
Assert.Equal("Path", month.Source);
|
||||
Assert.Equal("RangeRouteConstraint", month.ConstraintType);
|
||||
|
||||
var day = Assert.Single(description.ParameterDescriptions, p => p.Name == "day");
|
||||
Assert.False(day.IsOptional);
|
||||
Assert.Equal("Path", day.Source);
|
||||
Assert.Equal("IntRouteConstraint", day.ConstraintType);
|
||||
|
||||
var year = Assert.Single(description.ParameterDescriptions, p => p.Name == "year");
|
||||
Assert.True(year.IsOptional);
|
||||
Assert.Equal("Path", year.Source);
|
||||
Assert.Equal("IntRouteConstraint", year.ConstraintType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_DescribeParameters_FromAllSources()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
var url = "http://localhost/ApiExplorerRouteAndPathParametersInformation/MultipleTypesOfParameters/1/2/3";
|
||||
|
||||
var expectedRelativePath = "ApiExplorerRouteAndPathParametersInformation/"
|
||||
+ "MultipleTypesOfParameters/{path}/{pathAndQuery}/{pathAndFromBody}";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal(expectedRelativePath, description.RelativePath);
|
||||
|
||||
var path = Assert.Single(description.ParameterDescriptions, p => p.Name == "path");
|
||||
Assert.Equal("Path", path.Source);
|
||||
|
||||
var pathAndQuery = Assert.Single(description.ParameterDescriptions, p => p.Name == "pathAndQuery");
|
||||
Assert.Equal("Path", pathAndQuery.Source);
|
||||
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "pathAndFromBody" && p.Source == "Body");
|
||||
Assert.Single(description.ParameterDescriptions, p => p.Name == "pathAndFromBody" && p.Source == "Path");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_RouteTemplate_MakesParametersOptional()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("http://localhost/ApiExplorerRouteAndPathParametersInformation/Optional/");
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
|
||||
|
||||
// Assert
|
||||
var description = Assert.Single(result);
|
||||
Assert.Equal("ApiExplorerRouteAndPathParametersInformation/Optional/{id}", description.RelativePath);
|
||||
|
||||
var id = Assert.Single(description.ParameterDescriptions, p => p.Name == "id");
|
||||
Assert.True(id.IsOptional);
|
||||
Assert.Equal("Path", id.Source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApiExplorer_HttpMethod_All()
|
||||
{
|
||||
|
|
@ -500,6 +748,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
public string Source { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string ConstraintType { get; set; }
|
||||
}
|
||||
|
||||
// Used to serialize data between client and server
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ namespace ApiExplorer
|
|||
IsOptional = parameter.IsOptional,
|
||||
Name = parameter.Name,
|
||||
Source = parameter.Source.ToString(),
|
||||
Type = parameter.Type.FullName,
|
||||
Type = parameter?.Type?.FullName,
|
||||
ConstraintType = parameter?.Constraint?.GetType()?.Name,
|
||||
};
|
||||
|
||||
data.ParameterDescriptions.Add(parameterData);
|
||||
|
|
@ -101,6 +102,8 @@ namespace ApiExplorer
|
|||
public string Source { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string ConstraintType { get; set; }
|
||||
}
|
||||
|
||||
// Used to serialize data between client and server
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ApiExplorer
|
||||
{
|
||||
[Route("ApiExplorerRouteAndPathParametersInformation")]
|
||||
public class ApiExplorerRouteAndPathParametersInformationController
|
||||
{
|
||||
[HttpGet]
|
||||
public void Get() { }
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public void Get(int id) { }
|
||||
|
||||
[HttpGet("Optional/{id?}")]
|
||||
public void GetOptional(int id = 0) { }
|
||||
|
||||
[HttpGet("Constraint/{integer:int}")]
|
||||
public void GetInteger(int integer) { }
|
||||
|
||||
[HttpGet("CatchAll/{*parameter}")]
|
||||
public void GetCatchAll(string parameter) { }
|
||||
|
||||
[HttpGet("MultipleParametersInSegment/{month:range(1,12)}-{day:int}-{year:int}")]
|
||||
public void GetMultipleParametersInSegment(string month, string day, string year) { }
|
||||
|
||||
[HttpGet("MultipleParametersInMultipleSegments/{month:range(1,12)}/{day:int?}/{year:int?}")]
|
||||
public void GetMultipleParametersInMultipleSegments(string month, string day, string year = "") { }
|
||||
|
||||
[HttpGet("MultipleTypesOfParameters/{path}/{pathAndQuery}/{pathAndFromBody}")]
|
||||
public void MultipleTypesOfParameters(string query, string pathAndQuery, [FromBody] string pathAndFromBody) { }
|
||||
|
||||
[HttpGet("CatchAllAndConstraint/{*integer:int}")]
|
||||
public void GetIntegers(string integer) { }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue