I have configured Swagger in my .NET 6 Web API project. In local, I can access Swagger file. Below is my code for that.
public static void ConfigureSwaggerMiddleware(this WebApplication app)
{
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.yaml", "API V1");
});
}
else
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("swagger/v1/swagger.yaml", "API V1");
c.RoutePrefix = "api/abc";
});
}
}
Now, I want to configure its URL in dev, test and higher environment like https://devurl/api/abc/swagger.yaml. I am trying above code but I am getting an error as
No webpage was found for the web address: https://localhost:7120/swagger/index.html
I'm afraid what you want can't be realized if you only changed the route prefix.
public static IApplicationBuilder UseSwaggerUI(
this IApplicationBuilder app,
Action<SwaggerUIOptions> setupAction = null)
{
SwaggerUIOptions options;
using (var scope = app.ApplicationServices.CreateScope())
{
options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<SwaggerUIOptions>>().Value;
setupAction?.Invoke(options);
}
// To simplify the common case, use a default that will work with the SwaggerMiddleware defaults
if (options.ConfigObject.Urls == null)
{
var hostingEnv = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
options.ConfigObject.Urls = new[] { new UrlDescriptor { Name = $"{hostingEnv.ApplicationName} v1", Url = "v1/swagger.json" } };
}
return app.UseSwaggerUI(options);
}
When we didn't set any config option for use swagger UI, it will set the default URL as v1/swagger.json with the default route prefix public string RoutePrefix { get; set; } = "swagger"; That makes us get the swagger.json file to load the index page. While you changed the route prefix value, it will make your API application failed to find the swagger configuration file, which will make it 404 error.
So we need to change both launchsetting.json and add app.UseSwagger options. I referred to this answer.
Here's my configuration in Program.cs and in launchsetting.json, I changed "launchUrl": "api/abc/swagger".
if (app.Environment.IsDevelopment())
{
//app.UseSwagger();
app.UseSwagger(c =>
{
c.RouteTemplate = "api/abc/swagger/{documentname}/swagger.json";
});
//app.UseSwaggerUI();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/api/abc/swagger/v1/swagger.json", "API V1");
c.RoutePrefix = "api/abc/swagger";
});
}
Then whenever the api application is running, it will direct to https://localhost:7212/api/abc/swagger/index.html and show the APIs. Here's my test result.
Related
I'm using ASP.NET Core with Swashbuckle.AspNetCore.SwaggerGen in version v6.1.5.
I want routes like [HttpGet] or [HttpGet("user")] in a public swagger.json for public endpoints and other routes that contain internal inside the route like this [HttpGet("internal/user/{userGuid}")] to be in an internal instance of swagger.json endpoints.
I configure Swagger in startup in ConfigureServices like this:
services.AddSwaggerGen(gen =>
{
gen.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Server API",
Version = "1.0",
Description = "This API features all public available endpoints showing different API features."
});
gen.SwaggerDoc("v1-internal", new OpenApiInfo
{
Title = "Viewer Server API (internal)",
Version = "v1-internal",
Description = "This API features all public available endpoints showing different API features."
});
});
and in Configure
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Viewer Server API v1");
c.SwaggerEndpoint("/swagger/v1-internal/swagger.json", "Viewer Server API v1 (internal)");
});
How can I get it to create a public and an internal swagger.json?
Filtering different version or in my case "visibility-layer" can be done like the follwing.
Configure Swagger in startup in ConfigureServices like this:
services.AddSwaggerGen(gen =>
{
gen.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Server API",
Version = "1.0",
Description = "This API features all public available endpoints showing different API features."
});
gen.SwaggerDoc("v1-internal", new OpenApiInfo
{
Title = "Viewer Server API (internal)",
Version = "v1-internal",
Description = "This API features all public available endpoints showing different API features."
});
gen.DocInclusionPredicate((docName, apiDesc) =>
{
if (docName.Contains("internal"))
{
return apiDesc.RelativePath.Contains("internal/");
}
else
{
return !apiDesc.RelativePath.Contains("internal/");
}
});
});
and in Configure
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Viewer Server API v1");
c.SwaggerEndpoint("/swagger/v1-internal/swagger.json", "Viewer Server API v1 (internal)");
});
And now you can choose different configurations like in the image below:
I updated Our net core API application from 2.1 to 3.1, SwashBuckle.Asp.NetCore to 5.0.0. Here is my startup set:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string authServerUrl = "http://testserver.com/identityserver4";
services.AddControllersWithViews();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "NetCore API V1" });
// Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow)
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(authServerUrl + "connect/authorize"),
TokenUrl = new Uri(authServerUrl + "connect/token"),
Scopes = new Dictionary<string, string>
{
{ "netCoreAPI.read", "read permission" },
{ "netCoreAPI.write", "write permission" }
} }
}
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
},
new[] { "netCoreAPI.read", "netCoreAPI.write" }
}
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("swagger/v1/swagger.json", "NetCore V1");
c.EnableDeepLinking();
c.OAuthClientId("clientId");
c.OAuthClientSecret("clientSecret");
c.OAuthAppName("netCoreApp");
c.OAuthScopeSeparator(" ");
c.OAuthUsePkce();
});
});
}
}
The initial Swagger UI displays relatively quickly. However, when a method in a controller is clicked, it takes 30 seconds to display "Try it out" button. Is there a way to debug the problem? Or Is there anyone having the same problem?
Before the code was converted from SwashBuckle 2.5 and net core 2.1 to SwashBuckle 5.0 and net core 3.1, the swagger UI works very fast.
I'm using "Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" And with that version switching to "Swashbuckle.AspNetCore.Newtonsoft" was not really helping. There was no significant improvement.
Then I have fount this issue listed on Github. It is an old resolved issue but reopened in 2020. Where they explain Swagger UI 3.x has "Pretty print" and "Syntax highlight" which causing the render issues. It can be turned off in Swagger config:
SwaggerUI({
syntaxHighlight: {
activated: false,
theme: "agate"
},
//url: path,
....
});
In .NET Core you can access config as well: Setup.cs in Configure()
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Web API");
c.ConfigObject.AdditionalItems.Add("syntaxHighlight", false); //Turns off syntax highlight which causing performance issues...
c.ConfigObject.AdditionalItems.Add("theme", "agate"); //Reverts Swagger UI 2.x theme which is simpler not much performance benefit...
});
Are you using NewtonSoft?
You need to add:
Install-Package Swashbuckle.AspNetCore.Newtonsoft -Version 5.1.0
And add:
services.AddSwaggerGenNewtonsoftSupport();
// explicit opt-in - needs to be placed after AddSwaggerGen()
https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft
Allowing the binary serialization in csproj speeded it up for me: <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
I faced a similar problem with swagger in my .Net 5 Web API project and it was fixed after following the steps and adding the code mentioned in both the above answers. To summarize:
Installed package Swashbuckle.AspNetCore.Newtonsoft 6.1.4
This line was already there in the Startup.cs:
services.AddSwaggerGenNewtonsoftSupport();
Added 2 lines of code in the Configure() of Startup.cs: (c.ConfigObject.AdditionalItems...)
I wrote an API. Trying to auto register the NSwagger documentation.
How do I derive the route into another variable? [Action]/{id} ?
For one below, its HttpGet. and contains 'Action/Id' etc,
Needs to be done through IApplicationModelProvider, and similarly through Controller Model and Action Model kind of loop.
*From knowing the Verb and the Route above, we can register the appropriate StatusCode.
Example: Will require 200 and 500 for All Apis, 404 for Only Get/Id Apis, 400 for Put Apis etc,
Net Core API: Make ProducesResponseType Global Parameter or Automate
[HttpGet("[Action]/{id}")]
public async Task<ActionResult<GetDepartmentResponse>> GetByDepartment(int id)
{
try
{
var department = await departmentAppService.GetDepartmentById(id);
var response = new GetDepartmentResponse { Body = department };
return Ok(response);
}
Need to know by reading the following similar loop below,
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
{
foreach (ActionModel action in controller.Actions)
{
try
{
if (action.ActionMethod.ReturnType.GenericTypeArguments[0].GetGenericArguments().Any())
{
Type returnType = action.ActionMethod.ReturnType.GenericTypeArguments[0].GetGenericArguments()[0];
var methodVerbs = action.Attributes.OfType<HttpMethodAttribute>().SelectMany(x => x.HttpMethods).Distinct();
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status200OK));
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status500InternalServerError));
}
if (methodVerbs.Contains("GET")) // and contains Route/Id
{
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status404NotFound));
}
if (methodVerbs.Contains("PUT"))
{
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status404NotFound));
}
if (methodVerbs.Contains("POST"))
{
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status201Created));
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status400BadRequest));
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status404NotFound));
}
}
catch { }
}
}
}
The Good News is that Swagger will auto-generate everything you need :)
All you have to do is add a couple of lines to your Startup.cs:
Add NuGet package Swashbuckle.AspNetCore to your REST project.
dotnet add package Swashbuckle.AspNetCore
Register Swashbuckle in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
services.AddMvc();
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
app.UseStaticFiles();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
Start your app, browse tohttp://localhost:<port>/swagger, and enjoy your new UI:
NOTE: The syntax above was valid in .Net Core 2.x. It has changed slightly for .Net Core 3.0:
NEW SYNTAX (.Net Core 3.x)
1.Nuget > Install Swashbuckle.AspNetCore v5.0.0-rc4
<= NOTE: You must check `Include prerelease= Y` in order to see this version
In Startup.cs > ConfigureServices(), substitute Microsoft.OpenApi.Models.OpenApiInfo for Swagger.Info.
EXAMPLE:
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1",
new Microsoft.OpenApi.Models.OpenApiInfo {
Title = "Contacts App", Version = "v1"
});
});
You can go to action.Attributes.OfType<HttpMethodAttribute>(). and then in Template, you will notice the Route Value. See image below in Visual Studio Debugging Window.
I have a net core 2.2 project with apicontrollers, EF, and I use latest swashbuckle swagger for net core 4.0.1
Its set up, everything is working, its showing all the api endpoints from the controllers in swagger.
The problem: We want to secure this api by using api-keys in the header of the requests. We want user A to only se for example, /GetAllUsers in usercontroller, but user B can see all the api endpoints in usercontroller.
I have yet to find an example that does this.
This is my thoughts.
Scenario one: User A goes to the api adress and gets prompted for a username and password (password is apikey) and we check the database if there is a username and password that matches. If so, we direct the user to the swagger view/endpoints that this user has access to.
Scenario two: send the apikey in the header and we check the db and return the correct view.
Is this doable?
This is what my startup looks like, and this an extension method for swagger/startup where I configure it.
startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddUnitOfWork();
services.AddHelpers();
services.AddTransient(typeof(IPipelineBehavior<,>),
typeof(RequestValidationBehavior<,>));
services.AddMediatR();
services.AddConfiguredSwagger(); // <--- extension
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure
the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
}
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json",
typeof(Startup).Namespace);
options.RoutePrefix = string.Empty;
});
app.UseMvc();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello");
});
}
Extension
public static class SwaggerServiceCollectionExtensions
{
public static IServiceCollection AddConfiguredSwagger(this
IServiceCollection services)
{
// Register the Swagger generator, defining 1 or more Swagger documents
return services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info { Title =
typeof(Startup).Namespace, Version = "v1" });
var xmlFile = $"
{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
}
}
I have an odd problem where my API project has started inserting a base URL ("/swagger") seemingly overnight with no changes to the code. The base URL only appears when I am working on the project locally, when it is deployed to an Azure app service everything is normal. See image for the problem:
In startup my swagger setup looks like this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My AppName API", Version = "v1" });
c.OperationFilter<SwaggerTokenParameter>();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("../swagger/v1/swagger.json", "V1 Docs");
});
...
}
I have tried recloning the project onto my machine but the problem has persisted, I have also tried manually setting the RoutePrefix:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("../swagger/v1/swagger.json", "V1 Docs");
c.RoutePrefix = string.Empty;
}
This doesn't help - it just breaks the UI and the definition still contains the base url:
{"swagger":"2.0","info":{"version":"v1","title":"My AppName API"},"basePath":"/swagger",...
Thanks to this answer I was able to come up with a solution. I still don't understand what changed to add in the base URL though or why it doesn't happen in the App Service.
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.BasePath = null;
});
});