Cant' load custom css for Swagger ASP.Net Core 6 - asp.net-core-webapi

I am trying to load a custom .css for my swagger page that I added to an ASP.Net Core 6 Web API application.
var app = builder.Build();
app.UseStaticFiles();
app.Logger.LogInformation("Setup application");
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.DocExpansion(DocExpansion.None);
c.InjectStylesheet("/swagger-ui/custom.css");
});
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto
});
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Logger.LogInformation("Running application");
app.Run();
I added 'swagger-ui' folder in my project folder and added 'custom.css' file in the folder. Its build action is set to 'Embedded resource' and copied to output folder. I have added the inject to my UseSwaggerUI in Program.cs, but when the swagger page loads it still loads the default 'swaggerui.css' sheet. What am I missing?

I found that specifying the static file path in the staticFile options solved it for me.
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "swagger-ui")),
RequestPath = "/swagger-ui"
});

Related

Blazor without Duende Identity Server

I have a small Blazor WASM project that I recently migrated to .net 6. But now I tried to run the published project and the application warned me that I don't have a license for Duende Identity Server.
My question is:
Can I do without the Duende Identity Server?
In my application, I need user login and role assignment. I want to have users defined only for this application and I want to use the application database to store them.
My Program.cs looks like this:
var builder = WebApplication.CreateBuilder(args);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
var appDbConStr = builder.Configuration.GetConnectionString("AppDbConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(appDbConStr));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddClaimsPrincipalFactory<AppClaimsPrincipalFactory>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppDbContext>(opt =>
{
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Role);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Role);
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Avatar);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Avatar);
opt.IdentityResources["openid"].UserClaims.Add(UserClaim.Nick);
opt.ApiResources.Single().UserClaims.Add(UserClaim.Nick);
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
builder.Services.AddAuthentication().AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient<IRazorRendererHelper, RazorRendererHelper>();
builder.Services.AddScoped<Vks.Server.Services.SerialGenerator>();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
Thank you
Use default ASP.NET CORE Identity (do not add reference to Duende).
Must read article covers all features of ASP.NET CORE Identity
https://chsakell.com/2018/04/28/asp-net-core-identity-series-getting-started/
Remove Duende reference by deleting reference on Microsoft.AspNetCore.ApiAuthorization.IdentityServer (duende is sub referenced)
Add asp.net core identity as follow:
builder.Services
.AddIdentity<ApplicationUser<int>, ApplicationRole>(config =>
{
config.SignIn.RequireConfirmedEmail = false;
config.Lockout.AllowedForNewUsers = true;
config.Lockout.MaxFailedAccessAttempts = 3;
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
//...other opts//
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<CustomUserManager>() // inherited from UserManager with overriden logic
.AddDefaultTokenProviders()
.AddTokenProvider<CustomAuthenticatorTokenProvider>(TokenOptions.DefaultAuthenticatorProvider) // inherited from AuthenticatorTokenProvider with overriden logic
.AddPasswordValidator<CustomPasswordValidator>(); // implements IPasswordValidator for additional password validation

Does not start ASP.net service with SPA (React)

Please help me launch the web service. I run the .exe file from the folder "\bin\Debug\net 5.0". When prompted https://localhost:5001/ gives an error.
I specified it in the spa.Options.sourcepath, but it didn't help.
the ERROR that I received is the following:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HMD866NK2DDC", Request id "0HMD866NK2DDC:00000011": An unhandled exception was thrown by the application.
System.InvalidOperationException: The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request.
Your application is running in Production mode, so make sure it has been published, or that you have built your SPA manually. Alternatively you may wish to switch to the Development environment.
This is the folder structure ClientApp Paths
My config code:
public void ConfigureServices(IServiceCollection services)
{
// получаем строку подключения из файла конфигурации
string connection = Configuration.GetConnectionString("DefaultConnection");
// добавляем контекст ApplicationContext в качестве сервиса в приложение
services.AddDbContext<ApplicationContext>(options =>
options.UseSqlServer(connection));
//чтобы кирилица нормально отображалась
services.AddWebEncoders(o =>
{
o.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic, UnicodeRanges.CyrillicExtendedA, UnicodeRanges.CyrillicExtendedB);
});
services.AddControllersWithViews();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// 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();
}
else
{
app.UseExceptionHandler("/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.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
React assets are not built by dotnet build or dotnet run not even with -c Release.
To also build the React assets you need dotnet publish.
This requirement may be masked by the presence of assets due to earlier use of dotnet publish. In that case they are potentially stale. Therefore, for any environment other than dev, you must use dotnet publish.
Why don't dotnet build and dotnet run build the React assets? In development you don't need them because you're using the dev server for hot swap. In production you're almost certainly using assets prepared by dotnet publish.
AspNetCore React can only find spa files in dev mode

How to detect page refresh in .net core angular SPA template?

I have project setup with .net core MVC SPA template. When the application is loaded, it loads angular spa into MVC.
My startup configure has below code to load SPA.
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
Once the SPA is loaded, there is no contact with server side on the MVC site. However when the page is reloaded, I would like to intercept this call and do something with the HTTPRequest and HTTPResponse. How do I achieve this ? I do not have any controller in the MVC project.
My project structure looks like this.
WEB
- wwwroot
- ClientApp ---> Angular spa
- Controllers ---> Empty
- Pages ---> Empty
- startup.cs
- program.cs
For intercepting the request between client and server, you could try ASP.NET Core Middleware. All the requests from client will be handled by middleware.
A simple code like below:
app.Use((context, next) =>
{
Console.WriteLine(context.Request.Path);
return next.Invoke();
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
Update
app.Map("/css/site1.css", map => {
map.Run(context => {
context.Response.Redirect("/css/site2.css");
return Task.CompletedTask;
});
});

Can't read swagger JSON file on ASP.NET Core 1.2 Application after hosting into local IIS

After hosting my asp.net core 1.2 application, I am getting an error as:
swagger is unable to find the swagger.json file.
I have tried to solve the problem by giving a virtual path name app.UseSwaggerUI() but it's not working.
Edit to clarify question based on comments:
After hosting Asp.net core application in IIS, the swagger.json file is generating on localhost:<random_port>/swagger/v1/swagger.json path.
How do I serve the swagger.json file on a custom route like:
localhost:<random_port>/virtualpathname/swagger/v1/swagger.json
I have tried to set a virtual path in app.UseSwaggerUI() like {virtualpathname}/swagger/v2/swagger.json but still it is not working
Could be a few reasons for this - one being that .Net Core doesnt serve static files by default (although looking at online examples this doesnt seem to be an issue).
If you havent already, try installing the package Microsoft.AspNetCore.StaticFiles and adding UseStaticFiles() in your Configure() method in Startup.cs with the following configuration. I dont think that the order is important, but this is the order I have mine running in a working app.
public void Configure(...)
{
// Enable middleware to serve static files (like .json)
app.UseStaticFiles();
//Enable middleware for your API
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger();
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "YourApp API V1");
});
}
You will also need SwaggerGen middleware configured in your ConfigureServices() method.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "api_name", Version = "1.0"});
});
Edit Based on comment - to serve swagger json on a custom route:
// Enable middleware to serve generated Swagger as a JSON endpoint on a custom endpoint
app.UseSwagger(c => c.RouteTemplate = "custom/swagger/{documentName}/swagger.json");
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
// Using custom endpoint defined above
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/custom/swagger/v1/swagger.json", "YourApp API V1");
});
If you need to serve SwaggerUI on a custom route as well, then:
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
// Using custom endpoint defined above
// And serving UI on a custom route
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/custom/swagger/v1/swagger.json", "YourApp API V1");
c.RoutePrefix = "custom"; // serves UI on http://{domain}:{port}/custom/
});
I suggest you to perform the two next steps.
First, open your project web.config and enable stdoutLogEnabled. (Remember to create the folder logs on your application folder and give it proper permissions)
Second, make sure you're doing the right configuration. (https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger)
Note: The first step is going to give you more details about the error you're facing.
In my case the issue was the virtual directory which I fixed by adding a relative path(../). In any case make sure you setup ConfigureServices first, then when Configure make sure everything is in order, UseSwagger should be before UseMvc and at the end UseSwaggerUI
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info { Title = "Utility", Version = "v1" });
});
// initialize configuration
var conf = new ConfigurationHelper(Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath);
Configuration = conf.Configuration; // just in case
// inject the RestApiWrapperService as singleton into the services configuration
var restService = new RestApiWrapperService(conf);
services.AddSingleton<IRestApiWrapperService>(restService);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseSwagger();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// app.UseMvc();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseSwaggerUI(s => {
s.RoutePrefix = "help";
s.SwaggerEndpoint("../swagger/v1/swagger.json", "Utility");
s.InjectStylesheet("../css/swagger.min.css");
});
Change the following on your startup.cs class:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyService.API v1");
});
To
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/MyWebsiteName/swagger/v1/swagger.json",
"MyService.API v1");
});
[MyWebsiteName] being the name of application configured in IIS.
I happened to have a simple copy paste mistake!
see the first line in below code, the if statement env.IsDevelopment() is causing this section to not run when deployed to IIS. One option is to comment it out!
//if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger(c =>
{
c.RouteTemplate = "swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c => {
c.RoutePrefix = "swagger";
c.SwaggerEndpoint("/swagger/v1/swagger.json", "StockOps.WebAPI v1");
});
}

Angular 2 routing with ASP .Net Core (non-mvc)

I'm trying to set up routing with .net core and Angular 2 but the routes do not resolve because they are resolved by the server.
One solution I have seen is to register a default route to your home controller or something... but I don't have any MVC controllers.
I've added this to my main component (and done all the other router prerequisites)
#RouteConfig([
{ path: '/', name: 'Table', component: TableComp, useAsDefault: true },
{ path: '/login', name: 'Login', component: LoginComp }
])
And I do have these in startup.cs:
within ConfigureServices()
services.AddMvc();
within Configure()
app.UseMvc();
But since I'm not actually using any MVC Controllers or registering any MVC routes, I'm at a loss as to how to get my angular routes to resolve in the browser rather than the server, and why they aren't just doing the thing...
The following configuration should fit most projects using client side routing in .NET Core:
DefaultFilesOptions options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("index.html");
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
})
.UseCors("AllowAll")
.UseMvc()
.UseDefaultFiles(options)
.UseStaticFiles();
You are looking for the Microsoft.AspNetCore.SpaServices found in this github repo. Look for this example.
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
There is an older post here with some previous versions, but the setup should be very similar.
Try this example Asp Core + Angular2 + Swashbuckle + Docker.
It uses UseMvc() for C# API controllers. And UseStaticFiles() to serve AngularJs (and other static) files.
So you're running Asp Core backend as service. Using Webpack you can build AngularJs application from Typescript source code. It will be published to public folder Backend looks to serve statics from.
I put index.html in wwwroot and use DefaultFiles() in start up page. The webserver knows and finds the default file - index.html automatically.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
}

Resources