Add route in Blazor server side app for .json or .js file to be handled by controller - asp.net-core

This seems like it should be simple but cannot quite work out how.
I want to be able to make a file in the root of the application to by dynamically served.
i.e.
https://localhost:5000/test.json
to be handled by a controller. Its easy if you just have a path without a . but if its a json or js file it seems to get handled differently by the server.

ok really simple - just needed to do this in startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/test.js", context => context.Response.WriteAsync("Hello world"));
});

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"

Get site URL in ConfigureServices method of Startup.cs

I need to register SPA files based on URLs...I have registered AddHttpContextAccessor but not sure how i can check the url there?
services.AddSpaStaticFiles(configuration =>
{
if (GetUrl(_httpContextAccessor).EndsWith("support") || GetUrl(_httpContextAccessor).EndsWith("support/"))
configuration.RootPath = "ClientApp/build";
else
configuration.RootPath = "support-app/build";
});
Why are you trying to change the root path based on the request url?
Is the reason for this requirement that you are you serving multiple SPA's? If that is the case, you might want to check out this thread that describes how to configure the pipeline to serve multiple apps at different paths:
How to handle multiple SPA application in ASP.NET Core
If you only have one SPA, then you might need to describe in more detail what you are trying to achieve.

Asp Core UsePathBase : how to deal with AJAX calls?

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.

prevent handling of missing files by other use/get in express

I'd like to keep the contents of the folder /public for use of local files by a local app. However, when a file is missing, instead of getting the 404, the user is getting the index.html page.
What I would like to do is have user get the 404 when accessing any resource that does not exist for anything under /public/*, but have the react app handle everything else from index.html
this is my setup:
app.use('/public', express.static(path.resolve(myPath, 'public')));
app.get('*', (req, res) => {
res.sendFile(path.resolve(myPath, 'index.html'));
}
should the get include a regex to not include public matches, or is there a way to handle this with use?
The problem is that you've explicitly told express to look into your public folder for ANY file that the user requests with this line:
app.use('/public', express.static(path.resolve(myPath, 'public')));
Express, by default, serves NO files. It makes nothing public by default. So, the first thing to do is to remove the line of code that makes that entire directory public. And, then if you want to serve some specific things from that folder, you need to either make very specific routes to only the files you want to be public or you need to move the "public" files out to a directory where everything can be public and then point express.static() at that directory.
Then, you've added another line to respond to any request possible with one file:
app.get('*', (req, res) => {
res.sendFile(path.resolve(myPath, 'index.html'));
}
This is just not really how you should be using express at all. Here are some steps to think through:
Figure out which static files you want Express to serve automatically (without a specific route being made for each file).
Then, organize those files so they are in their own sub-directory on your server hard drive.
Then, you can point express.static() at that directory without any fear of it serving files you don't want to be automatically public.
Think about organizing things into a hierarchy (not required, but sometimes simpler to manage) so that css files might be one place, client-side js another place, etc... such as /css/css files here and /js/js files here when laying out your hierarchy. Then, you can control the serving of each type of file separately if wanted and it may make maintenance easier (since separate people often maintain CSS files and JS files).
Then, design specific routes you want handled for other types of files.
Then, add an error handler route which determines what should be returned to the browser when no other route handler was found. How to do that is described here. Also, note that Express has a different default error handler based on whether the NODE_ENV environment variable is set to production or not.
Don't use * routes that handle everything with the same content, including things you don't want to provide a specific page for. You don't want search engines to index things that you don't have original content for and you don't want users to bookmark unintended URLs just because you happen to be using * in a route handler.
I was able to resolve it by adding
app.get('/public/*', (req, res) => {
res.status(404).send(req.path + ' not found');
})

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.