tjanczuk / iisnode

Hosting node.js applications in IIS on Windows
Other
1.85k stars 587 forks source link

URL passed to node does not reflect prior IIS URL Rewrites #271

Open dpolivy opened 11 years ago

dpolivy commented 11 years ago

I have some IIS Rewrite rules that transform a URL prior to it being rewritten to my node.js app. For caching purposes, we include a cache-busting hash value (or timestamp) in our URLs, and then use IIS to remove it when serving up the file. This allows us to have far-future cache expiration on our URLs, but still rev the files without changing the filenames.

Here's an example:

  1. ORIGINAL URL: "/path//file.jpg"
  2. IIS Rewrite rule takes ORIGINAL URL and produces output of "/path/file.jpg"
  3. A second IIS Rewrite rule takes an INPUT URL of "/path/file.jpg", and rewrites that URL to point to my server.js node app.

Now, in my node app, the request URL comes in as ORIGINAL URL, aka "/path//file.jpg". What I want (expect) is that the URL would be the input URL to step #3 above (the rule that passes a URL to node, aka "/path/file.jpg").

Is it possible for iisnode to know and forward the INPUT URL instead of the ORIGINAL URL?

I can obviously work around this in node by specifying my routes for both URLs, but it would be a lot cleaner to simply leave this to IIS and have the rewritten URL passed in to node.

I do have some Failed Request Tracing logs if it helps explain the scenario.

ajholyoake commented 11 years ago

I believe I am trying to do something similar. I have a node application (a REST interface) that runs fine when run directly with node on a dedicated port. As such all of the app.get('/myapp/foo'....) commands work fine. However when I want to run this with iisnode as a subdirectory of the main site the url passed from IIS to node is not altered (i.e. the rewritten url does not get passed to node).

When running with node directly domain.com:3000/foo/bar/baz works as expected.

However when running with iisnode as a subdir domain.com/subdir/foo/bar/baz even after the rewrite rule subdir/(.*) -> server.js/{R:1} express still sees the url as /subdir/foo/bar/baz, rather than the rewritten rule /foo/bar/baz.

Is there a way for express to get the rewritten rule?

tjanczuk commented 11 years ago

Your app would probably not work as expected even if the rewritten URL was passed in, because it would include the server.js URL segment in it.

Really the only practical way of hosting node.js applications in IIS\iisnode while exposing the same URL space the app would expose as a self-hosted executable is to create a dedicated IIS Web Site for the application as opposed to hosting the application in a virtual directory.

dwmkerr commented 11 years ago

@tjanczuk Hi Thomas,

Thanks for this great library, can you tell me what the web.config would be for the arrangement you've mentioned above? I've got a separate website on my dev box with port XYZ and I want http://localhost:XYZ/my/api to pass 'my/api/' to my server.js file - I'm struggling a bit with the web.config and rewriting.

dpolivy commented 11 years ago

@dwmkerr Check out the rewrite rule example; that should get you going:

https://github.com/tjanczuk/iisnode/blob/master/src/samples/urlrewrite/web.config

If you have the IIS web site setup and bound to port XYZ, then put the web.config in your root directory for that site. Update the rewrite rule to map '/my/api/ to your main node.js file.

dwmkerr commented 11 years ago

@tjanczuk Hi Tomasz - thanks for the reply! I don't think I explained very well, basically I want to keep my express routes as they are at the moment, for example, one might be 'categories' - so I could call http://localhost:xyz/categories. Still acceptable would be to call http://localhost:xyz/api/categories, allowing me to easily write a rewrite like the below:

         <rewrite>
            <rules>
                <rule name="Api Rule">
                    <match url="api/(.*)" />
                    <action type="Rewrite" url="server.js/{R:1}" appendQueryString="false" />
                </rule>
            </rules>
        </rewrite>

The problem is at given an URL such as /api/one/two/three, even through {R:1} is one/two/three, server.js is sent /api/one/two/three - the url before rewrite.

So altogether, is there a sample web.config for passing everything to a server.js file (except for the contents of the log/debug folder) or a way to make the rewrite work taking the rewritten url? Thanks!

dpolivy commented 11 years ago

@dwmkerr Hey Dave, this is Dan, not Tomasz :) But to answer your question, the way to do what you want is just add additional express routes for the paths you want to handle, but have them all refer to the same route handler function, e.g.

function handleRoute(req, res, next) { ... }

app.get('/categories', handleRoute);
app.get('/api/categories', handleRoute);
....etc....

If you have a consistent prefix you want applied, you could build a json object of the route name and handler func, then iterate over that and register the route name, plus all prefixed variations you desire, at once.

Tomasz can confirm, but I don't believe there's any way to get at the rewritten URL from node.

Dan

dwmkerr commented 11 years ago

@dpolivy Hi Dan,

Thanks for the info! What you've suggested works great, in fact to keep things general I've got a config module, one for unix and one for windows, on windows I essentially do:

var config = {
  servicePrefix = 'api' /*this is blank on unix*/
  // snip
};

// In routes:
app.get(config.servicePrefix  + '/categories');

This seems to work nicely and lets me change the prefix across all routes or not use it all on unix if preferred.

Thanks for your help! Now it's back to #286 to try and integrate the latest version of Node Inspector!

futurechan commented 10 years ago

I'm having the same issue (#354). I am trying to nest multiple applications under a single site, but I am having trouble getting the urls to exclude the name of the child app from the url path.

ashelley commented 9 years ago

i'd like to +1 this issue even though it's old. Right now my work around is using web.config to set an environment variable so that the node app can detect its root url and use the express router to change the way it mounts the app based on the existence of this environment variable. Ideally there would be a way to send the node.js app a rewritten url as @dwmkerr proposed but alas I can't get this to work.

My use case for this is that I'm developing a node.js app for use as a normal azure website but also as a azure site extension. The site extension version has to modify the express routes to work around this issue because it has to be mounted at https://mysite.scm.azurewebsites.net/myextension and therefore needs a special web.config with custom appsettings to make it aware that it's running mounted in a subdirectory vs at the root of the website like http://mysite.azurewebsites.net to avoid 404's

futurechan commented 9 years ago

@ashelley I worked around my issue with a bit of middleware, a simple function that inspects req.url and adjusts it based upon a config value.

ashelley commented 9 years ago

@futurechan My solution was to use a main application router at the top level + environment variable set by web.config but it seems to me that it would be nice to be able to rewrite urls before they hit node although obviously this works just as good for this usecase:

https://github.com/ashelley/hellohtml/blob/e35a534a96a5436e8a71ee41cf2d48a7ad05befa/siteextension/web.config#L10-12

https://github.com/ashelley/hellohtml/blob/e35a534a96a5436e8a71ee41cf2d48a7ad05befa/app.js