Swagger API breaks when Route attribute is set to Controller Action - ASP.NET Core - asp.net-core

I am using ASP.NET Core 3.1. In My Web Application I have integrated swagger. Which works fine, shows the endpoints properly in the swagger API documentation. The code in startup is as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen( c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Clients}/{action=Index}/{id?}");
});
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Push Notification API V1");
});
}
To my Error Controller I just added a HttpStatusCodeHandler method and gave a route as below.
Then the swagger gives a error as:
"Failed to load API definition." Fetch error undefined
/swagger/v1/swagger.json
The method code is as follows.
[Route("Error/Error/{statusCode}")]
public IActionResult HttpStatusCodeHandler(int statusCode)
{
switch (statusCode)
{
case 404:
ViewBag.ErrorMessage = "Sorry, the resource you requested could not be found.";
break;
}
return View("Error");
}
Every time I comment out the Route attribute [Route("Error/Error/{statusCode}")] the swagger API works fine. What could be the error in this?

I removed the [Route("Error/Error/{statusCode}")] from the method and included the middleware app.UseStatusCodePagesWithRedirects() in the configure method.
I am not sure on the exact reason for the swagger API error.

The root cause is your controller is recognized as ApiController if you add Route attribute, and swagger will be broken if you do not add HttpMethod attribute in your controller.
Add [ApiExplorerSettings(IgnoreApi = true)] attribute to your controller, and the swagger broken problem should be fixed.

Related

cors error in authentication type windows - visual studio 2019

I started a .NET CORE 5 project
And I chose windows type authentication
type authentication image
And this type of project gives me a CORS error on the client side (react)
But if instead of windows I choose none, I won't get an error
This is the call from the client side:
const res = await fetch(`https://localhost:44373/weatherforecast`)
I need this type of project because I want to use AD authentication
I tried adding it to the fetch call:
const res = await fetch(`https://localhost:44300/weatherforecast`,{credentials: 'include'})
and change the STARTUP:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication3
{
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)
{
services.AddControllers();
services.AddCors(options =>
{
options.AddPolicy("MyMyAllowCredentialsPolicy",
policy =>
{
policy.WithOrigins("https://localhost:44300")
.AllowCredentials();
});
});
}
// 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.UseHttpsRedirection();
app.UseCors();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
I also created a new .net 6 web api project with windows authentication. I also had a front end project.
This is my code and it worked for me. In my Program.cs, I added Cors policy and others are generated by default.
using Microsoft.AspNetCore.Authentication.Negotiate;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("mypolicy",
policy =>
{
policy.WithOrigins("http://localhost:8848").AllowCredentials();
//.AllowCredentials();
});
});
// 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();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors("mypolicy");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
And I used ajax to send the request, missing withCredentials will lead to 401 error:
$("#btn2").click(function(event) {
$.ajax({
url: "https://localhost:7272/WeatherForecast",
type: "get",
xhrFields: {
withCredentials: true
},
success: function(data) {
alert(data);
console.info(data);
}
})
});
Another point which needs to notice is that, when opening the client website in the private mode, it will still meet 401 while everything worked well when open the website in the normal window. That is because private mode doesn't contain auth information I think.
For Post request, still worked.
Post with parameters??

Web API method not fetching data after adding OData functionality

I am using ASP.NET Core 2.1 Web API with Angular 6 as frontend.
I have added OData functionality to the Startup.cs file in the Web API project. I also use Swagger to document the API.
This is the code in the Startup.cs file for ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddCors();
services.AddMvc(opt =>
{
opt.UseGeneralRoutePrefix("BookManagement");
opt.SslPort = SettingsLoader<AppSettings>.Settings.SslPort;
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;
services.AddMvcCore(options =>
{
foreach (var outputFormatter in options.OutputFormatters.OfType<OutputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
{
outputFormatter.SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (var inputFormatter in options.InputFormatters.OfType<InputFormatter>().Where(x => x.SupportedMediaTypes.Count == 0))
{
inputFormatter.SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddRequestScopingMiddleware(() => _scopeProvider.Value = new Scope());
services.AddCustomControllerActivation(Resolve);
services.AddCustomViewComponentActivation(Resolve);
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc($"v{AppVersion.cVersion}", new Info { Title = "Books Microservice", Version = $"v{AppVersion.cVersion}" });
c.IncludeXmlComments(Path.Combine(Environment.CurrentDirectory, "Synergy.AssetsModule.WebApi.xml"));
});
}
Configure method is as below
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
Kernel = RegisterApplicationComponents(app);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
if (SettingsLoader<AppSettings>.Settings.AutoUpdateDatabase)
{
IDatabaseLogic databaseLogic = new DatabaseLogic(Kernel.Get<IDatabasePersistence>());
databaseLogic.UpgradeDatabase();
}
InitiateMessageReprocess();
#if !NO_AUTH
ISingleSignOnLogic singleSignOnLogic = Kernel.Get<ISingleSignOnLogic>();
app.UseMiddleware<AuthenticationFilter>(singleSignOnLogic);
#endif
// 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(c =>
{
c.SwaggerEndpoint($"/swagger/v{AppVersion.cVersion}/swagger.json", $"Books Microservice API v{AppVersion.cVersion}");
c.RoutePrefix = string.Empty;
});
app.UseCors(builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyMethod();
builder.AllowAnyHeader();
});
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection();
routeBuilder.Expand().Select().Count().OrderBy().Filter();
});
app.UseRewriter(new RewriteOptions().AddRedirectToHttps(StatusCodes.Status301MovedPermanently, SettingsLoader<AppSettings>.Settings.SslPort));
LogProvider.Instance.LogInformation("Started application");
}
With this code, the Swagger API is returning 204 Undocumented.
Don't know where it is going wrong. The parameter content type previously I was using is application/json-patch+json. Please suggest why I am not getting the data from the Web API method.
A couple of things. First, I would suggest creating a separate web service to host your OData endpoints. Second, prior to .net 6, it's somewhat difficult to get OData and Swagger to work together. Even in .net 6, there are a few things missing when it comes to swagger.
I have an open-source project that fills in the gaps. Here's a sample project that illustrates how to use it.
https://github.com/ikemtz/NRSRx/tree/master/samples/IkeMtz.Samples.OData

CORS error with Aurelia calling .NET core API 2.0

I am getting a CORS error and I don't know how to fix it. I have an Aurelia app, calling a .NET core 2.0 API using aurelia-fetch-client. I am getting the following error:
Failed to load http://localhost:58289/api/info: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
TypeError: Failed to fetch
at applyInterceptors (webpack-internal:///./node_modules/aurelia-fetch-client/dist/native-modules/aurelia-fetch-client.js:428:14)
at processResponse (webpack-internal:///./node_modules/aurelia-fetch-client/dist/native-modules/aurelia-fetch-client.js:411:10)
at eval (webpack-internal:///./node_modules/aurelia-fetch-client/dist/native-modules/aurelia-fetch-client.js:299:14)
From previous event:
at HttpClient.eval (webpack-internal:///./node_modules/aurelia-fetch-client/dist/native-modules/aurelia-fetch-client.js:287:61)
at HttpClient.fetch (webpack-internal:///./node_modules/aurelia-fetch-client/dist/native-modules/aurelia-fetch-client.js:273:21)
at App.callApi (webpack-internal:///app:42:25)
at CallScope.evaluate (webpack-internal:///./node_modules/aurelia-binding/dist/native-modules/aurelia-binding.js:1578:19)
at Listener.callSource (webpack-internal:///./node_modules/aurelia-binding/dist/native-modules/aurelia-binding.js:5279:40)
at Listener.handleEvent (webpack-internal:///./node_modules/aurelia-binding/dist/native-modules/aurelia-binding.js:5288:10)
at HTMLDocument.handleDelegatedEvent (webpack-internal:///./node_modules/aurelia-binding/dist/native-modules/aurelia-binding.js:3363:20)
Please find my code below.
aurelia-fetch-client configuration:
const http = new HttpClient().configure(config => {
config
.withBaseUrl(environment.apiBaseUrl)
.withDefaults({
headers: {
'Content-Type': 'application/json'
}
})
.withInterceptor({
request(request: Request) {
var token = localStorage.getItem('access_token')
request.headers.append('Authorization', 'Bearer ' + token)
return request;
},
responseError(error){
return error;
}
});
});
aurelia.container.registerInstance(HttpClient, http);
Call the API:
callApi(){
this.httpClient.fetch("/info")
.then(response => console.log(response));
}
API startup configuration:
public void ConfigureServices(IServiceCollection services)
{
string domain = $"https://{Configuration["Auth0:Domain"]}/";
var allowedCors = Configuration["CorsSite"];
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = Configuration["Auth0:ApiIdentifier"];
});
services.AddCors(options => options.AddPolicy("AllowSpecificOrigin", `builder => {`
builder.AllowAnyOrigin().AllowAnyMethod(); }));
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowSpecificOrigin");
app.UseAuthentication();
app.UseMvc();
}
Controller:
[Produces("application/json")]
[Route("api")]
public class InfoController : Controller
{
// GET api/values
[HttpGet]
[Route("Info")]
public IActionResult Get()
{
return Ok("Api V1.0");
}
[Route("authorizedInfo")]
[Authorize]
[HttpGet]
public IActionResult GetAuthorized()
{
return Ok("Authorized Api V1.0");
}
}
Please ignore the authorisation bit for now. I am only trying to hit the unauthorised API endpoint in localhost, but I am stuck. How can I fix my problem?
To do this start with registering CORS functionality in ConfigureServices() of Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add service and create Policy with options
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials() );
});
services.AddMvc();
}
The AddCors() call above adds the CORS features to ASP.NET and creates a custom policy that can be reused in the application by name. There are other ways to do essentially the same thing by explicitly adding a policy builder in the configuration step but to me this seems cleanest - define one or more policies up front and then apply it.
Once the policy has been defined it can be applied.
You can apply the policy globally to every request in the application by call app.useCors() in the Configure() method of Startup:
public void Configure(IApplicationBuilder app)
{
// ...
// global policy - assign here or on each controller
app.UseCors("CorsPolicy");
// ...
// IMPORTANT: Make sure UseCors() is called BEFORE this
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
or you can apply the policy to individual controllers:
[EnableCors("CorsPolicy")]
[ApiExceptionFilter]
public class AlbumViewerApiController : Controller
Thank You
The answer in the following link fixed my issue.
Web API 2 CORS IIS Express Debug and No Access-Control-Allow-Origin header
It appears that if there is no origin header in the request the server will not respond with the corresponding Access-Control-Allow-Origin response. Also with aurelia-fetch-client defaults I would have expected to have the origin header added by default.

Swashbuckle root error when i deploy on subfolder

I begin with Swashbuckle, i make a Web API in .NET Core with Swashbuckle.
I need to deploy my API in a sub-application of an IIS site
IIS infrastructure
IIS Site (site)
myApi (subApplication)
http://ip:80/myApi
I would like the access to swagger UI to be done via this url :
http://ip:80/myApi/swagger
I would like the access to the methods to be done by this url :
http://ip:80/myApi/api/[controller]/methode
ConfigureServices(IServiceCollection services)
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info { Title = "My REST API", Version = "v1" });
});
Configure(IApplicationBuilder app..)
if (env.IsEnvironment("ProdIT"))
{
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.BasePath = "/myApi/");
c.RouteTemplate = "myApi/api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "myApi/swagger";
c.SwaggerEndpoint("/myApi/api-docs/v1/swagger.json", "MY REST API V1");});
Route Controller
[Route("api/[controller]")]
Could you tell me what's wrong with my code?
Thanks in advance
I had the same issue and I resolved it by configuring the SwaggerUI like that;
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("v1/swagger.json", "MY REST API V1");});
}

.NET & Swagger started to get 401 error

I have configured Swagger for my .NET Core webApi application, here is the configuration for it
public void ConfigureServices(IServiceCollection services)
{
services.AddAuth();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.Build());
});
services.AddMvc();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1",
new Info
{
Version = "v1",
Title = "XXX Synchronization API",
Description = "XXX Synchronization API",
TermsOfService = "None",
Contact = new Contact { Name = "XXX Inc.", Email = "XXXX", Url = "XXX.com" }
});
c.IncludeXmlComments(GetXmlCommentsPath());
c.DescribeAllEnumsAsStrings();
});
services.AddSingleton<IHashProvider, Pbkdf2Version1HashProvider>();
services.AddSingleton<IPasswordHasher, PasswordHasher>();
services.AddTransient<IAuthenticationService, GfdAuthenticationService>();
services.AddSingleton(new GfdSqlServerDatabase(Gfd2Configuration.Settings.GfdSqlServerConnectionString));
services.AddSingleton(new MySqlSyncDatabase(Gfd2Configuration.Settings.AwsMySqlConnectionString));
services.AddTransient<SyncRepository, AwsMySqlSyncRepository>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
services.AddSingleton<ArchivedJobProcessorBase, ArchivedJobProcessorFeatures>();
}
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseCors("CorsPolicy");
app.UseMvc();
app.UseSwagger();
#if DEBUG
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "XXXX Synchronization API V1"); });
#else
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/sync/swagger/v1/swagger.json", "XXX Synchronization API V1"); });
#endif
}
and everything was wokring fine. And for some reason this morning anytime I open the /swagger url I'm getting a 401 error.
I don't need any authentication for the Swagger docs and UI, all is public.
Could this be related to .NET Core 2.1.2?
using Swashbuckle.AspNetCore 3.0.0
Any ideas are welcome.
For those who might have such an issue, the fix was actually quite simple. For one of my new webApi controllers which actually requires authentication, I forgot to add the attribute [Route("api/[controller]")], as such the /swagger url was pointing to that controller and thus the 401 error. Go figure :)
This issue happened to me when I added a new endpoint with wrong route.
I used [Route("/{id}")].
It should have no forward slash at the beginning [Route("{id}")]