I am trying to use Swagger with existing parameters. Adding in ODataQueryOptions removes All my other parameters in Swagger. How can I fix this to see my parameters? Code is below:
[HttpGet("[action]/{rollType}/{assessmentYear}/{lkAssessmentStatus}/{eventId?}")]
[ProducesResponseType(typeof(PageResult<BasicAssessmentDetailsDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PageResult<BasicAssessmentDetailsDto>), StatusCodes.Status500InternalServerError)]
public ActionResult GetBasicPropertyAssessments(
int rollType,
int assessmentYear,
int lkAssessmentStatus,
int? eventId,
ODataQueryOptions<BasicAssessmentDetailsDto> queryOptions)
{
var result = assessmentRollService.GetBasicPropertyAssessmentsQueryable(rollType, assessmentYear, lkAssessmentStatus, eventId).ToODataPageResult(queryOptions);
return Ok(result);
}
We added the following in Startup, and yet is still not working:
services.AddOData();
services.AddControllers().AddNewtonsoftJson();
services.AddMvc(options =>
{
foreach (var outputFormatter in options.OutputFormatters.OfType<ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (var inputFormatter in options.InputFormatters.OfType<ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
}
Add the following to Configure method
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Expand().Filter().OrderBy().MaxTop(1000).Count();
});
namespace Common.OData
{
/// <summary>
/// This class allows us to add OData to our endpoints in swagger.
/// </summary>
public class ODataParametersSwaggerDefinition : IOperationFilter
{
private static readonly Type QueryableType = typeof(IQueryable);
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var parameterDescriptions = context.ApiDescription.ParameterDescriptions;
foreach (var parameter in parameterDescriptions)
{
// identify if any of the response types of the end point
// return a queryable type.
if (parameter.Type == typeof(ODataQueryOptions) || parameter.Type.IsSubclassOf(typeof(ODataQueryOptions)))
{
// the schema of our new parameter (string)
var schema = new OpenApiSchema { Type = "string" };
operation.Parameters.Clear();
// $filter
operation.Parameters.Add(new OpenApiParameter
{
Name = "$filter",
Description = "Filter the results using OData syntax.",
Required = false, // these are all optional filters, so false.
In = ParameterLocation.Query, //specify to pass the parameter in the query
Schema = schema
});
// $orderby
operation.Parameters.Add(new OpenApiParameter
{
Name = "$orderby",
Description = "Order the results using OData syntax",
Required = false,
In = ParameterLocation.Query,
Schema = schema
});
// $skip
operation.Parameters.Add(new OpenApiParameter
{
Name = "$skip",
Description = "Skip the specified number of entries",
Required = false,
In = ParameterLocation.Query,
Schema = schema
});
// $top
operation.Parameters.Add(new OpenApiParameter
{
Name = "$top",
Description = "Get the top x number of records",
Required = false,
In = ParameterLocation.Query,
Schema = schema
});
}
}
}
We do not see additional parameters
Currently using Net Core 3.1
Related
I am using Swagger UI to test my ASP.NET Web Api app. I added a class to allow operation parameters
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = "ApiKey",
In = ParameterLocation.Header,
Required = true,
Schema = new OpenApiSchema
{
Type = "String"
}
});
operation.Parameters.Add(new OpenApiParameter
{
Name = "Authentication",
In = ParameterLocation.Header,
Required = false,
Schema = new OpenApiSchema
{
Type = "String"
}
});
}
In my Startup.cs, I added this line to the ConfigurationServices method
c.OperationFilter<CustomHeaderSwaggerAttribute>();
When I try and test one of the controller methods, my ApiKey string parameter always show an error no matter what I put in the textbox.
I am not sure about the Schema property but the following worked for me in past (setting the type to string):
operation.Parameters.Add(new Parameter
{
name = "ApiKey",
#in = ParameterLocation.Header,
required = true,
type = "string"
});
For more details, refer to this post
Is it possible to hide the 'api-version' and 'x-api-version' parameters?
services.AddApiVersioning(config =>
{
config.ReportApiVersions = true;
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader(),
new HeaderApiVersionReader()
{
HeaderNames = { "x-api-version" }
});
});
services.AddVersionedApiExplorer(
options =>
{
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
options.DefaultApiVersionParameterDescription = "Do NOT modify api-version!";
});
I already checked how-to-set-up-swashbuckle-vs-microsoft-aspnetcore-mvc-versioning which implements a 'RemoveVersionFromParameter' method, but in that case the Swagger page would loose the api version and always uses the default v1.0. As shown in the code snippet, I am using the QueryStringApiVersionReader and HeaderApiVersionReader, but I don't want to support the url api versioning.
Note: The API does have multiple swagger json pages for all versions (e.g. V1, V1.1, V2.0)
You can try an operation filter. This is similar to Helder's solution, but the implementation doesn't have to be at the document level, so it seems simpler:
public void Configure(SwaggerGenOptions options)
{
// Filter out `api-version` parameters globally
options.OperationFilter<ApiVersionFilter>();
}
internal class ApiVersionFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var parametersToRemove = operation.Parameters.Where(x => x.Name == "api-version").ToList();
foreach (var parameter in parametersToRemove)
operation.Parameters.Remove(parameter);
}
}
Have you looked into IDocumentFilter with that you can remove stuff from the final swagger.json and that will remove it from the UI
Here is an example me removing some properties from the definitions:
private class HideStuffDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
foreach (var definition in swaggerDoc.definitions)
{
foreach (var prop in definition.Value.properties.ToList())
{
if (prop.Value.maxLength == 9999)
definition.Value.properties.Remove(prop);
}
}
}
}
I have a few more samples here:
https://github.com/heldersepu/Swagger-Net-Test/blob/e701b1d20d0b42c1287c3da2641ca521a0a7b592/Swagger_Test/App_Start/SwaggerConfig.cs#L766
You can add your own custom CSS
and use it to hide those elements (and do any other customisation you want).
app.UseSwaggerUI(c =>
{
...
c.InjectStylesheet("/swagger-ui/custom.css");
...
});
Edit - example:
Suppose you're trying to hide - in my example; you can easily adapt it to yours - the tenantId parameter in this "Remove Basket" operation:
This would do that:
div#operations-baskets-remove tr[data-param-name="tenantId"] {
display: none;
}
This can be done by setting ApiExplorerOption SubstituteApiVersionInUrl = true . In your case:
services.AddVersionedApiExplorer(
options =>
{
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
options.DefaultApiVersionParameterDescription = "Do NOT modify api-version!";
options.SubstituteApiVersionInUrl = true;
});
this worked for me.
public class SwaggerOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation?.Parameters == null
|| !operation.Parameters.Any())
{
return;
}
var parametersWithPropertiesToIgnore = context.ApiDescription
.ActionDescriptor.Parameters.Where(p =>
p.ParameterType.GetProperties()
.Any(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null));
foreach (var parameter in parametersWithPropertiesToIgnore)
{
var ignoreDataMemberProperties = parameter.ParameterType.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null)
.Select(p => p.Name);
operation.Parameters = operation.Parameters.Where(p => !ignoreDataMemberProperties.Contains(p.Name))
.ToList();
}
}
}
On Startup
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
c.SchemaFilter<SwaggerSchemaFilter>();
c.OperationFilter<SwaggerOperationFilter>();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var filePath = Path.Combine(System.AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(filePath);
});
Just add the data annotation [IgnoreDataMember] in the property to hide it.
public class ExampleRequest
{
[IgnoreDataMember]
public string? HiddenProp { get; set; }
public string OtherProp { get; set; }
}
I have based on this article to create this solution.
Do you really want to hide the API version parameters or just make them read-only? The API version parameter is required when invoking the API so that should not be hidden from the documentation or user.
Your question implies that you're more concerned that the user not be able to change the API version default value for a particular documented API. That's fair, but has a different solution.
While you can mark a parameter as read-only, I have not been able to get the UI to honor that. It might be a bug or something else is misconfigured. You can definitely get read-only like behavior by using an enumeration with a single value. For example:
public class SwaggerDefaultValues : IOperationFilter
{
public void Apply( OpenApiOperation operation, OperationFilterContext context )
{
var apiDescription = context.ApiDescription;
operation.Deprecated |= apiDescription.IsDeprecated();
if ( operation.Parameters == null )
{
return;
}
foreach ( var parameter in operation.Parameters )
{
var description = apiDescription.ParameterDescriptions
.First( p => p.Name == parameter.Name );
parameter.Description ??= description.ModelMetadata?.Description;
if ( parameter.Schema.Default == null &&
description.DefaultValue != null &&
description.DefaultValue is not DBNull &&
description.ModelMetadata is ModelMetadata modelMetadata )
{
var json = JsonSerializer.Serialize(
description.DefaultValue,
modelMetadata.ModelType );
// this will set the API version, while also making it read-only
parameter.Schema.Enum = new[] { OpenApiAnyFactory.CreateFromJson( json ) };
}
parameter.Required |= description.IsRequired;
}
}
You can added startup.cs file.
services.AddApiVersioning(options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
});
services.AddVersionedApiExplorer(options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
And then,you can added top on the controller. But I try to without this([ApiVersion("1.0")]),it could run.I had successfully hide version parametre.
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/Check")]
[ApiController]
[Authorize]
public class CheckController : ControllerBase {}
I am documenting the API of my application through Swagger, which works with validation through a session token, that is, through a username and password a token is generated for a certain time that allows the user to navigate the site. What I need is for swagger to capture this session token and place it in the header of the requests for the rest of the API methods automatically, the only thing I could achieve so far is to pass the token as a parameter, but doing it manually. The idea is to make it automatic. I leave the configuration of SwaggerConfig.cs that I carry for now.
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
// HABILITAMOS SWAGGER.
.EnableSwagger(c =>
{
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory + #"\bin\";
var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".xml";
var commentsFile = Path.Combine(baseDirectory, commentsFileName);
c.SingleApiVersion("v1", "API");
c.OperationFilter<AddRequiredHeaderParameter>();
c.PrettyPrint();
c.ApiKey("apikey")
.Description("API Key Authentication")
.Name("Bearer")
.In("header");
c.IgnoreObsoleteActions();
c.IgnoreObsoleteProperties();
c.DescribeAllEnumsAsStrings();
.EnableSwaggerUi(c =>
{ c.DocumentTitle("DocumentaciĆ³n API");
c.EnableApiKeySupport("apikey", "header");
});
}
}
In turn, add a class to validate the application from where it is made on request
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(Swashbuckle.Swagger.Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
operation.parameters.Add(new Parameter
{
name = "AuthorizedClient",
#in = "header",
type = "intenger",
description = "Aplicacion",
required = true,
#default = axaxax
});
operation.parameters.Add(new Parameter
{
name = "ClientKey",
#in = "header",
type = "string",
description = "Cliente",
required = true,
#default = "bxbxbx"
});
operation.parameters.Add(new Parameter
{
name = "Authorization",
#in = "header",
type = "apikey",
description = "Token de sesiĆ³n",
});
}
}
I am using .NetCore 3 and Swagger 5.0.0-rc4. I am trying to upload file(image) using Swagger but it does not work because the apply method in the IOperationFilter or even Swashbuckle.AspNetCore.Swagger are missing some attributes. For instance NonBodyParameter and Consumes do not exit in Swagger 5.0
Do anyone use face the same problem or tried to solve it?
public class FileOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.OperationId.ToLower() == "apivaluesuploadpost")
{
operation.Parameters.Clear();
operation.Parameters.Add(new **NonBodyParameter**
{
Name = "uploadedFile",
In = "formData",
Description = "Upload File",
Required = true,
Type = "file"
});
operation.**Consumes**.Add("multipart/form-data");
}
}
}
AS for the missing Parameters Now these are changed to OpenApiParameter and OpenApiOperation.
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.OperationId == "MyOperation")
{
operation.Parameters.Clear();
operation.Parameters.Add(new OpenApiParameter
{
Name = "formFile",
In = ParameterLocation.Header,
Description = "Upload File",
Required = true,
Schema= new OpenApiSchema
{
Type="file",
Format="binary"
}
});
var uploadFileMediaType = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "object",
Properties =
{
["uploadedFile"] = new OpenApiSchema()
{
Description = "Upload File",
Type = "file",
Format = "binary"
}
},
Required = new HashSet<string>()
{
"uploadedFile"
}
}
};
operation.RequestBody = new OpenApiRequestBody
{
Content =
{
["multipart/form-data"] = uploadFileMediaType
}
};
}
}
I managed to solve this in Swashbuckle.AspNetCore 6.1.5
Swagger recognizes automatically IFormFile as a multipart/form-data media type.
You just have to delete the filter class and remove the [FromForm] or [FromBody] attribute from your parameter in the controller.
void Post([FromForm] IFileForm file) <= old
void Post(IFileForm file) <= new
I have implemented a web api with basic authentication filter. When I want to call it from Swagger, I am facing below problems:
1) I am unable to pass the username in Base64 encode.
2) Username is passed in Scheme property of ActionContext.Request.Headers.Authorization instead of Parameter property of the same.
I have tried with below code in swagger.
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.OperationFilter(() => new AddRequiredAuthorizationHeaderParameter());
});
public class AddRequiredAuthorizationHeaderParameter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline();
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Instance)
.Any(filter => filter is IAuthorizationFilter);
var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
operation.parameters.Add(new Parameter
{
name = "Authorization",
#in = "header",
type = "",
required = true,
description = "User Name"
});
}
}
Please help me out.