NET CORE - How to create Home Page in API? - asp.net-core

Everytime my api is started, it's executed with LOCALHOST:PORT/api/values/. How to LOCALHOST:PORT/ with a static home page?

In your project, locate your launchSettings.json file. In visual studio you will need to expand Properties to find it from solution explorer or use Ctrl + T. This file contains an array of profiles. Each profile has a launchUrl field where you can mention your path as empty.
As of adding a content in the home page, you can always make a middleware as follows:
app.Use(async (context, _next) => {
if (string.IsNullOrEmpty(context.Request.Path.ToString())
|| context.Request.Path.ToString() == "/")
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Web API is now running.");
}
else
await _next();
});
You can always have an action, But I would recommend using a middleware like the above.

possible duplicate of How to set start page in dotnet core web api?
I assume you mean having a default page when the user navigates to http://localhost instead of calling http://localhost/api/controller.
In .net core 2 it's fairy easy to do. You can use static files if you only want to show a simple static page by adding
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...other code here...
app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List<string> { "index.html" } });
app.UseDefaultFiles();
app.UseStaticFiles();
}
and making sure there is an index.html in the wwwroot folder.
or you could use routing in mvc
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
see answer by aslan at https://stackoverflow.com/a/40651363/3786363
oh and unless your server is mapped to port 80 you will probably need to call localhost:port not just localhost.

Are you looking for something like this?
$.ajax({
url: "/api/values/METHOD/?PARAM=0",
type: "GET",
dataType: "json",
cache: false,
statusCode: {
200: function (data) {
//Do stuff
}
}
});
Anything running within the solution context will start at the root.

Related

How to resolve ~/ aka application relative url in a middleware?

I'm struggling to redirect a user to the correct location in an ASP.NET Core middleware.
What I'm trying to archieve is an application relative redirect, so if my app runs as https://www.example.com/app any link generated should be rooted there.
E.g. ~/path should resolve to /app/path.
I tried using UrlHelperFactory, but that's MVC and needs an ActionContext, which does not exist inside a middleware.
Heres a code sample of my middleware:
app.Map(pathMatch, appMap =>
{
// [... more code ...]
appMap.Use((context, next) =>
{
var nextUrl = context.Request.Query["nextUrl"];
var applicationBase = context.Request.PathBase;
if (nextUrl == StringValues.Empty || !UrlUtil.IsLocalUrl(nextUrl[0]))
{
context.Response.Redirect(applicationBase);
}
else
{
context.Response.Redirect(applicationBase.Add(nextUrl[0]));
}
return Task.CompletedTask;
});
});

Setup Asp.Net Core app to launch Vue SPA with webpack and HMR

I'm trying to set up a new application using ASP.NET Core 3.0 and Vue. I've been struggling to set it up properly so that the backend and frontend servers are correctly integrated, with proper routing and working HMR. I do have it working now, but I can't tell if this is the correct way do this, and there are some pieces that I don't feel are working as well as they can.
My current setup:
Webpack is configured to output files into wwwroot. I also have HMR and
WriteFilePlugin enabled when running webpack-dev-server:
mode: 'development',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../wwwroot'),
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// write files to fs instead of memory
// so that the server can access and serve index.html
new WriteFilePlugin(),
],
devServer: {
hot: true,
watchOptions: {
poll: true,
},
port: 9001,
},
In Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
.....
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "wwwroot";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
if (env.IsDevelopment())
{
if (System.Diagnostics.Debugger.IsAttached)
{
// run the dev server
endpoints.MapToVueCliProxy("{*path}", new SpaOptions { SourcePath = "ClientApp" },
npmScript: "serve", regex: "Compiled successfully");
}
}
// serve index.html file when no api endpoints are matched
endpoints.MapFallbackToFile("index.html");
}
My problems with this setup:
Typically, HMR builds files in memory, but I have it set up here to write the new files to the file system so that the backend server can access and serve the index.html. This works, but now I have tons of bloat being written to my file system. Is there a way to use the in-memory HMR in conjunction with the ASP.NET server?
The proxying isn't always reliable; I often get errors in the console that it can't connect to the server. Additionally, index.html sometimes gets cached and I have to do a hard reload. Can this be made more reliable somehow?

Logout of MVC Application From Angular 5

We have an angular 5 application built on top of MVC authentication. The application is served up from the Home/Index action, and once the application is loaded, the angular routing takes care of pretty much everything. We are doing this primarily because we wanted to use MVC's Authentication processes (we are using Identity Server 4 as our Oath system).
This works well with one exception: logout. When we attempt to logout, the application seems to be immediately reauthorized and reloads instead of returning us to our Identity Server login page.
Originally, we had success in our development environment through this code:
[HttpPost]
public async Task<IActionResult> Logout()
{
foreach (string key in Request.Cookies.Keys)
{
Response.Cookies.Delete(key);
}
await HttpContext.SignOutAsync();
return Ok();
}
But it was a false positive, because all of our applications were running on localhost, so they had access to each other's cookies. Due to this, the Identity Server cookies were cleared along with the cookies for the Angular application.
We attempted to replace that logic with something like this:
public async Task Logout()
{
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
}
However, it did not log out in either environment (however that code works for one of our MVC applications using the same identity server).
To give some background, our logout process for the Angular application comes from a material menu, where we then prompt the user if they really want to logout with a modal, before calling the logout function. Code snippets of this process:
Method called by logout button:
public openDialog(): void {
let dialogRef = this.dialog.open(ModalComponent, {
width: '250px',
data: { text: this.logoutText, ok: this.okText, cancel: this.cancelText }
});
dialogRef.afterClosed().subscribe(result => {
if (result !== undefined) {
switch (result.data) {
case 'ok':
this.logout();
break;
case 'cancel':
break;
default:
break;
}
}
});
}
private logout(): void {
this.sessionService.logOut();
}
Session service:
public logOut(): void {
this.auth.revokeToken();
this.http.post(this.logoutUrl, undefined).subscribe(x => window.location.reload());
}
As mentioned, calling logout this way ends up with a page refresh and the user not really being logged out. There's probably something simple we are missing, but all of the tinkering I've done so far has not led to any success.
EDIT:
Our angular routing is fairly simple (though the pathing prevents us from calling something like home/logout):
{
path: 'parts',
component: PartsComponent,
canActivate: [AuthGuard],
runGuardsAndResolvers: 'always',
data: { title: 'Parts' }
},
{
path: '',
redirectTo: 'parts',
pathMatch: 'full'
},
{
path: '**',
redirectTo: 'parts'
}
Our MVC routes are also fairly straightforward
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
"Sitemap",
"sitemap.xml",
new { controller = "Home", action = "SitemapXml" });
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
I'm not certain how we could easily route to home/logout with the angular routes the way they are. I'm guessing we'd have to add a route for it. We tried that at one point but it never routed correctly. I'm trying to find my notes on what we tried.
After you cleared the local cookies of your application send the user to the end_session_endpoint of your Idsrv. (The code you showed to clear your session should work, if not I'd the config in startup.cs and debug to check if the redirect really removed the cookie in the browser).
E.g. https://demo.identityserver.io/.well-known/openid-configuration
There you see the endpoint. This should remove the session on the Idsrv. Depending on your setup this could kill your sessions on your other applications that uses the same Identity Server instance.
You can read more about this in the docs.
I don't see a problem with the Angular routing interfering with your server side routing. As long as you ofcourse really do a redirect to that page using a regular window.location.replace
And ofcourse like #Win mentioned it would be a good security guideline to revoke refresh_token and reference_tokens if you use those.
I could not answer for Angular; I'm still working on it. However, client web app could ask IDP to revoke access token and refresh token when user signs out at client. For example, in ASP.Net Core -
using System;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace MY_APP.Controllers
{
public class AccountController : Controller
{
[HttpGet]
public async Task<IActionResult> SignOut()
{
var discoveryClient = new DiscoveryClient("IDP_URL");
var metaDataResponse = await discoveryClient.GetAsync();
var revocationClient = new TokenRevocationClient(
metaDataResponse.RevocationEndpoint,
"CLIENT_NAME",
"CLIENT_SECRET");
// revoke the access token
string accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
if (!string.IsNullOrWhiteSpace(accessToken))
{
var revokeAccessTokenResponse = await revocationClient.RevokeAccessTokenAsync(accessToken);
if (revokeAccessTokenResponse.IsError)
throw new Exception("Problem encountered while revoking the access token.",
revokeAccessTokenResponse.Exception);
}
// revoke the refresh token
string refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
if (!string.IsNullOrWhiteSpace(refreshToken))
{
var revokeRefreshTokenResponse = await revocationClient.RevokeAccessTokenAsync(refreshToken);
if (revokeRefreshTokenResponse.IsError)
throw new Exception("Problem encountered while revoking the refresh token.",
revokeRefreshTokenResponse.Exception);
}
return SignOut(
new AuthenticationProperties { RedirectUri = "CALL_BACK_URL" },
CookieAuthenticationDefaults.AuthenticationScheme,
OpenIdConnectDefaults.AuthenticationScheme);
}
}
}

How to ignore routes in ASP.NET Core?

Previously, one would add something like this to Global.aspx.cs, which is gone in .NET Core:
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
Here's what I currently have in my Startup.cs (for .NET Core):
app.UseDefaultFiles();
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" });
});
The problem is that in MVC (pre-Core) routes was a RouteCollection and in .NET Core it's a Microsoft.AspNetCore.Routing.IRouteBuilder so IgnoreRoute is not a valid method.
You could write middleware for this.
public void Configure(IApplciationBuilder app) {
app.UseDefaultFiles();
// Make sure your middleware is before whatever handles
// the resource currently, be it MVC, static resources, etc.
app.UseMiddleware<IgnoreRouteMiddleware>();
app.UseStaticFiles();
app.UseMvc();
}
public class IgnoreRouteMiddleware {
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
if (context.Request.Path.HasValue &&
context.Request.Path.Value.Contains("favicon.ico")) {
context.Response.StatusCode = 404;
Console.WriteLine("Ignored!");
return;
}
await next.Invoke(context);
}
}
.NET Core 3.1
For .NET Core 3.1 with endpoint routing, this seems like the easiest way. You don't need to build a middleware just for this simple case.
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/favicon.ico", async (context) =>
{
context.Response.StatusCode = 404;
});
// more routing
});
The original question is about ignoring routes. If you do want to serve a favicon, you can add HTML markup in _Layout.cshtml like this. This technique allows more control over where the icon is served from.
<link rel="icon" href="#Url.Content("~/images/favicon.ico")" />
I am using both of these techniques in my code.
If you want to make a static file accessible without the routing condition, simply use the build-in StaticFiles Middleware.
Activate it with app.UseStaticFiles();
in Configure Method and put your static files in wwwroot directory.
They're availible on HOST/yourStaticFile
For more information, refer here
inside public void Configure
add
app.Map("/favicon.ico", delegate { });
Allow favicon requests to be parsed by the route handler, and keep your routes to a minimum. Avoid using middleware, this just adds additional complexity to your code and means all other requests must go through the middleware before the route handler, which is worse in terms of performance for busy websites. For websites that aren't busy you would just be wasting your time worrying about this.
See https://github.com/aspnet/Routing/issues/207
In ASP.NET Core, you can write a constrained catch-all route template. To do so, in your ASP.NET Core example, replace the call to routes.MapSpaFallbackRoute with the following:
// Returns the home/index page for unknown files, except for
// favicon.ico, in which case a 404 error is returned.
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^(?!favicon.ico).*$)}",
defaults: new { Controller = "Home", action = "Index" });

Can I set authorization headers with RequireJS?

We want to have 2 sets of resources for our AngularJS app (public/private) which uses RequireJS for dependency management. Basically everything on the login page would be public and once logged in, another angularjs app would be loaded (new requirejs config) that would load resources that require authentication to access.
Is there a way to configure requirejs to set an authorization header when loading resources?
It depends on what you mean by "resources" and how your server is configured. But in general - yes, since you are using AngularJS you can use the $httpProvider to inject an interceptor service.
For example, in a service:
var dependencies = ['$rootScope', 'userService'];
var service = function ($rootScope, userService) {
return {
request: function(config) {
var currentUser = userService.getCurrentUser();
var access_token = currentUser ? currentUser.access_token : null;
if(access_token) {
config.headers.authorization = access_token;
}
return config;
},
responseError: function (response) {
if(response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return response;
}
};
};
module.factory(name, dependencies.concat(service));
Then, after you configure your routes, you can use:
$httpProvider.interceptors.push( 'someService');
You can find some more information on interceptors here: https://docs.angularjs.org/api/ng/service/$http#interceptors
UPDATE
You might be able to use the text plugin to try and receive it, but I don't see the point in protecting client side code. Plus, if you want to use optimization the resources will just come in one file anyway...
config: {
text: {
onXhr: function (xhr, url) {
xhr.setRequestHeader('Authorization','Basic ' + token);
}
}
}
Refer to: custom-xhr-hooks
Another UPDATE
You could also use urlArgs (mainly used for cache invalidation) without using the text plugin:
require.config({
urlArgs: 'token='+token,
...
)}