My swagger is not working in my ASP.net core project. It works when I comment out either the put or post action method. I have also tried putting the name of eachpoint to fix the problem but it didnt work. Can anyone tell me how to overcome this issue? Thanks
[HttpPut("{ids}", Name = nameof(Edit))]
[Route("api/[controller]/[action]")]
[ApiController]
public class CompanyController : ControllerBase
{
private readonly IMediator _mediator;
public CompanyController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet()]
public async Task<ActionResult<List<Company>>> List()
{
return await _mediator.Send(new List.Query());
}
[HttpPost]
public async Task<ActionResult<Unit>> Create(Create.Command command)
{
return await _mediator.Send(command);
}
[HttpGet("{id}")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<ActionResult<Company>> Details(int id)
{
return await _mediator.Send(new Details.Query { Id = id });
}
[HttpPut("{ids}", Name = nameof(Edit))]
public async Task<ActionResult<Unit>> Edit(int ids, Edit.Command command)
{
command.Id = ids;
return await _mediator.Send(command);
}
}
}
This is how I configured swagger in my startup class.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.WithOrigins("http://localhost:3000");
});
});
// Register the Swagger generator, defining 1 or more Swagger documents
**services.AddSwaggerGen();**
services.AddControllers()
.AddFluentValidation(cfg =>
{
cfg.RegisterValidatorsFromAssemblyContaining<Create>();
});
services.AddMediatR(typeof(List.Handler).Assembly);
}
// 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.UseCors("CorsPolicy");
// 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/v1/swagger.json", "My API V1");
});**
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Related
I have two applications running together at the same time. I was trying to find a way to be able to use TempData in my own type of class and after reading it I implemented it in my Middleware for my MVC project which works smoothly. However, when I copy the Middleware code from my MVC project to my Middleware for my asp.net web api project it does not work. When I run the programs together, and when it calls the web api project it returns the following the web api (MVC works fine I do not get any errors on that):
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory' while attempting to activate 'AddressService.API.Middleware.CorrelationIdMiddleware'.
Before I implemented TempData (ITempDataDictionaryFactory) in the middleware of my web api project it worked fine... but after implementing ITempDataDictionaryFactory to it, it gives me that error. Is there something I have to do in order for it to work like it does in my Middleware for my MVC project?
Middleware in my web api project:
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;
public CorrelationIdMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, ITempDataDictionaryFactory tempDataDictionaryFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<CorrelationIdMiddleware>();
_tempDataDictionaryFactory = tempDataDictionaryFactory;
}
public async Task Invoke(HttpContext context)
{
string correlationId = null;
string userName;
string ipAddress;
var tempData = _tempDataDictionaryFactory.GetTempData(context);
var key = context.Request.Headers.Keys.FirstOrDefault(n => n.ToLower().Equals("x-correlation-id"));
if (!string.IsNullOrWhiteSpace(key))
{
correlationId = context.Request.Headers[key];
_logger.LogInformation("Header contained CorrelationId: {#CorrelationId}", correlationId);
}
else
{
if (tempData.ContainsKey("username") && tempData.ContainsKey("ipaddress"))
{
userName = tempData.Peek("username").ToString();
ipAddress = tempData.Peek("ipaddress").ToString();
context.Response.Headers.Append("X-username", userName);
context.Response.Headers.Append("X-ipAddress", ipAddress);
}
correlationId = Guid.NewGuid().ToString();
_logger.LogInformation("Generated new CorrelationId: {#CorrelationId}", correlationId);
}
context.Response.Headers.Append("x-correlation-id", correlationId);
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await _next.Invoke(context);
}
}
CorrelationIdExtensions.cs (use to call app.UseCorrelationId() in startup):
public static class CorrelationIdExtensions
{
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CorrelationIdMiddleware>();
}
}
Startup.cs:
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.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "AddressService.API", Version = "v1" });
});
services.AddHttpContextAccessor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCorrelationId();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AddressService.API v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
One of the ways to solve this should be using:
services.AddControllersWithViews();
or
services.AddMvc();
Instead of services.AddControllers();.
Custom AuthorizationHandler is not being called when Authorize attribute is placed at method level on SignalR Hub implementation.
I have the following code, with what I believe to be the correct inclusion for custom authorization handling. I am using the following setup:
ASP.Net Core version 5.0,
.Net 5.0 console app with Kestrel host configure for Windows Authentication (the Identity is populated):
Any ideas what is missing/wrong?
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapHub<ExampleHub>("/Example"));
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
services.AddAuthorization(options =>
{
options.AddPolicy(nameof(ExampleRequirement), policy => policy.Requirements.Add(new ExampleRequirement()));
});
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddSingleton<IAuthorizationRequirement, ExampleAuthorizationRequirementHandler>();
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
}
}
public class ExampleRequirement : IAuthorizationRequirement
{
}
public class ExampleAuthorizationRequirementHandler : AuthorizationHandler<ExampleRequirement, HubInvocationContext>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ExampleRequirement requirement,
HubInvocationContext resource)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
public class ExampleHub : Hub
{
[Authorize(Policy = "ExampleRequirement")]
public Task Register()
{
return Task.CompletedTask;
}
}
Re-ordering the ConfigureServices declarations to put the AddSignalR at the star, caused this to start working when using Kestrel, not sure about other Asp.Net hosts:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
services.AddAuthorization(options =>
{
options.AddPolicy(nameof(ExampleRequirement), policy => policy.Requirements.Add(new ExampleRequirement()));
});
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddSingleton<IAuthorizationRequirement, ExampleAuthorizationRequirementHandler>();
}
I installed a fresh .net core web api (5.0) with Identity Users (IdentityServer).
I added a middleware:
public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private const string APIKEYNAME = "ApiKey";
public ApiKeyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Api Key was not provided. (Using ApiKeyMiddleware) ");
return;
}
var appSettings = context.RequestServices.GetRequiredService<IConfiguration>();
var apiKey = appSettings.GetValue<string>(APIKEYNAME);
if (!apiKey.Equals(extractedApiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized client. (Using ApiKeyMiddleware)");
return;
}
await _next(context);
}
}
and in my startup I added a middleware component:
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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
// .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test002.Api", Version = "v1" });
});
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Test002.Api v1"));
}
app.UseHttpsRedirection();
app.UseMiddleware<ApiKeyMiddleware>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
When I open swagger configuration Im getting 401: Unauthorized.
What I'm missing?
here is the link to the solution on my gdrive.
You have set the response StatusCode as 401 when the request header doesn't contain ApiKey, or it doesn't match that in appsettings.json. When you test with swagger, it won't add ApiKey in the request header.
I created an an API application using .NET Core on VS Code.
I type dotnet run in the terminal and its result is shown in the below pic:
but when I browse URL in browser it shows:
what should I do? I changed the port but not work yet.
this is my StartUp:
namespace API
{
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.AddSwaggerGen(c =>
// {
// c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
// });
}
// 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.UseSwagger();
// app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
}
// app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
and this is my program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
If you check the code, the default route of API controller may look like this Route("api/[controller]"), which does not include [action] token in attribute route.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET /api/values
[HttpGet]
public IActionResult Get()
{
return Ok("Data From Get Action");
}
// GET /api/values/3
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok(id);
}
// POST /api/values
[HttpPost]
public IActionResult Post()
{
return Ok("Data From Post Action");
}
}
To access the first action, you can make request with following URL.
https://localhost:5001/api/values
Besides, if you really want to match /api/values/get to your endpoint, you can try to modify the code as below.
// GET /api/values/get
[HttpGet("get")]
public IActionResult Get()
{
return Ok("Data From Get Action");
}
I am trying to add swagger+swashbuckle to my ASP.NET Core project. I can get the Swagger UI up and running but it is completely empty. I tried poking around and found a similar problem at https://github.com/domaindrivendev/Swashbuckle/issues/1058. This made me think that maybe the routing was the issue so I tried giving my controller an explicit route using [Route("testroute")] over the method but not the class. This made the endpoints I added a route to show up with no problem.
As adding an explicit route to every endpoint is non-optimal, what am I doing wrong and how do I fix it to get swagger to show all my endpoints?
My startup where swagger is integrated
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true , reloadOnChange: true);
Configuration = builder.Build();
}
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.AddMvc().AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
services.AddDbContext<PromotionContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:Jasmine"]));
services.AddTransient<PromotionDbInitializer>();
services.AddTransient<IComponentHelper, ComponentHelper>();
services.AddTransient<IComponentFileHelper, ComponentFileHelper>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, PromotionDbInitializer promotionSeeder)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Promotion}/{action=Index}/{id?}");
});
//Because there is not a seed method built into the EF migrations pipeline in EFCore this seeding method will interfere with the migrations when attempting to deploy the database
//uncomment if you need to seed
//promotionSeeder.Seed().Wait();
}
}
My controller where the GetAll and Post method show up on the swagger ui page under testroute and testFormRoute, but the Get and Delete methods do not show up
public class PromotionController : Controller
{
private PromotionContext context;
public PromotionController(PromotionContext _context)
{
context = _context;
}
public IActionResult Index()
{
return View();
}
[HttpGet]
[Route("testroute")]
public IActionResult GetAll()
{
try
{
var result = context.Promotions
.Include(promotion => promotion.CombinabilityType)
.Include(promotion => promotion.ValueType)
.Include(promotion => promotion.Currency)
.Include(promotion => promotion.Components)
.ThenInclude(component => component.TargetType)
.ToList();
return Ok(result);
}
catch(Exception ex)
{
return StatusCode(500);
}
}
public IActionResult Get(string promoCode)
{
try
{
var result = context.Promotions
.Include(promotion => promotion.CombinabilityType)
.Include(promotion => promotion.ValueType)
.Include(promotion => promotion.Currency)
.Include(promotion => promotion.Components)
.ThenInclude(component => component.TargetType)
.FirstOrDefault(x => x.PromoCode == promoCode);
return Ok(result);
}
catch(Exception ex)
{
return StatusCode(500);
}
}
[HttpPost]
[Route("testFormRoute")]
public IActionResult Post([FromForm] Promotion newPromotion)
{
try
{
context.Promotions.Add(newPromotion);
context.SaveChanges();
}
catch(DbUpdateException ex)
{
return StatusCode(500);
}
return Ok();
}
[HttpDelete]
public IActionResult Delete(string promoCode)
{
try
{
var promotion = context.Promotions.FirstOrDefault(x => x.PromoCode == promoCode);
if(promotion != null)
{
context.Promotions.Remove(promotion);
context.SaveChanges();
}
}
catch(DbUpdateException ex)
{
return StatusCode(500);
}
return Ok();
}
}
Add a route attribute to your controller:
[Route("[controller]/[action]")]
public class PromotionController : Controller
{
...
And set the HttpGet attribute on your actions:
[HttpGet]
public IActionResult GetAll()
{
...
[HttpGet("{promoCode}")]
public IActionResult Get(string promoCode)
{
...
You have to be careful about how you mix and match static and dynamic routes. Check out this article for more detail about attribute based routing in asp.net core.
Try changing
public class PromotionController : Controller
to
public class PromotionController : ApiController