Good Afternoon,
I've been following the documentation for adding mutual TLS to Identity Server.
However, when I add the following code:
var builder = services.AddIdentityServer(options =>
{
options.MutualTls.Enabled = true;
options.MutualTls.ClientCertificateAuthenticationScheme = "x509";
});
I get this import reference error:
'Identity Server Options' does not contain a definition for 'MutualTls'...
It's the same for AddMutualTlsSecretValidators.
Are those references in a separate library? I scoured the documentation and have been digging around for a while but can't seem to find anything.
Any help you can give will be greatly appreciated.
I tried various imports in my Startup class such as IdentityModel, idunno.Authentication.Certificate and IdentityServer4 but those didn't help.
Here's my Startup class:
using IdentityModel;
using idunno.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
namespace IdentityServer
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication()
.AddCertificate("x509", options =>
{
options.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;
options.Events = new CertificateAuthenticationEvents
{
OnValidateCertificate = context =>
{
context.Principal = Principal.CreateFromCertificate(context.ClientCertificate, includeAllClaims: true);
context.Success();
return Task.CompletedTask;
}
};
});
var builder = services.AddIdentityServer(options =>
{
// Complains about missing reference
options.MutualTls.Enabled = true;
// Complains about missing reference
options.MutualTls.ClientCertificateAuthenticationScheme = "x509";
})
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers())
// Complains about missing reference
.AddMutualTlsSecretValidators();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
}
}
As Ruard van Elburg said, looks like my version of Identity Server 4 was not the most current. When I updated to the latest (2.5.0) it worked.
Related
I am working on project using microservice architecture, I use ASP.NET Core Identity as a separate microservice to create users and roles. I extend users and roles with custom fields and configure Identity in my API project's startup.cs. But while I run my application I got an error as following,
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microservice.IdentityMS.Application.Interfaces.IIdentityMSService Lifetime: Transient ImplementationType: Microservice.IdentityMS.Application.Services.IdentityMSService': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.RoleManager`1[Microservice.IdentityMS.Domain.Models.MembershipRole]' while attempting to activate 'Alexa.IdentityMS.Data.Repository.IdentityMSRepository'.)
Here's my Identity Microservice startup
Startup.cs:
using Microservice.IdentityMS.Data.Context;
using Microservice.IdentityMS.Domain.Models;
using Microservice.Infra.IoC;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microservice.Identity.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();
var userConnectionString = Configuration["DbContextSettings:UserConnectionString"];
var dbPassword = Configuration["DbContextSettings:DbPassword"];
var userBuilder = new NpgsqlConnectionStringBuilder(userConnectionString)
{
Password = dbPassword
};
services.AddDbContext<MembershipDBContext>(opts => opts.UseNpgsql(builder.ConnectionString));
services.AddDbContext<UserDBContext>(opts => opts.UseNpgsql(userBuilder.ConnectionString));
services.AddIdentity<MembershipUser, MembershipRole>(options =>
{
options.Password.RequiredLength = 8;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+ ";
options.SignIn.RequireConfirmedEmail = false;
}).AddRoles<MembershipRole>().AddEntityFrameworkStores<MembershipDBContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Identity Microservice", Version = "v1" });
});
services.AddMediatR(typeof(Startup));
RegisterServices(services);
}
private void RegisterServices(IServiceCollection services)
{
DependencyContainer.RegisterServices(services);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Identity Microservice V1");
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
DependencyContainer.cs:
using MediatR;
using Microservices.Domain.Core.Bus;
using Microservices.Infra.Bus;
using Microsoft.Extensions.DependencyInjection;
using Microservices.ProductMS.Domain.Interfaces;
using Microservices.ProductMS.Data.Repository;
using Microservices.ProductMS.Data.Context;
using Microservices.ProductMS.Application.Interfaces;
using Microservices.ProductMS.Application.Services;
using Microservices.PartyMS.Application.Interfaces;
using Microservices.PartyMS.Application.Services;
using Microservices.PartyMS.Domain.Interfaces;
using Microservices.PartyMS.Data.Repository;
using Microservices.PartyMS.Data.Context;
using Microservices.MasterMS.Application.Interfaces;
using Microservices.MasterMS.Application.Services;
using Microservices.MasterMS.Domain.Interfaces;
using Microservices.MasterMS.Data.Repository;
using Microservices.MasterMS.Data.Context;
using Microservices.MasterMS.Domain.Commands;
using Microservices.MasterMS.Domain.CommandHandler;
using Microservices.ProductMS.Domain.Commands;
using Microservices.ProductMS.Domain.CommandHandler;
using Microservices.PartyMS.Domain.CommandHandler;
using Microservices.AccountMS.Application.Interfaces;
using Microservices.AccountMS.Application.Services;
using Microservices.AccountMS.Domain.Interfaces;
using Microservices.AccountMS.Data.Repository;
using Microservices.AccountMS.Data.Context;
using Microservices.SalesPurchaseMS.Domain.Interfaces;
using Microservices.SalesPurchaseMS.Data.Repository;
using Microservices.SalesPurchaseMS.Data.Context;
using Microservices.SalesPurchaseMS.Application.Interfaces;
using Microservices.SalesPurchaseMS.Application.Services;
using Microservices.IdentityMS.Application.Interfaces;
using Microservices.IdentityMS.Application.Services;
using Microservices.IdentityMS.Domain.Interfaces;
using Microservices.IdentityMS.Data.Repository;
using Microservices.IdentityMS.Data.Context;
using Microsoft.AspNetCore.Identity;
using Microservices.IdentityMS.Domain.Models;
namespace Microservices.Infra.IoC
{
public class DependencyContainer
{
public static void RegisterServices(IServiceCollection services)
{
//Domain Bus
services.AddSingleton<IEventBus, RabbitMQBus>(sp =>
{
var scopeFactory = sp.GetRequiredService<IServiceScopeFactory>();
return new RabbitMQBus(sp.GetService<IMediator>(), scopeFactory);
});
//Subscriptions
services.AddTransient<ProductMS.Domain.EventHandler.CompanyEventHandler>();
services.AddTransient<PartyMS.Domain.EventHandler.CompanyEventHandler>();
//Domain Events
services.AddTransient<IEventHandler<ProductMS.Domain.Events.CompanyEvent>, ProductMS.Domain.EventHandler.CompanyEventHandler>();
services.AddTransient<IEventHandler<PartyMS.Domain.Events.CompanyEvent>, PartyMS.Domain.EventHandler.CompanyEventHandler>();
//services.AddTransient<IEventHandler<SalesMS.Domain.Events.CompanyEvent>, SalesMS.Domain.EventHandler.CompanyEventHandler>();
//services.AddTransient<IEventHandler<SalesMS.Domain.Events.PartyEvent>, SalesMS.Domain.EventHandler.PartyEventHandler>();
//Domain Commands
services.AddTransient<IRequestHandler<CompanySyncCommand, bool>, CompanySyncCommandHandler>();
services.AddTransient<IRequestHandler<ProductSyncCommand, bool>, ProductSyncCommandHandler>();
services.AddTransient<IRequestHandler<ProductCategorySyncCommand, bool>, ProductCategorySyncCommandHandler>();
services.AddTransient<IRequestHandler<PartySyncCommand, bool>, PartySyncCommandHandler>();
//Application Services
services.AddTransient<IMasterService, MasterService>();
services.AddTransient<IProductService, ProductService>();
services.AddTransient<IPartyMSService, PartyMSService>();
//services.AddTransient<IPurchaseService, PurchaseService>();
services.AddTransient<IPurchaseService, PurchaseService>();
services.AddTransient<ISaleService, SaleService>();//SaleMS
services.AddTransient<IAccountMSService, AccountMSService>();
services.AddTransient<IIdentityMSService, IdentityMSService>();
services.AddTransient<IAdministrationService, AdministrationService>();
//Data
services.AddTransient<IMasterRepository, MasterRepository>();
services.AddTransient<MasterDbContext>();
services.AddTransient<IProductRepository, ProductRepository>();
services.AddTransient<ProductsDBContext>();
services.AddTransient<IPartyMSRepository, PartyMSRepository>();
services.AddTransient<PartyMSDBContext>();
services.AddTransient<IPurchaseRepository, PurchaseRepository>();
services.AddTransient<ISaleRepository, SaleRepository>();
services.AddTransient<SPDBContext>();
services.AddTransient<IAccountMSRepository, AccountMSRepository>();
services.AddTransient<AccountDbContext>();
services.AddTransient<IIdentityMSRepository, IdentityMSRepository>();
services.AddTransient<IAdministrationRepository, AdministrationRepository>();//IdentityMS
services.AddTransient<UserDBContext>();
}
}
}
IdentityMSRepository.cs:
public class IdentityMSRepository : IIdentityMSRepository
{
private readonly UserDBContext _userContext;
private readonly RoleManager<MembershipRole> _roleManager;
private readonly UserManager<MembershipUser> _userManager;
public IdentityMSRepository(UserDBContext userContext, RoleManager<MembershipRole> roleManager, UserManager<MembershipUser> userManager)
{
_userContext = userContext;
_roleManager = roleManager;
_userManager = userManager;
}
}
What am I missing ?
Finally I made change as the comment suggested I moved it from DI container to startup file and it's working. #Rena Thank you so much.
After creating basic Web API project based on .NET Core 3.0 framework, all API responses were coming in camel case. I installed SwashBuckle Swagger + built-in JSON serializer from System.Text.Json, specifically, to display enums as strings, everything worked as before. Then, I decided to switch to NSwag + NewtonSoftJson, because of some limitations of built-in serializer with dynamic and expando objects. Now, all API responses are displayed in PascalCase and I cannot change neither naming policy, nor even create custom contract resolver.
Example
https://forums.asp.net/t/2138758.aspx?Configure+SerializerSettings+ContractResolver
Question
I suspect that maybe some package overrides contract resolver behind the scene. How to make sure that API service uses ONLY custom contract resolver that I assign at startup and ignores all other similar settings?
Custom JSON contract resolver:
public class CustomContractResolver : IContractResolver
{
private readonly IHttpContextAccessor _context;
private readonly IContractResolver _contract;
private readonly IContractResolver _camelCaseContract;
public CustomContractResolver(IHttpContextAccessor context)
{
_context = context;
_contract = new DefaultContractResolver();
_camelCaseContract = new CamelCasePropertyNamesContractResolver();
}
// When API endpoint is hit, this method is NOT triggered
public JsonContract ResolveContract(Type value)
{
return _camelCaseContract.ResolveContract(value);
}
}
Controller:
[ApiController]
public class RecordsController : ControllerBase
{
[HttpGet]
[Route("services/records")]
[ProducesResponseType(typeof(ResponseModel<RecordEntity>), 200)]
public async Task<IActionResult> Records([FromQuery] QueryModel queryModel)
{
var response = new ResponseModel<RecordEntity>();
return Content(JsonConvert.SerializeObject(response), "application/json"); // NewtonSoft serializer
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services
.AddCors(o => o.AddDefaultPolicy(builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()));
services
.AddControllers(o => o.RespectBrowserAcceptHeader = true)
/*
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
o.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
o.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
})
*/
.AddNewtonsoftJson(o =>
{
o.UseCamelCasing(true);
o.SerializerSettings.Converters.Add(new StringEnumConverter());
//o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };
o.SerializerSettings.ContractResolver = new CustomContractResolver(new HttpContextAccessor());
});
services.AddOpenApiDocument(o => // NSwag
{
o.PostProcess = document =>
{
document.Info.Version = "v1";
document.Info.Title = "Demo API";
};
});
DataConnection.DefaultSettings = new ConnectionManager(DatabaseOptionManager.Instance); // LINQ to DB
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(o => o.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(o => o.MapControllers());
app.UseOpenApi(); // NSwag
app.UseSwaggerUi3(o => o.Path = "/v2/docs");
app.UseReDoc(o => o.Path = "/v1/docs");
}
Still don't understand why custom contract resolver is not triggered by API endpoint, but found a combination that works for me to switch API to camel case. Feel free to explain why it works this way.
services.AddControllers(o => o.RespectBrowserAcceptHeader = true)
// Options for System.Text.Json don't affect anything, can be uncommented or removed
//.AddJsonOptions(o =>
//{
// o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
// o.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
// o.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
//})
.AddNewtonsoftJson(o =>
{
o.UseCamelCasing(true);
o.SerializerSettings.Converters.Add(new StringEnumConverter());
// This option below breaks global settings, so had to comment it
//o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver
//{
// NamingStrategy = new CamelCaseNamingStrategy()
//};
});
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
Idea was taken from this article.
NewtonSoft allows to set global serialization settings disregarding MVC, Web API, and other frameworks.
After having a hard time getting my area to show with endpoint routing i managed to fix it in this self answered thread (albeit not in a very satisfactory way) : Issue after migrating from 2.2 to 3.0, default works but can't access area, is there anyway to debug the endpoint resolution?
However Identity UI doesn't show at all for me, i get redirected on challenge to the proper url but the page is blank. I have the identity UI nugget package added and, changing from mvc routing to endpoint routing, i didn't change anything that should break it.
I also don't seem to do much different than what the default project does and identity works there even if i add a route as i did in my hack.
As often the issue hides around the line and not on it i'm posting my whole startup file.
Regular (default) controllers work.
Admin area works (one of the page doesn't have authentication and i can access it)
Any other Admin area page redirect me to /Identity/Account/Login?ReturnUrl=%2Fback (expected behavior) but that page as well as any other /Identity page i tested is blank with no error while running in debug and with a debugger attached.
Any help is most appreciated, full startup bellow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using FranceMontgolfieres.Models;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
namespace FranceMontgolfieres
{
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.AddSingleton<IConfiguration>(Configuration);
services
.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddDbContext<FMContext>(options => options
.UseLazyLoadingProxies(true)
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services
.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<FMContext>();
services
.AddMemoryCache();
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = Configuration.GetConnectionString("SessionConnection");
options.SchemaName = "dbo";
options.TableName = "SessionCache";
});
services.AddHttpContextAccessor();
services
.AddSession(options => options.IdleTimeout = TimeSpan.FromMinutes(30));
services.AddControllersWithViews();
services.AddRazorPages();
}
// 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.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("Back", "Back", "back/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default","{controller=Home}/{action=Index}/{id?}");
});
}
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//initializing custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] roleNames = { "Admin", "Manager", "Member" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
}
}
The Identity UI is implemented using Razor Pages. For endpoint-routing to map these, add a call to MapRazorPages in your UseEndpoints callback:
app.UseEndpoints(endpoints =>
{
// ...
endpoints.MapRazorPages();
});
Why is the following line not having any effect? It still gives me a 307!
services.AddHttpsRedirection(options => options.RedirectStatusCode = StatusCodes.Status301MovedPermanently);
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace blog
{
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.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddHttpsRedirection(options => options.RedirectStatusCode = StatusCodes.Status301MovedPermanently);
services.Configure<RouteOptions>(options =>
{
options.LowercaseUrls = true;
});
}
// 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();
}
else
{
app.Use((context, next) =>
{
context.Request.PathBase = new PathString("/blog");
return next();
});
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
}
}
As I read your startup config, I see that while not in development mode, you are also using HSTS. If you are testing with Chrome, analyze the network traffic response headers and see if it contains Non-Authoritative-Reason: HSTS. If this is the case, then HSTS is managing the redirect which is expected. The UseHttpsRedirection middleware is more of a fallback in this case for when the client does not support HSTS.
To test using a client that does not support HSTS and ensure the 301 is actually being returned during HTTPS redirection, use an API client such as Postman or Insomnia and view the timeline of the request.
I have VS2015 and .Net Core Web API project created.
I'm following example in http://www.technicalblogs.sentientmindz.com/2017/04/09/enabling-swagger-support-to-the-web-api/
I have installed Swashbuckle.AspNetCore and next trying to code, but getting errors when using UseSwagger. Please advise me.
/* Startup.cs */
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Owin;
using Owin;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.Extensions.DependencyInjection;
[assembly: OwinStartup(typeof(TestApi.Startup))]
namespace TestApi
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
/*use swagger added by me*/
app.UseSwagger(); /*ERROR:iAppBuilder does not contain definition for UseSwagger…*/
app.UseSwaggerUI(c =>. /*ERROR :iAppBuilder does not contain definition for UseSwaggerUI…*/
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Accounts API V1");
});
}
//Add framework services by me
public void ConfigureServices(IServiceCollection services) {
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "AcccountsAPI", Version = "v1" });
});
}
}
}
I'm assuming from your code that you're using .Net Core v1.1, this is how I've done it:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
config.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebAPI");
c.IncludeXmlComments(GetXmlCommentsPath());
c.ResolveConflictingActions(x => x.First());
}).EnableSwaggerUi();
}
protected static string GetXmlCommentsPath()
{
return System.String.Format($#"{0}\bin\MyApi.XML",
System.AppDomain.CurrentDomain.BaseDirectory);
}