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

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");
});
}

Related

How do I host a WebApplication and a BackgroundService in the same application?

I have a command-line application (similar to what would be created with the dotnet new worker command) that hosts a BackgroundService implementation, like this:
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services => {
services.AddHostedService<MyBackgroundService>();
services.AddSingleton<IMyType>(myinstance);
// and so on...
}).Build();
await host.RunAsync();
Now, I would like this application to also host a web api. Currently, I have a whole separate builder that I'm instantiating inside the MyBackgroundService class, with a separate set of services/singletons/whatever:
var builder = WebApplication.CreateBuilder();
// ... various web stuff...
var webApi = builder.Build();
// ... more web api stuff...
await webApi.StartAsync(cancellationToken);
I'd like to set this up to share a single DI container... how do I do that? My web api uses a WebApplicationBuilder, while my BackgroundService uses a DefaultBuilder (which is a IHostBuilder). Is there any way to set this up elegantly?
Edit: I found this: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0 which gives me the ConfigureWebHostDefaults method for the generic host, but the example isn't complete. I can put the AddControllers() and AddRouting() etc on the generic host, but once I call Build() I can't do things like UseHttpLogging() or UseSwagger() or MapControllers().
Assuming you're starting from a console app and want to add in the API portion, you can do something like:
await Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<SomeBackgroundService>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.ConfigureServices(services =>
{
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
})
.Configure((hostContext, app) =>
{
if (hostContext.HostingEnvironment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
​
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
});
//can optionally use a startup file similar to what older versions of .NET used
//webBuilder.UseStartup<Startup>();
})
.Build()
.RunAsync();
}
Also, assuming you started from a console app, you may need to add:
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
to your csproj, as that will pull in the additional web options.
Edit: It appears the services specific to the web context need to be registered within the webBuilder.

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

CORS issue in IdentityServer 4

I'm using IdentityServer 4 as oauth for my application ( Reactjs ) I'm running Identityserver on port http://localhost:5000 and reactjs app on http://localhost:3000. I have tried using CORS for my idenityserver4 with the following code.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
})
.AddClientStore<ClientStore>()
//.AddInMemoryApiResources(Config.GetApiResources())
.AddResourceStore<ResourceStore>()
//.AddInMemoryClients(Config.GetClients())
.AddCustomUserStore()
.AddCertificateFromFile();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins( "http://localhost:3000/")
.AllowAnyMethod()
.AllowAnyHeader());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
app.UseForwardedHeaders();
if (environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
//app.UseCors("default");
app.UseIdentityServer();
app.UseStaticFiles();
// uncomment, if you want to add an MVC-based UI
app.UseMvcWithDefaultRoute();
}
}
Even though I have added localhost:3000 in WithOrigins(), when I try to make a request from react app with axios I'm getting the following blocked error.
Can someone help me to know where I'm doing wrong. I need my application to only allow some list of origins (apps)
Thanks
It's likely this could be because of the trailing slash, this is mentioned in the documentation.
Note: The specified URL must not contain a trailing slash (/). If the URL terminates with /, the comparison returns false and no header is returned.
Try http://localhost:3000 instead of http://localhost:3000/.
I'd also question the usage of both .AllowAnyOrigin() and .WithOrigins(). What you're looking to achieve should be possible using only .WithOrigins().
If you are sending a request to another domain, try sending a http request from your identity server not react.js app. I encountered a similar issue but i just used my API as a proxy and it worked fine.

asp.net core identity and identityserver

I'm following this walkthrough on integrating asp.net core identity with IdentityServer but have hit a few roadblocks.
Where I'm updating the ConfigureServices method, if I follow the guide and use
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
I can no longer access any of the account related functions. The routing for the register link changes from
~/Identity/Account/Register
to
~/?area=Identity&page=%2FAccount%2FRegister
Which breaks all account related functions
If I leave it at
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Then the routing still works, I can enter my credentials via the login page and the login is successful, but
SignInManager.IsSignedIn(User)
returns false, so I'm guessing something is fundamentally broken here.
I have added identityserver to my ConfigureServices:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.IdentityResources.GetIdentityResources())
.AddInMemoryApiResources(Config.APIResources.GetApiResources())
.AddInMemoryClients(Config.Clients.GetClients())
.AddAspNetIdentity<IdentityUser>();
Any ideas what needs to change - I'm guessing its something in the latest version of asp.net core that has caused this has it?
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();
});
In Net Core 2.1 Microsoft have removed the AccountController and moved all the Identity logic to Razor pages (there is no alternative now available) which makes the logic difficult to follow (it reminds me of ASP classic or PHP). The Quickstart in the documentation relies entirely on the AccountController remaining in place (no longer the case) and guess this needs to be rewritten as Razor pages before anything will work. However, there is not a lot of point in doing this whilst the authentication mechanism is broken.
I used the following Startup.cs to demonstrate that authentication no longer works in IdentityServer4 when added to a new Net Core 2.1 project. It should work but shows the following behaviour when accessing a controller method protected by [Authorize] and the challenge presented as a Login page.
1) Entering the incorrect credentials causes the 'Invalid login attempt' text to be displayed
2) Entering correct credentials fails to authenticate and this can be seen by there being no Logout link or debugging and observing User.isAuthenticated is false
A couple of changes can be made to the Startup.cs in order to show authentication works when IdentityServer is disabled and the standard authentication enabled. Simply comment out the block commencing 'services.AddIdentityServer(options =>
' to disable IdentityServer. Next comment out 'useIdentityServer()' and uncomment 'useAuthentication()' and all the authentications work correctly again.
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.Lax;
});
// Add authentication options
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
});
// Identity Context
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["IdentityConnection"],
sqlOptions => sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().
Assembly.GetName().Name));
},
ServiceLifetime.Scoped
);
// Configure default Identity implementation
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
// Add application services.
services.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender>();
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
})
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// 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();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
I'm not sure how to the authentication working in IdentityServer4 since have not followed how it would work in Net Core 2.1. Has anyone got further than me and got this server working?
Figured this out in the end. Seems like a weird bug as MSFT migrates to Razor pages.
All I needed to do was add in the Scaffolding UI and it just started working

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