I'm trying to integrate swagger / swashbuckler into a project with UI into a project based on the latest Bot Framework V4 template. However it seems I'm getting only 500 http status codes when I try to navigate to /swagger or /swagger/v1/swagger.json.
The things I'm trying to add are in ConfigureServices:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
and in Configure:
loggerFactory = loggerFactory;
app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
I also tried to put the UseSwagger parts before the other but then the bot didn't work anymore as well. Likely there's some collision in endpoints being generated by the different parts but I'd really like to get that swagger part working.
The problem is Swagger requires MVC features to work and it looks like you're using the default templates for a bot which don't actually need MVC so it's not added to avoid the massive overhead that feature brings to the table.
So, you need to add the following in ConfigureServices:
services.AddMvc();
And then the following to Configure. NOTE: The order of the UseXXX calls do matter, but the order you're doing them in right now is fine and I would just do this:
app.UseDefaultFiles()
.UseStaticFiles()
.UseBotFramework()
.UseMvc()
.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
Related
I am writing a web api in ASP.NET CORE for the data models Package and Content. They are originally from MSSQL so I connected to the db server successfully. After writing http requests in the controllers, when I ran the project to test, it takes me to swagger/index.html that shows a blank page, I have no idea why.
Here is my program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
//OperationsContext is a db context that has both content and package dbcontext
builder.Services.AddDbContext<OperationsContext>(x => x.UseSqlServer(connectionString));
/*builder.Services.AddMvc();
builder.Services.AddHttpContextAccessor();*/
builder.Services.AddScoped<IContentRepository, ContentRepository>();
builder.Services.AddScoped<IPackageRepository, PackageRepository>();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
if (app.Environment.IsDevelopment() || app.Environment.IsProduction())
{
c.SwaggerEndpoint("/swagger/Package/swagger.json", "Package v1");
}
else
{
// To deploy on IIS
c.SwaggerEndpoint("/swagger/Package/swagger.json", "Package v1");
}
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
I am assuming it's the program.cs, but there are no issues when I ran it. Does anyone know why?
Swagger UI does not support Internet Explorer. Use Chrome, Firefox, Safari, or Edge instead.
https://github.com/domaindrivendev/Swashbuckle.AspNetCore
By default, Swagger JSON will be exposed at the following route -
"/swagger/{documentName}/swagger.json". If necessary, you can change
this when enabling the Swagger middleware. Custom routes MUST include
the {documentName} parameter.
Why does the template config require this placeholder but the UI config does not?
app.UseSwagger(c =>
{
c.RouteTemplate = "api-docs/{documentName}/swagger.json";
})
NOTE: If you're using the SwaggerUI middleware, you'll also need to update its configuration to reflect the new endpoints:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/api-docs/v1/swagger.json", "My API V1");
})
What is {documentName} for? Is there a feature to swap it out dynamically or something? Because the UI config in the example has it statically configured. why wouldn't it just be "/api-docs/v1/swagger.json" in the RouteTemplate config too?
documentName
The {documentName} refers to the name you specify in the AddSwaggerGen() method.
The following code uses myapi as the name for a swagger document.
builder.Services.AddSwaggerGen(options =>
options.SwaggerDoc("myapi", new OpenApiInfo { Title = "My API", Version = "v1" })
);
Using UseSwagger as follows
app.UseSwagger(options =>
options.RouteTemplate = "swagger/{documentName}/swagger.json");
leads to a swagger file being created the following location:
/swagger/myapi/swagger.json
Which means your Swagger UI configuration must be
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("/swagger/myapi/swagger.json", "Swagger v1");
});
The Swagger UI can make a UI based on any swagger file, whether it comes from this project or not. That's why it doesn't include the {documentName} placeholder. There isn't a relationship between these, necessarily.
Multiple Swagger UIs
This, for example, is the configuration where I have 1 Swagger Doc, 2 swagger files, and two UI endpoints. I describe the same API, but once using the OpenAPI v3 standard, and once using the old Swagger v2 standard.
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("myapi", new OpenApiInfo { Title = "My API", Version = "v1" });
});
app.UseSwagger(options =>
{
options.SerializeAsV2 = true;
options.RouteTemplate = "swagger/{documentName}/swaggerV2.json";
});
app.UseSwagger(options =>
{
options.SerializeAsV2 = false;
options.RouteTemplate = "swagger/{documentName}/openapiV3.json";
});
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("/swagger/myapi/openapiV3.json", "OpenApi v3");
options.SwaggerEndpoint("/swagger/myapi/swaggerV2.json", "Swagger v2");
});
When you go to the swagger UI, you will see a dropdown to select one of the two endpoints.
Multiple Swagger Docs
Your app can also have multiple swagger docs. E.g. your 'normal' API + some legacy API stuff.
options.SwaggerDoc("myapi", new OpenApiInfo { Title = "My API", Version = "v1" });
options.SwaggerDoc("myapiLegacy", new OpenApiInfo { Title = "My Legacy API", Version = "v1" });
There are several ways to specify when a method of your project belongs to a certain swagger doc. But the easiest way is to mark it with an attribute:
[HttpPost]
[ApiExplorerSettings(GroupName = "myapiLegacy")]
public void Post([Product product)
So since you can have multiple swagger docs, it makes sense to create a placeholder for it. i.e., {documentName}.
In my swagger UI I now end up with 4 endpoints:
normal api as Swagger V2
normal api as OpenApi V3
legacy api as Swagger V2
legacy api as OpenApi V3
I want to integrate MiniProfiler is a WebApi or View /XX/results-index.
The WebApi is authenticated with Bearer Tokens. I only want Group Users in Active Directory can see the results, but I don't get it.
I have this code in ServicesCollection:
services.AddMiniProfiler(options =>
{
options.RouteBasePath = "/profiler";
options.ResultsAuthorizeAsync = async request => await GetAuthorization(request); }).AddEntityFramework();
private static async Task<bool> GetAuthorization(HttpRequest request)
{
// var user = request.HttpContext.User.Identity.Name; --> Is null
return true;
}
In Configure Method in StartUp:
app.UseSwagger().UseSwaggerUI(options =>
{
options.SwaggerEndpoint($"/swagger/v1/swagger.json", $"{env.ApplicationName} V1");
options.OAuthClientId("TestApiswaggerui");
options.OAuthAppName("TestApi Swagger UI");
options.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream(
"TestApi.SwaggerMiniProfiler.html");
})
.UseMiniProfiler();
I want to see mini profiler information through some options:
http://localhost:5050/profiler/results-index --> Show the list methods called
http://localhost:5050/swagger/index.html --> Show the MiniProfiler in the same page
Environment:
.NET Core version: 3.1
MiniProfiler version: MiniProfiler.AspNetCore.Mvc v.4.2.1
Operative system: Windows 10
The piece you're probably missing here is that MiniProfiler shows your results. What's "you" is determined by the UserIdProvider option. When recording and viewing profiles, ensure that these are the same "user ID" (defaults to IP address). It looks like this in options:
services.AddMiniProfiler(options =>
{
options.UserIdProvider = request => ConsistentUserId(request);
});
If your swagger has zero server-side processing at all (e.g. it does not include the MiniProfiler <script> tag from .RenderInludes() or the <mini-profiler /> tag helper, then the issue isn't viewing the profiles so much as not even attempting to view. There are some ideas I have around a static tag without profiles to currently view, but I do not know how to get them into Swagger in it's generation phase (just not familiar enough). Note that it's a blatant hack, but you could work around the issue at the moment with a manual script tag. You'll want to follow https://github.com/MiniProfiler/dotnet/issues/326 for this.
I just want to leave the option of having the traces read for that group from the active directory:
services.AddMiniProfiler(options =>
{
// (Optional) Path to use for profiler URLs, default is /mini-profiler-resources
options.RouteBasePath = "/profiler";
options.ColorScheme = StackExchange.Profiling.ColorScheme.Light;
options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft;
options.PopupShowTimeWithChildren = true;
options.PopupShowTrivial = true;
options.ShouldProfile = ShowProfile;
options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
options.ResultsAuthorize = request => request.HttpContext.User.IsInRole("S-INFORMATICA");
})
.AddEntityFramework();
I'm getting a 'TypeError: Failed to fetch' error when trying to call my AspNetCore Restful API from Blazor Wasm. I can call it from Postman, and it works fine.
My Environment:
Microsoft Visual Studio Community 2019 Preview Version 16.6.0 Preview 3.0
Client: Blazor Wasm Service (dotnetstandard 2.1)
AspNet.WebApi.Client 5.2.7
AspNetCore..WebAssembly 3.2 preview 4.2
System.Net.Http.Json 3.2 preview 5.2
Important Usings:
using Microsoft.AspNetCore.JsonPatch;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
Parent namespace and class omitted
_httpClient is injected into parent class
public async Task<MyDto> UpdatePartialAsync(Guid primaryId, ObjectForUpdateDto objectForUpdateDto)
{
MyDto dtoFromApi = null;
var patchDoc = new JsonPatchDocument<ObjectForUpdateDto>()
.Replace(o => o.Name, objectForUpdateDto.Name)
.Replace(o => o.Description, objectForUpdateDto.Description)
var uri = $"MyUri/myResources/{primaryId}";
try
{
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var serializedPatchDoc = JsonConvert.SerializeObject(patchDoc);
var json = new StringContent(serializedPatchDoc, Encoding.UTF8, "application/json-patch+json");
var response = await _httpClient.PatchAsync(uri, json);
return await response.Content.ReadAsAsync<MyDto>();
}
catch (Exception)
{
throw; //throws 'TypeError: Failed to fetch'
}
return dtoFromApi;
}
My API (.Net 5.0, also tried .Net Core 3.1):
[HttpPatch]
[Route("{primaryId}")]
public ActionResult UpsertPartial([FromRoute]Guid primaryId, [FromBody] JsonPatchDocument<ObjectForUpdateDto> objectForUpdateDto)
{
//client call never makes it here
return NoContent();
}
What a misleading error message. It was a CORS issue.
The fix was adding "PATCH" to my CORS policy in my API's startup.cs ConfigureServices method (which previously was "GET, DELETE, PUT, POST, OPTIONS").
services.AddCors(options =>
{
options.AddPolicy(CorsAllowAll,
builder =>
{
builder.WithOrigins(Constants.ApiClientCors).AllowAnyHeader().WithMethods("GET, PATCH, DELETE, PUT, POST, OPTIONS");
});
});
#inliner49er, I wish that I could add a comment to clarify what you responded, since your answer is correct, but I don't have enough reputation points. Therefore, I'll post my tweaks to your answer as a separate answer.
You nailed it, the CORS issue fixed my program also. The only part of your code that didn't make sense was the reference that you have to a class called Constants. I am in the process of trying to complete the PluralSight tutorial, and because I'm working entirely internally, I can safely replace your code with the following:
services.AddCors(options =>
{
options.AddPolicy("PolicyName", builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
I am super new to all of this, literally hours into the process of learning about it, so there is a buttload that I don't understand. I just thought I'd post this to help anyone who might have similar questions to what I had.
You can also try adding these lines directly to the Configure method of the Startup class:
//ENABLE CORS
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
.AllowCredentials()); // allow credentials
I have a .NET Core 2.0 application, using Swashbuckle/Swagger to generate API documentation. When we were on 2.1.0-preview, Swagger was working fine. Then we did the big upgrade to 2.1.0 release and SDK 2.1.300. We didn't notice exactly when things broke, but now our Swagger docs won't load. Here's what we see:
Project has a reference to Swashbuckle.AspNetCore version 2.5.0. The relevant code in Startup.cs is below. In ConfigureServices():
services.AddSwaggerGen(swaggerOptions =>
{
// Register a swagger doc
swaggerOptions.SwaggerDoc("v1", new Info
{
// Optional descriptive info that will be included in the Swagger output
Contact = new Contact
{
Name = "LightSail",
Url = "https://myurl.com/"
},
Description = "A description of the API can go here",
Title = "My API",
Version = "v1"
});
// Xml file to get comment information from
swaggerOptions.IncludeXmlComments("App_Data/Api.xml");
});
And in Configure():
app.UseSwagger();
app.UseSwaggerUI(swaggerUiOptions => swaggerUiOptions.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1"));
I found lots of other similar questions, one of which suggested that there might be duplicate endpoints; I tried adding a call to .ResolveConflictingEndpoints() but that made no difference. I have searched through my project folders and there is no file called swagger.json, so I'm guessing that's the problem.
Any ideas why this is not working, or how to fix?
This is usually indicative of controllers/actions that Swashbuckle doesn't support for one reason or another.
It's expected that you don't have a swagger.json file in your project. Swashbuckle creates and serves that dynamically using ASP.NET Core's ApiExplorer APIs. What's probably happening here is that Swashbuckle is unable to generate Swagger.json and, therefore, the UI is failing to display.
As HelderSepu said, it's hard to know exactly what caused the failure, so the best way to debug is probably just to remove half your controllers (just move the files to a temporary location) and check whether the issues persists. Then you'll know which half of your controllers contains the troublesome action. You can 'binary search' removing controllers (and then actions) until you figure out which action method is causing Swashbuckle to not be able to generate Swagger.json. Once you know that, it should be obvious whether this is some issue in your code or an issue that should be filed in the Swashbuckle repo.
For example, Swashbuckle appears to not support open generics, so having a response type attribute like [ResponseType(typeof(IEnumerable<>))] could cause this sort of behavior. It could also be an issue with ambiguous routes or something like that tripping Swashbuckle up. Once you've narrowed down the cause of failure to something more specific like that, it can either be fixed or filed, as appropriate.
Today I found out that I could just go to the json url in the browser and get some error information
for example
myapiurl/api/vi/swagger.json
I was able to solve this error by explicitly adding the http verb attribute to my asp.net core 2.x controller method. The convention of prefixing the method name with the http verb is not enough for Swashbuckle apparently.
[HttpPost]
public async Task<IActionResult> AddNewData([FromBody] MyType myType) { … }
In my case I can reproduce your error by omitting "." from the end point as you have done.
I don't get the error if I include "." at the start of the path.
Here is more of my code in case it is relevant.
In ConfigureServices I have
services.AddSwaggerGen(c =>
{
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "My API",
Description = "ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "my name",
Email = "me#myemail.com"
}
});
});
In configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRewriter(new RewriteOptions()
.AddRedirectToHttpsPermanent());
app.UseSwagger(c =>
{
c.RouteTemplate =
"api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
//Include virtual directory if site is configured so
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
Also there is
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "access token",
Required = true,
Type = "string"
});
}
}
My dependencies are
Microsoft.AspNetCore.App (2.1.0)
Swashbuckle.AspNetCore (2.5.0)
Microsoft.NETCore.App (2.1.0)
Personally I was a bit quick and forgot to add this line to the method ConfigureServices in Startup.cs.
services.AddSwaggerDocument();
In my case, I missed the 'HttpAttribute':
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
Then I put it and swagger likes it:
[HttpGet]
public async Task<IEnumerable<ClientesListDto>> GetAll()
{
return await _service.GetAllAsync();
}
In my case, I had this:
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> CleanUpSnoozedLeads()
[HttpGet("CleanUpSnoozedLeads")]
public async Task<ActionResult<bool>> DoSomethingElse()
Notice the HttpGet() had the same name. That causes the undefined error as well.
A very common case is ambiguity. Just use the same signature for two PUT or POST operations for example and you will get the error.
Others answers did not worked for me.
I was able to fix and understand my issue when I tried to go to the swagger.json URL location:
https://localhost:XXXXX/swagger/v1/swagger.json
The page will show the error and reason why it is not found.
In my case, I saw that there was a misconfigured XML definition of one of my methods based on the error it returned:
NotSupportedException: HTTP method "GET" & path "api/Values/{id}" overloaded by actions - ...
...
...
In my case, i just forgot to add the HttpPostAttribute annotation to the method.
[HttpPost]
public ActionResult Post()
{
return Ok();
}
In my case there was a conflict in the schemaId. Apparently every class in the swagger JSON must have a unique schemaId. If you have two classes in different namespaces with the same name this will not work. We have to configure "UseFullTypeNameInSchemaIds" in the startup class.
Add "options.CustomSchemaIds(x => x.FullName);" in "services.AddSwaggerGen"
I found the trace by enabling Output window in VS, selecting the main project from Show output from dropdown list then visit http://{yourapiendpoint}/swagger/v1/swagger.json
If your api have same two or more [HttpGet] its not working swagger.
You should be specify [HttpGet] , [HttpGet ("{id}")]
simple solution