Swagger UI execute button not working when file is uploading - asp.net-core

One of the endpoint in my API is for uploading a file and submitting details about the file . The API is implemented in ASP.Net Core. NSwag package is installed in my project. OAS 3 specification is implemented
On Swagger UI I can submit data without uploading file. if the file is chosen using file browser, the execute button is not working when clicked.
I checked Console and Network tab in Chrome Dev tools. There is no network movement. There is no request at all. Neither errors nor any logs were seen in console.
I used bearer token authorisation according to this. I tried in both cases of when in authorised and not authorised state. In both case Execute button not working when file is chosen. When bearer token was not given, and I execute with other parameters except file. I got 401 status response. If the file chosen , the click of execute button has no effect.
The swagger file is:
{
"x-generator": "NSwag v13.1.5.0 (NJsonSchema v10.0.27.0 (Newtonsoft.Json v12.0.0.0))",
"openapi": "3.0.0",
"info": {
"title": "My Title",
"version": "1.0.0"
},
"servers": [
{
"url": "https://localhost:5300"
}
],
"paths": {
"/api/Order": {
"post": {
"tags": [
"Order"
],
"operationId": "Order_Post",
"responses": {
"200": {
"description": "",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
}
}
}
},
"/api/Order/{action}/document": {
"post": {
"tags": [
"Order"
],
"operationId": "Order_CreateDocument",
"parameters": [
{
"name": "FileName",
"in": "formData",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
},
{
"name": "FileType",
"in": "formData",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 2
},
{
"name": "DocumentType",
"in": "formData",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 3
},
{
"name": "Description",
"in": "formData",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 4
},
{
"name": "Owners",
"in": "formData",
"collectionFormat": "multi",
"schema": {
"type": "array",
"nullable": true,
"items": {
"type": "string"
}
},
"x-position": 5
},
{
"type": "file",
"name": "SomeFile",
"in": "formData",
"schema": {
"type": "string",
"format": "binary",
"nullable": true
},
"nullable": true
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
}
}
}
},
"/api/Debug": {
"get": {
"tags": [
"Debug"
],
"operationId": "Debug_Get",
"parameters": [
{
"name": "message",
"in": "query",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"authorization": {
"type": "apiKey",
"name": "authorization",
"in": "header"
}
}
},
"security": [
{
"authorization": []
}
]
}
I want to submit the file and other details as multipart form data.
ASP.Net Core code for the endpoint:
[HttpPost("document")]
public ActionResult CreateDocument([FromForm]Document request)
{ ... }
Document Class :
public class Document
{
public string FileName { get; set; }
public string FileType { get; set; }
public string DocumentType { get; set; }
public string Description { get; set; }
public List<string> Owners { get; set; } = new List<string>();
public Microsoft.AspNetCore.Http.IFormFile SomeFile { get; set; }
}
In Startup.cs :
public void ConfigureServices(IServiceCollection services)
{
....
...
// Register the Swagger services
services.AddOpenApiDocument(document =>
{
document.AddSecurity("authorization", Enumerable.Empty<string>(), new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
Name = "authorization",
In = NSwag.OpenApiSecurityApiKeyLocation.Header
});
document.OperationProcessors.Add(
new NSwag.Generation.Processors.Security.AspNetCoreOperationSecurityScopeProcessor("bearer"));
}); // registers a OpenAPI v3.0 document
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
. . . . . .
. . . . . .
// Register the Swagger generator and the Swagger UI middlewares
app.UseOpenApi();
app.UseSwaggerUi3();
}
Why is execute button in Swagger-UI not working ? How can i upload file from Swagger-UI ?

#Helen has commented and pointed the issue. It is NSwag.AspNetCore package/library.
It seems NSwag library has a bug in Swagger Generation for ASP.Core API in OpenAPI sepecification 3.0 . the issues for this error. It is only when using FromForm attribute for Model binding. In my case one of the property is Microsoft.AspNetCore.Http.IFormFile type that also may cause this error.
Changing to OAS 2.0 is temporary solution.

Related

Swagger UI returning fetch error for certain endpoint names [duplicate]

This question already has answers here:
Getting "net::ERR_BLOCKED_BY_CLIENT" error on some AJAX calls
(15 answers)
I am getting Failed to load resource: net::ERR_BLOCKED_BY_CLIENT with Google chrome
(17 answers)
Closed 5 months ago.
This is the first time I've experienced this in my entire application. I have a new controller defined as such
[Authorize]
[ApiController]
public class AdvertisementsController : ControllerBase
{
private readonly IAdvertisementStore _store;
private readonly IGetAdvertisementDashboardUseCase _dashboard;
private JsonPresenter jsonPresenter = new JsonPresenter();
private ViewModelPresenter<AdvertisementListViewModel> adListPresenter = new ViewModelPresenter<AdvertisementListViewModel>();
private ViewModelPresenter<AdvertisementDetailsViewModel> adDetailsPresenter = new ViewModelPresenter<AdvertisementDetailsViewModel>();
private ViewModelPresenter<AdvertisementDashboardViewModel> dashboardPresenter = new ViewModelPresenter<AdvertisementDashboardViewModel>();
public AdvertisementsController(IAdvertisementStore advertisementStore,
IGetAdvertisementDashboardUseCase getAdvertisementDashboardUseCase)
{
_store = advertisementStore;
_dashboard = getAdvertisementDashboardUseCase;
}
[HttpPost]
[ValidateModel]
[Route("api/advertisement")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<GenericResponse>> CreateAdvertisement([FromBody] AdvertisementRequest request)
{
await _store.Handle(AccessEvent.Create, request, jsonPresenter);
return jsonPresenter.ContentResult;
}
[HttpGet]
[ValidateModel]
[Route("api/advertisement")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<AdvertisementDetailsViewModel>> GetAdvertisement([FromQuery] GenericRequest request)
{
await _store.Handle(AccessEvent.Read, request, adDetailsPresenter);
return adDetailsPresenter.ContentResult;
}
[HttpGet]
[ValidateModel]
[Route("api/advertisements")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<AdvertisementListViewModel>> GetAdvertisements([FromQuery] AdvertisementPaginatingRequest request)
{
await _store.Handle(AccessEvent.ReadList, request, adListPresenter);
return adListPresenter.ContentResult;
}
[HttpPut]
[ValidateModel]
[Route("api/advertisement")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<GenericResponse>> EditAdvertisement([FromBody] AdvertisementRequest request)
{
await _store.Handle(AccessEvent.Update, request, jsonPresenter);
return jsonPresenter.ContentResult;
}
[HttpDelete]
[ValidateModel]
[Route("api/advertisement")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<GenericResponse>> DeleteAdvertisement([FromQuery] GenericRequest request)
{
await _store.Handle(AccessEvent.Delete, request, jsonPresenter);
return jsonPresenter.ContentResult;
}
[HttpGet]
[ValidateModel]
[Route("api/advertisement_dashboard")]
[Authorize(Policy = Policies.ADVERTISEMENTS)]
public async Task<ActionResult<AdvertisementDashboardViewModel>> Dashboard([FromQuery] AdvertisementDashboardListRequest request)
{
await _dashboard.Handle(request, dashboardPresenter);
return dashboardPresenter.ContentResult;
}
}
Looks just like everyone of my other controllers.
For some reason on my Swagger UI the GET api/advertisements and GET api/advertisement_dashboard endpoints return the following error when I try to execute it
But as soon as I change the name to api/advertisementts or api/advertisementt_dashboard (adding a random t to the route names) it works just fine. This is only an issue using Swagger UI though, making a cURL request or using Postman the endpoints work just fine. Seems like swagger isn't correctly finding those endpoints for some reason?
Relevant Swagger.json schema
"/api/advertisement": {
"post": {
"tags": [
"Advertisements"
],
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
}
}
}
}
},
"get": {
"tags": [
"Advertisements"
],
"parameters": [
{
"name": "Id",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "AdditionalArg",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDetailsViewModel"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDetailsViewModel"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDetailsViewModel"
}
}
}
}
}
},
"put": {
"tags": [
"Advertisements"
],
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementRequest"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
}
}
}
}
},
"delete": {
"tags": [
"Advertisements"
],
"parameters": [
{
"name": "Id",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "AdditionalArg",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/GenericResponse"
}
}
}
}
}
}
},
"/api/advertisements": {
"get": {
"tags": [
"Advertisements"
],
"parameters": [
{
"name": "Type",
"in": "query",
"schema": {
"$ref": "#/components/schemas/AdvertisementType"
}
},
{
"name": "Status",
"in": "query",
"schema": {
"$ref": "#/components/schemas/AdvertisementStatus"
}
},
{
"name": "StartDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "EndDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "Take",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "Skip",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "SearchField",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AdvertisementListViewModel"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementListViewModel"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementListViewModel"
}
}
}
}
}
}
},
"/api/advertisement_dashboard": {
"get": {
"tags": [
"Advertisements"
],
"parameters": [
{
"name": "PlatformIds",
"in": "query",
"schema": {
"type": "array",
"items": {
"type": "integer",
"format": "int32"
}
}
},
{
"name": "StartDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "EndDate",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "Take",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "Skip",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "SearchField",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDashboardViewModel"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDashboardViewModel"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/AdvertisementDashboardViewModel"
}
}
}
}
}
}
}
I'd rather not change my naming convention just so I can use Swagger to test out my endpoints.

OpenAPI Example multipart form data

I have a multipart/form-data POST in an API endpoint which takes some key/value strings, and a file upload via the key files.
I believe I have defined it correctly in OpenAPI;
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"properties": {
"file": {
"type": "array",
"items": {
"type": "string",
"format": "binary"
}
},
"myKey1": {
"type": "string"
},
"myKey2": {
"type": "string"
}
}
},
"examples": {
"value": ?
}
}
}
},
However, I am unsure how I can describe an example for a multipart/form-data in the examples field.
I assume I don't need to represent the file (although that would be a bonus) but just the myKey1 and myKey2 keys and values.
Your OAS definition seems to be correct. You can define the examples as shown below:
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"properties": {
"file": {
"type": "array",
"items": {
"type": "string",
"format": "binary"
},
"example": [
{
"externalValue": "http://www.africau.edu/images/default/sample.pdf"
}
]
},
"myKey1": {
"type": "string",
"example": "myKey1Example"
},
"myKey2": {
"type": "string",
"example": "myKey2Example"
}
}
}
}
}
},
externalValue can be added to point the sample file URL in Open API Specification. This is only for the document purpose.
However, it will not be displayed in the swagger-ui as swagger-ui does not support it yet. It is tracked in [1].
[1] https://github.com/swagger-api/swagger-ui/issues/5433

Generating OpenApi (Swagger) with Swashbuckle in GeneratePolymorphicSchemas mode, how to configure it to use $type prop from JSON.NET?

I have an ASP.NET Core 3 WebApi with polymorphic DTOs. I'm using Json.NET to add $type field to my json, i.e. "$type":"CrazyIvanMotors.DTO.Animal.Dog, CrazyIvanMotors.DTO".
I'm trying to generate an OpenApi 3.0.1 schema.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson((config) =>
{
config.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
});
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
c.GeneratePolymorphicSchemas();
});
}
Swashbuckle properly detects polymorphic classes and adds $type property, but does not put correct constant values into type or generate any mapping as described here OpenApi doc inheritance and polymorphism
"Animal": {
"required": [
"$type"
],
"type": "object",
"properties": {
"$type": {
"type": "string"
},
"name": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false,
"discriminator": {
"propertyName": "$type"
}
},
"Dog": {
"allOf": [
{
"$ref": "#/components/schemas/Animal"
},
{
"type": "object",
"properties": {
"sizeCategory": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
]
}
Am I missing some configuration here?

Can we declare stage name inside the template.serverless file?

We are creating AWS Serverless Lambda function using .NET Core. When we deploy this lambda function it added automatically "Prod" suffix in the url. But we want change it to "dev". Can we declare stage name inside the serverless.template file?
Here is my serverless.template file:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Transform" : "AWS::Serverless-2016-10-31",
"Description" : "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
"Parameters" : {
},
"Conditions" : {
},
"Resources" : {
"Get" : {
"Type" : "AWS::Serverless::Function",
"Properties": {
"Handler": "F2C.MAP.API.AWSLambda.PublicAPI::F2C.MAP.API.AWSLambda.PublicAPI.LambdaEntryPoint::FunctionHandlerAsync",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [ "AWSLambdaFullAccess" ],
"Environment" : {
"Variables" : {
}
},
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/{proxy+}",
"Method": "GET"
}
}
}
}
},
"POST" : {
"Type" : "AWS::Serverless::Function",
"Properties": {
"Handler": "F2C.MAP.API.AWSLambda.PublicAPI::F2C.MAP.API.AWSLambda.PublicAPI.LambdaEntryPoint::FunctionHandlerAsync",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [ "AWSLambdaFullAccess" ],
"Environment" : {
"Variables" : {
}
},
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/{proxy+}",
"Method": "POST"
}
}
}
}
}
},
"Outputs" : {
}
}
We are using AWS Toolkit for visual studio 2017 to deploy aws serverless lambda.("https://aws.amazon.com/blogs/developer/preview-of-the-aws-toolkit-for-visual-studio-2017")
The only way I could find to make this work is the specify a AWS::Serverless::Api to use. The following example sets both the StageName and the ASPNETCORE_ENVIRONMENT to the EnvironmentName parameter.
serverless.template:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Transform" : "AWS::Serverless-2016-10-31",
"Description" : "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
"Parameters" : {
"EnvironmentName" : {
"Type" : "String",
"Description" : "Sets the ASPNETCORE_ENVIRONMENT variable as well as the API's StageName to this.",
"MinLength" : "0"
}
},
"Resources" : {
"ProxyFunction" : {
"Type" : "AWS::Serverless::Function",
"Properties": {
"Handler": "PeopleGateway::PeopleGateway.LambdaEntryPoint::FunctionHandlerAsync",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [ "AWSLambdaFullAccess", "AWSLambdaVPCAccessExecutionRole" ],
"Environment" : {
"Variables" : {
"ASPNETCORE_ENVIRONMENT": { "Ref" : "EnvironmentName" }
}
},
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/{proxy+}",
"Method": "ANY",
"RestApiId": { "Ref": "APIGateway" }
}
}
}
}
},
"APIGateway": {
"Type" : "AWS::Serverless::Api",
"Properties": {
"StageName": { "Ref" : "EnvironmentName" },
"DefinitionBody": {
"swagger": "2.0",
"info": {
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProxyFunction.Arn}/invocations"
}
},
"responses": {}
}
}
}
}
}
}
},
"Outputs" : {
"ApiURL" : {
"Description" : "API endpoint URL for the specified environment",
"Value" : { "Fn::Sub" : "https://${APIGateway}.execute-api.${AWS::Region}.amazonaws.com/${EnvironmentName}/" }
}
}
}

geoserver upload kml no format

I'm using the API of the geoserver (link) to upload a new kml file to mu local machine using a post request to http://localhost:8080/geoserver/rest/imports with the following json
{
"import": {
"targetWorkspace": {
"workspace": {
"name": "sample"
}
},
"targetStore": {
"dataStore": {
"name": "sample"
}
},
"data": {
"type": "file",
"file": "/data/sample_dir/sample.kml"
}
}
}
I should get the data type and the state as ready on the response according to the API documentation but I'm getting a pending and no file format.
Response:
{
"import": {
"id": 23,
"href": "http://localhost:8080/geoserver/rest/imports/23",
"state": "PENDING",
"archive": false,
"targetWorkspace": {
"workspace": {
"name": "sample"
}
},
"targetStore": {
"dataStore": {
"name": "sample",
"type": "Directory of spatial files (shapefiles)"
}
},
"data": {
"type": "file",
"format": null,
"file": "sample.kml"
},
"tasks": []
}
}
Apparently the geoserver don't accept the relative path like it says on the API documentation.
If instead of:
"data": {
"type": "file",
"file": "./data/sample_dir/sample.kml"
}
I use
"data": {
"type": "file",
"file": "/var/lib/tomcat7/webapps/geoserver/data/sample_dir/sample.kml"
}
on the request it works fine.