If I have Route on an Action "/api/Timeline/Count/{id?}
if I don't specify an Id then the uri it uses is http://localhost:5086/api/Timeline/Count/{id}
if I do specify an Id then the uri is http://localhost:5086/api/Timeline/Count/{id}?id=1
they should be http://localhost:5086/api/Timeline/Count and http://localhost:5086/api/Timeline/Count/1 respectively.
If I don't make the id optional, it can only generate the uri with the trailing /N.
I've used Postman to verify that the optional id does in fact work.
I haven't check if the client code generated by AutoRest works, yet.
AutoRest is also generating an invalid URL. It is not replacing the .../{memberId} in the route
~c#
public async Task
{
// Tracing
bool _shouldTrace = ServiceClientTracing.IsEnabled;
string _invocationId = null;
if (_shouldTrace)
{
_invocationId = ServiceClientTracing.NextInvocationId.ToString();
Dictionary
tracingParameters.Add("memberId", memberId);
tracingParameters.Add("criteria", criteria);
tracingParameters.Add("oldestEventId", oldestEventId);
tracingParameters.Add("cancellationToken", cancellationToken);
ServiceClientTracing.Enter(_invocationId, this, "GetTimelineCountForCriteria", tracingParameters);
}
// Construct URL
var _baseUrl = BaseUri.AbsoluteUri;
var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "api/Timeline/Count/{memberId}").ToString();
List
if (memberId != null)
{
_queryParameters.Add(string.Format("memberId={0}", System.Uri.EscapeDataString(SafeJsonConvert.SerializeObject(memberId, SerializationSettings).Trim('"'))));
}
~
Optional route parameters are not a thing. According to https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-7:
If the parameter is in "path", this property is required and its value MUST be true. Otherwise, the property MAY be included and its default value is false.
closing as per @olydis's comment
The spec say
If the parameter is in "path"
one could argue that end of path is not _in_ path, and therefore could be optional.
Which is the use case where this is an issue.
@pi3k14 One could indeed, but looking at their tiny modification for OpenAPI 3.0 (which is now at http://swagger.io/specification/#parameterObject, I've updated the above link to point to the 2.0 spec):
If the聽parameter location聽is "path", this property is聽REQUIRED聽and its value MUST be聽true. Otherwise, the property MAY be included and its default value is聽false.
...it seems like they meant that. I assume the previous "in" made it's way in there since you say in: path in your Swagger, so having that in your brain while writing the spec may easily introduce an "in" (which in the Swagger sense also includes the end) 馃檪
@olydis Thanks for the clarification, a bit pity though since an optional argument at the end of a path has it uses.
The documentation linked whether thats OpenAPI or not is swagger specific, and simply not related to how .Net Core routing works.
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1
optional route parameters are surely a thing, and your documentation should work with it.
I shouldn't have to ignore features of the framework to accommodate your package.
@jcanady20 Let me get this straight - you, the person who has put zero hours into this project is the victim - having to "accommodate" my package.
I think not! I have put more personal hours into this than I would care to admit and am perfectly entitled to have opinions about what use-cases it does and doesn't support. Furthermore, I will continue to do so while I'm not getting paid to provide it.
I am always open to constructive discussion and there are many examples where that discussion has led to a change of mind and priorities for the project. But, I don't appreciate the tone of your comment. Unfortunately, it's a form of entitlement that's pervasive across the open source movement and is ultimately detrimental to it.
First of all, I agree with @domaindrivendev and want to stress, that I really appreciate the work of providing swagger for AspNetCore. It saved us tons of hours and will save them also in the future. Thank you!
Personally (as another zero hour guy 馃檲), I would be happy if it would be possible to add optional paramters. But then, maybe this is the wrong place to ask and we should ask the ppl from the swagger project to include this into their api.
As a workarround, I am currently just removing the optional parameter from the route. Swagger will then add the parameter like: route?optionalparameter=value
I'm a little late to the party here, but I was able to work around this by creating a custom OperationFilter and JavaScript. The OperationFilter allows the optional path parameters to not be required fields in the Swagger-UI. The custom JavaScript was necessary to ensure the parameter was stripped from the request URL for the "Try it out!" button when the field did not have value and present when it did (I have only tested this against RouteAttributes with single optional path parameters, not multiple using Swashbuckle v5.6.0).
OptionalPathParameterFilter.cs
using Swashbuckle.Swagger;
using System.Linq;
using System.Web.Http.Description;
namespace Your.Namespace
{
/// <summary>
/// The Swagger specification does not allow for optional path parameters,
/// so we will need to remove the required attribute on any nullable path params.
/// </summary>
public class OptionalPathParameterFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
foreach (var parameter in operation.parameters)
{
var parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault(p => p.Name.Equals(parameter.name));
// The API description will indicate the parameter is optional correctly, so if the parameter is marked as required, unmark it
if (parameterDescription != null && parameterDescription.ParameterDescriptor.IsOptional && parameter.required.HasValue && parameter.required.Value)
parameter.required = false;
}
}
}
}
SwaggerConfig.cs
.EnableSwagger (c => {
// Removes Required from Optional Path Parameters
c.OperationFilter<OptionalPathParameterFilter>();
...
.EnableSwaggerUi(c => {
c.InjectJavaScript(thisAssembly, "YourDeafultNamespace.YourDirectory.Swagger.js");
...
Swagger.js
$(document).ready(function () {
// Alter Request Path to Allow for Optional Path Parameters
$('.parameter:not(.required)').each(function () {
if ($(this).closest('td').next('td').next('td').text() === 'path') {
// First time around
updatePathParameters($(this));
// Handling input
$(this).on('focusout', function () {
updatePathParameters($(this));
});
}
});
});
function updatePathParameters(element) {
// Getting Parameter
var optionalPathParameter = element.attr('name');
// Getting API Name
var apiName = element.closest('.resource').find('a.toggleEndpointList').attr('data-id');
// Getting Operation Path, ID and Nickname
var operationPath = element.closest('.endpoint').find('span.path').find('a.toggleOperation').text();
var operationID = element.closest('.endpoint').find('span.path').find('a.toggleOperation').attr('href');
var operationNickname = operationID.substr(operationID.lastIndexOf('/') + 1, operationID.length);
// Getting Operation Model
var operationModel = swaggerApi.apisArray.find(a => a.name == apiName).operationsArray.find(o => o.nickname === operationNickname);
// If the element is empty, remove from the path, otherwise simply reset the path to the original one
if (element.val() === '') {
operationModel.path = operationPath.replace('{' + optionalPathParameter + '}', '').replace('//', '/');
}
else {
operationModel.path = operationPath;
}
}
Most helpful comment
First of all, I agree with @domaindrivendev and want to stress, that I really appreciate the work of providing swagger for AspNetCore. It saved us tons of hours and will save them also in the future. Thank you!
Personally (as another zero hour guy 馃檲), I would be happy if it would be possible to add optional paramters. But then, maybe this is the wrong place to ask and we should ask the ppl from the swagger project to include this into their api.
As a workarround, I am currently just removing the optional parameter from the route. Swagger will then add the parameter like:
route?optionalparameter=value