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

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

Related

Serve Classic ASP in Blazor WebAssembly Hosted

I have a Blazor WebAssembly Hosted site that I need to use to server up some classic ASP applications the customer needs to use until they are converted to Blazor.
I put the required asp files in the Client wwwroot folder. I then added the following logic to the server Program.cs file. What is happening is that it is serving up the full asp file, without processing it on the server to an html page. How do I get that to happen?
app.MapWhen(ctx => { var p = ctx.Request.Path.ToString(); return p.ToLower().EndsWith(".asp"); }, asp =>
{
asp.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
endpoints.MapFallbackToPage("/_Host");
endpoints.MapGet("/{Page}.asp", x =>
{
x.Response.Redirect("/{Page}.asp");
return Task.CompletedTask;
});
});
});
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();

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

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

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