Remove additional slash from URL in Asp.Net Core - azure

I have a web-site, hosted on Azure App Service. What I'd like to do is to redirect traffic, such that an address like:
https://mysitecom//test//test2
Or:
https://mysitecom/test//test2
Would redirect to the same location:
https://mysitecom/test/test2
So far, I've tried adding some middleware to deal with this; for example:
app.Use(next => context =>
{
if (context.Request.Path.Value.Contains("//"))
{
context.Response.Redirect(context.Request.Path.Value.Replace("//", "/"), true);
return;
}
});
I've also tried:
var options = new RewriteOptions()
.AddRedirect("redirect-rule/^(.*)//(.*)$", "redirected/$1/%2");
app.UseRewriter(options);
Both of these work locally to an extent; however, it appears that running under Kestrel the slashes are automatically reduced to a single one. When this is deployed to Azure App Service, the following happens:
The slashes remain (e.g. https://mysitecom//test//test2)
The correct page is located
The middleware sees only a single slash
Please can someone explain why the middleware (or the redirect) is only ever seeing a single slash when this is deployed to Azure?
(SO wouldn't allow me to use an actual URL in the post)

If your site runs on a Windows AppService, it runs in IIS. You are encountering an IIS feature (The "URL Rewrite Module", I think). If you run the same site on Linux, it will behave differently.
If you want to detect the "original" URL, you can try something like this:
// => /test/test2
var path1 = Request.Path
// => //test//test2
var path2 = HttpContext.Features.Get<IServerVariablesFeature>()["UNENCODED_URL"]
There are multiple sources for this behavior, though I am not sure how you could turn it off if you wanted to.
Remove multiple forward slashes
https://www.ctrl.blog/entry/relative-double-slashes-url.html
http://www.timacheson.com/Blog/2010/jun/prevent_multiple_slashes_in_url

It should work in .NET Core
var options = new RewriteOptions()
.AddRedirect("(.*)/$", "$1", 301 /* Moved Permanently */);
app.UseRewriter(options);

Related

How to set up rewrite rule in Azure Web App - Linux

I have an Azure Web App running on linux and i want to set up a rewrite rule for my application. When users visit example.com, i will redirect them to www.example.com.
In windows, it is easier to use web.config but how is it done in Linux ?
On the linux, there is no Apache or Nginx installed but i am wondering how the application is running.
How can i get this done ? Set up a rewrite rule on either Apache or Nginx to affect my Azure Web App running on linux ?
In theory, your needs can be solved in two ways.
For more details, you can read this blogs.
And also can download sample code(ASP.NET Core URL Rewriting Sample)to test it.
The first method:
Re-Writing a URL
Here's how to handle a Rewrite operation in app.Use() middleware:
app.Use(async (context,next) =>
{
var url = context.Request.Path.Value;
// Redirect to an external URL
if (url.Contains("/home/privacy")) // you can use equal
{
context.Response.Redirect("https://markdownmonster.west-wind.com")
//return; // short circuit
}
await next();
});
The second method:
The ASP.NET Core Rewrite Middleware Module
The above two methods are officially provided. The actual test needs to be deployed to Azure for testing. If you have any questions, you can also raise a support ticket for help.

What does localhost:1337 become in production?

I'm sure this question is very basic but it's confusing me.
Currently in development mode my backend runs in localhost:1337 (using Strapi cms to be precise).
But what will it turn into when the website is deployed to a web server?
Specifically, the website will reside in a sub-directory and not in the root, will it affect this?
Currently this is a url I'm using inside of my project as images src:
localhost:1337/uploads/image-example.jpg
I'm trying to understnad what will be the new url once the project has been deployed, and what's more it's deployed in a subdirectory and not in the root
Thanks in advance
You shouldn't have to do anything.
Most, if not all, web hosts use the PORT environment variable to configure the application server. I just did a quick code search through Strapi and found this:
"port": "${process.env.PORT || 1337}",
to read the environment variable or use a default value (instead of asking for a random port with 0, for predictability while developing). If you used their starter script, I would assume your application has the same.
In many cases, this is sufficient, with two exceptions.
If your application server doesn't know its URL, it can only generate relative URLs. For example, it won't be able to compose an email containing a link to itself.
HTTP redirects required an absolute URL once upon a time, but that has since been amended.
Setting just the port assumes that the application will be mounted on /. If, for example, you were configuring your own web server (instead of Heroku), and you wished to mount your application on /blog, you would need some way to tell your application so that it can put the right prefix on paths.
It might be possible to get around this by restricting the URLs to relative paths only, but that sounds tedious and I don't think it's usually done. And an incoming request doesn't actually have enough information to discern the URL that the user is looking at. Instead, you show the URL to your app out of band.
For example, Grafana has a root_url variable in its config file where you put the full URL prefix, Prometheus takes a -web.external-url argument, etc.
However, I don't think Heroku can mount your application anywhere other than /, so the port should be sufficient.
Here's a complete server that runs on Heroku and shows its environment to you:
const app = require("express")();
app.set("json spaces", 2);
app.get("/", (req, res) => {
res.send(process.env);
});
const server = app.listen(process.env.PORT, () => {
console.log('server is up on', server.address());
});
The PORT variable is all the configuration it needs; the environment does not include the app name.
Hardcoding the production URL of your app can become a maintenance burden, so make sure it remains configurable with a command line argument or environment variable if you do end up teaching your app what its URL is.
So to answer your question specifically, your image src should begin with /uploads.

Redirect with express in serverless/lambda container not working

I have setup an express application with various routes like:
/api/route1
/api/route2
/api/newroute
…
I need to redirect a request for route /api/route2 to /api/newroute. Thus I use res.redirect(); to achieve this. It works fine on my local development environment, where the application is hosted on http://localhost/api. A request for http://localhost/api/route2 gets successfully redirected to http://localhost/api/newroute.
We have now deployed the express application in a serverless/lambda container using aws-serverless-express. The application is hosted by AWS on https://xxx.execute-api.us-east-1.amazonaws.com/production/api.
In case of a request for:
https://xxx.execute-api.us-east-1.amazonaws.com/production/api/route2
it gets redirected to:
https://xxx.execute-api.us-east-1.amazonaws.com/api/newroute.
The redirect fails as the correct endpoint should be now /production/api/newroute.
I would like to avoid to hardcode the infix “/production” in res.redirect(); as the it will vary due to environment.
Thus I have checked whether
req.originalUrl
req.baseUrl
req.path
will provide the infix “/production”, however this is not the case.
Also req.get('Host') does not provide “/production” as it returns https://xxx.execute-api.us-east-1.amazonaws.com.
Are there any other options how I could get the full URL where the application is hosted? Or are there any better ideas?
I ran into this same issue. Here's my solution:
const baseUrl = req.url
.replace(
event.pathParameters.proxy, // => returns api/your/path
event.requestContext.path // => returns /production//api/your/path
)
.split('//') // fix the double-slashes (//)
.join('/');

Disable default domain https://[project-id].appspot.com of Node JS on Google App Engine

I have deployed my Node JS app onto Google Cloud App Engine, and I can successfully add my custom domain for my app.
Let say my custom domain is example.com.
Now I can browse my app via example.com & www.example.com, it works as expected. But I find that I can still browse my app through the default domain.
https://[project-id].appspot.com
I want to disable the default domain, is it possible to do that?
You cannot disable that default domain. You would have to write a redirect script. You can test for "appspot" in the request's HTTP_HOST, and test for "AppEngine-Google" in the request's HTTP_USER_AGENT, to determine if it is an internal request.
Also, internal functions, like cron jobs and task queues, run on that domain, so be careful what you do. Task queues will fail given a 301.
After considering GAEfan suggestion, I have made some change on the router logic. If the host name is ended with "appspot.com" and the user agent is included "AppEngine-Google", it will redirect to my custom domain.
router.get('/', function(req, res, next) {
if(req.get("host").endsWith(GOOGLE_APP_SPOT) &&
req.get("User-Agent").includes(GOOGLE_USER_AGENT))
{
var redirectURL = url.format({
protocol: req.protocol,
host: CUSTOM_DOMAIN,
pathname: req.originalUrl
});
res.redirect(redirectURL);
}
res.render('templateName', viewModel);
});
You can do it just using the dispatch.yaml file from App Engine, list all your domains there, but not the *.appspot.com one. Google will show a 404 route when you try to access that.
EDIT: Not possible anymore, check comments.
Checkout the official reference.

Node reverse proxy basic routes issue

I have node-reverse-proxy set up like this:
var options = {
pathnameOnly: true,
router: {
'/myapp': '127.0.0.1:9000',
}
}
httpProxy.createServer(options).listen(8000);
The webapp at the root of 9000 has an index.html file with a stylesheet link like this:
<link rel="stylesheet" href="styles/blue.css">
When I hit localhost:9000 directly the html is loaded and the css is found. Then I hit it through the reverse proxy at localhost:8000/myapp, however I get an Error 404 because localhost:9000/styles/blue.css is not found, because the file is served apparently at localhost:9000/myapp/styles/blue.css.
The html of my app of course doesn't know about the reverse proxy so I can't fix this in the index.html. So I guess I'm missing something basic about the setup of the proxy??
I don't know if you've found a workaround for this by now, but I had the exact same problem and think I've worked it out.
When you hit the target endpoint directly localhost:9000 the path is sent through as /, ie just the root. Then when it requests the css etc, it resolves those as expected, from the root.
When you call via the proxy localhost:8000/myapp it routes /myapp to the target and then passes the target service / as the path, at which point it behaves exactly as above.
The problem here is actually at the browser. Because the path is specified as /myapp, it assumes that "myapp" is a filename. Then when it requests the css etc, it peels that back to the last slash and uses that as the base for all relative hrefs, which is why it then asks for /styles/blue.css and not /myapp/styles/blue.css. If you try browsing to localhost:8000/myapp/ - note the trailing slash - then it works because the browser now treats /myapp/ as a component of the path.
Obviously, you can't dictate that users must add the trailing slash. And you can't easily do much about it in the target service, if only because it doesn't see any difference, and anyway you don't want to have to repeat the same solution in every end service. So the answer seems to be to intercept it in the proxy.
This example works, though there might be a more robust way of implementing it. It looks out for cases where the last part of the path is probably a directory component, in which case the request is redirected to force the browser to re-issue it with a trailing slash. It makes that decision based on it not looking like a filename.ext or already having a trailing slash:
httpProxy.createServer(function(req, res, proxy) {
var parsed = url.parse(req.url);
if(!parsed.pathname.match(/(\w+\.\w+|\/)$/)) {
parsed.protocol = 'http';
parsed.host = req.headers.host;
parsed.pathname += '/';
res.writeHead(301, {Location: url.format(parsed)});
res.end();
} else {
proxy.proxyRequest(req, res);
}
}, options);

Resources