Asp Core UsePathBase : how to deal with AJAX calls? - asp.net-core

I want to host two version of my ASP Core website on the same VPS, one for each supplied environement (Staging and Test). I would like both web sites to be accessible from a subfolder of the same URL:
http://www.mywebsite/Staging
http://www.mywebsite/Test
After reading a lot I found the following Github issue: https://github.com/aspnet/Hosting/issues/815
This tip works but for AJAX calls it crashes: Indeed, the JavaScript calls are not aware of this new path (BUG?). All requests are sent to the root base path (ignoring /Staging or /Test).
I partially solved it usig a middleware that redirects my AJAX calls to the correct path. It works for GET requests but it obviously fails for POST request that have a body.
Note that if we don't redirect, and just change the original PathBase request in the middleware, if the API call needs any authentication schema, it will throws a 401 error: The original PathBase is the root / where the response has a different path base, i.e. /Staging, then the cookie in the header prevents from this cross path base request/response.
Could someone tell me what is the recomanded practice to achieve my needs?
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env
{
app.UsePathBase($"/{env.EnvironmentName}");
...
}
My Middleware :
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers[RequestedWithHeader] == XmlHttpRequest)
{
if (string.IsNullOrEmpty(context.Request.PathBase))
{
if (context.Request.Method == HttpMethod.Post.Method)
{
// Do what ?
}
else
{
//Get request : set environement name + original path + original query string
context.Response.Redirect(context.Request.Path.ToString().Insert(0, "/" + _env.EnvironmentName) + context.Request.QueryString);
return;
}
}
}
}

Of course, your client-side code will not be able to automatically change its code based on the path base that is active for the current request. JavaScript code is (usually) static content, so unless you actually rewrite the code dynamically, it cannot update URLs that are embedded inside.
You could determine the base path from the current request’s URL but that is a rather fragile solution since you need to find a logic that will work regardless of where in the application you are. So doing so is not recommended.
The overall problem is that on the client-side, there is no concept of a path base (or even an environment). So you will have to transfer that knowledge from the server to the client. And just like the path base is set for the server-side code using the path base middleware, you will need some server-side code that will pass the path base to the client-side code.
What I personally do is just configure the root path to the API in the application layout. So my _Layout.cshtml contains the following line:
<body data-api="#Url.Content("~/api/")">
That way, I can then retrieve the root path to the API using document.body.dataset.api in JavaScript. My AJAX calls are then all made so they respect that value. That way, I can host the site at any path, and the AJAX calls will use the proper path too.
You could also just use "~" as the content path to just refer to the root of your application. And you could also expose this value in a JavaScript variable inside a <script> block instead, if you prefer that.

Related

Only Map existing Blazor Pages(Razor Components) in the pipeline?

The usual pattern for Blazor (server) apps, is that at the end pf program.cs there is app.MapFallbackToPage("/_Host");
That will result in ALL request paths, that match no earlier endpoint, executing the Blazor App. Sure the Blazor App has a <NotFound> Renderfragment, but that will still result in establishing the SignalR and so on. What if I want to display a static file (.html) from wwwroot or a specfic RAZOR PAGE (.cshtml), which doesnt establlish a websocket for all those cases where the route does NOT EXIST IN ANY BLAZOR PAGE ???
NavigationManager.NavigateTo cannot be used directly inside the BuildRenderTree markup of the <NotFound> ... I could maybe put a component inside there that redirects OnInitialized ... but that would still first require the SignalR.
How can I completely avoid Blazor/SignalR for URLs that don't exist in the Blazor App ?
Idea: I could try to add ALL POSSIBLE Blazor-routes as fallbacks and the most general fallback to some static file or Razor Page... but that seems LIKE A LOT OF DOUBLE WORK and very error prone... is there no way?
app.MapFallbackToPage("~/Admin/{*clientroutes:nonfile}", "/_Host");
app.MapFallbackToPage("~/SomeRoute/More/{*clientroutes:nonfile}", "/_Host");
app.MapFallbackToPage("~/SomePage/{*clientroutes:nonfile}", "/_Host");
app.MapFallbackToPage("/NotValidRoute");
Edit:
#Reason:
I wanted to have a single fallback page (razor page or static) for all routes/urls that don't match any target in the app. But sadly as it seems, that conflicts with blazor'S client side routing. since the server-side routing does not know, which routes should be handled by blazor, it also cannot know, which routes would not map to anything (razor pages and blazor combined). Other please correct me here.
There's server-side routing and client-side routing. On the server side, the middleware pipeline runs first, handling static files and server defined routes, including the fallback route. Fallback routes are routes that don't match static file requests and have the lowest priority, so they are not preferred over other matching routes.
Server-side routing has no idea what routes are going to match on the client-side. That data model is separate. What's happening is, when no server route matches, the host page is rendered, which bootstraps the client. Now you've transitioned to client-side routing and cannot re-enter (without a change to refresh the browser)
What if I want to display a static file (.html) from wwwroot or a specfic RAZOR PAGE (.cshtml), which doesnt establlish a websocket for all those cases where the route does NOT EXIST IN ANY BLAZOR PAGE ???
That requires knowing what routes exists on the client.
Idea: I could try to add ALL POSSIBLE Blazor-routes as fallbacks and the most general fallback to some static file or Razor Page... but that seems LIKE A LOT OF DOUBLE WORK and very error prone... is there no way?
Possible using the <NotFound> component to run some JavaScript and redirect the browser to a fixed route on the server.
Edit:
Another would be to build that found all routable razor components and map them all as fallback routes to "_Host".
Something like this (untested):
// This should match wherever components are declared.
var types = typeof(Program).Assembly.GetTypes();
foreach (var type in types)
{
if (typeof(ComponentBase).IsAssignableFrom(type) && type.GetCustomAttribute<RouteAttribute>() is { } routeAttribute)
{
var route = routeAttribute.Template;
endpointRouteBuilder.MapFallbackToPage(route, "/_Host");
}
}
It partly depends on how your app is hosted. In IIS, you can add applications to any website, which are basically websites of any type you want with a "/path" added.
Currently, my company's main app is "ABCsite.com," and my Blazor app is "ABCsite.com/Blazor"

how to get controller and action name from RewriteContext

having a rewrite rule like this:
public class MyRule : IRule
{
public void ApplyRule(RewriteContext context)
{
var path = context.HttpContext.Request.Path;
///var controllerName = ???
}
}
how do I get the controller and action name ?
in Startup.cs /Configure I have:
var rewriter = new RewriteOptions().Add(new MyRule());
app.UseRewriter(rewriter);
app.UseStaticFiles();
app.UseMvc(
...
Rewrite Rules wont know anything about the controller and action.
It is way too early in the pipeline to know anything about MVC routing and is usually associated with rewriting to resources based on a pattern defined for the request.
URL rewriting is the act of modifying request URLs based on one or more predefined rules. URL rewriting creates an abstraction between resource locations and their addresses so that the locations and addresses aren't tightly linked.
URL rewriting is valuable in several scenarios to:
Move or replace server resources temporarily or permanently and maintain stable locators for those resources.
Split request processing across different apps or across areas of one app.
Remove, add, or reorganize URL segments on incoming requests.
Optimize public URLs for Search Engine Optimization (SEO).
Permit the use of friendly public URLs to help visitors predict the content returned by requesting a resource.
Redirect insecure requests to secure endpoints.
Prevent hotlinking, where an external site uses a hosted static asset on another site by linking the asset into its own content.
Reference URL Rewriting Middleware in ASP.NET Core

Deep linking single page application with dotNet core

I am using a dotNet core project to host an Angular2 application. I am having problems with the deep linking URLs.
For example, when I initially browse to http://localhost:54675/app/dashboard I get a 404 error because there is nothing to serve at app/dashboard. I want to actually load index.html (the angular app) and then have routing take me to app/dashboard.
I use the code below to redirect to index.html if I get a 404 and the URL has no extension.
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();
}
});
This will not work when I have a routing with parameters that include JSON such as:
http://localhost:54675/app/repairReturnListing;filter=%7B%22Status%22:[%22AWP%22]%7D
My if statement ignores requests with an extension and Path.HasExtension throws and ArgumentException on this path. The path resolves to this on the server side:
"/app/repairReturnListing;filter={\"Status\":[\"AWP\"]}"
I removed the 'HasExtension' condition and then I get a lot of console errors looking for map files that I don't host. Like this:
Failed to parse SourceMap:
http://localhost:54675/lib/js/rxjs/operator/timeout.js.map
I don't get these errors in the network tab. I think this is something used for debugging.
My angular2 app uses HTML5 routing. I use static files to serve the angular2 application. I have one webApi controller that returns some configuration data (the rest of the data is returned by another webApi project).
Waiting on a 404 and redirecting seems like a work-around and it's not even working.
Any suggestions on the best way to do this?
Check out ng2-kestrel-appserver http://tattoocoder.com/kestrel-as-a-static-server-for-angular/
Does exactly what you're looking for. It was created for RC2 but should work for the current release with few or no changes.

Servicestack Swagger UI endpoint not behaving as expected with UseHttpsLinks

Using 4.0.31, my AppHost Configure method is declared like this:
public override void Configure(Funq.Container container)
{
HostConfig hc = new HostConfig()
{ HandlerFactoryPath = "api", UseHttpsLinks = true };
SetConfig(hc);
Plugins.Add(new CorsFeature());
Plugins.Add(new SwaggerFeature());
...
}
I was under the impression that this would set the url for swagger-ui to be the https version of ../api/resources, however I'm still getting the regular http endpoint in the swagger URL textbox, like:
http://example.com/myapp/api/resources
Our web servers are behind load-balancers that perform SSL offloading (and require SSL), so the URL should be:
https://example.com/myapp/api/resources
On a positive note, using 'UseHttpsLinks' actually allows swagger-ui to function once you fix the endpoint url. It didn't work at all without it!
I'm overriding the swagger url property using a replacement index.html via the Virtual File System as a hack -- since in the development, QA, and staging environments, the url property is pointing to production...(not so good)
Config.UseHttpsLinks has been expanded to also apply to the BaseUrl where it now changes http:// urls to https:// in this commit.
This change is available from v4.0.33+ that's now available on MyGet.

Dynamically setting the BaseUrl within configuration in Symfony2

I know that within Symfony2's configuration, there is no reference to the base url, as there is no request; the application could either run in cli or within a web server, and therefore we cannot rely on request. But still, I have configuration that asks for stylesheets or javascript base url (such as the JQueryHelperBundle, where you can set your jquery local path - being the local url). The thing is, is there a way to dynamically set a base url for the configuration, without having to change it so that:
The application can move from any directory under development, whether www/myproject or www/foo/myproject without having to change the settings
Production would work the same, except that rewriting the base url with apache would be detected (virtualhosting is common, where the baseurl is mapped to the web directory as '/').
Is there a way to get that base url information? Would using the difference between $_SERVER['DOCUMENT_ROOT'] minus the kernel root dir be a way to detect such base url? But what about virtualhost rebasing the url to / on the web directory? Hardcoding the base url completely couples the project to where it stands in development, and moving project around would require to change the base url everytime, which is annoying.
So, is there a way to dynamically detect the base url within Symfony2's configuration, according to the environment, without depending on the request?
I had to do that in a service, so I injected the router service in my own service and then:
$baseUrl = $router->getContext()->getHost();
But I considered it more as an hack that a real fixture of Symfony2 framework. For instance, in Controller you can generate absolute url easily (example from the symfony book):
$router->generate('blog_show', array('slug' => 'my-blog-post'), true);
And in the twig template, you have the {{ url }} function
I hope this help