I have noticed that GET requests for nonexistent paths don't return a 404 response. Instead, the client gets a "200 Ok", AngularJS renders the main view, and rewrites the path to /. A request for a nonsense URI is logged as successful in the server logs. If I understand correctly, the problem is that since AngularJS handles routing, the server has to accept a GET request for any URI and always respond by serving the client side of the app ("200 Ok" or "304 Not Modified").
For example, using the project scaffolded by the angular-fullstack Yeoman generator, requesting a nonexistent /unicorn goes like this:
GET /unicorn 200 31ms - 3.29kb
GET /partials/main 304 36ms
GET /api/awesomeThings 304 5ms
The Express route that handles the request looks like this:
// server, last route:
app.get('*', controllers.index);
// controllers:
exports.index = function(req, res) {
res.render('index');
};
and index.jade is the root of the whole client side of the app.
After a quick look at the server side code of other AngularJS / Express projects on Github (AngularJS Express seed, AngularJS login), I see that this is a common pattern. I am wondering if there is a better way to handle requests for nonexistent paths, so that the client gets a real HTTP 404 response?
The angular documentation has a section about the routing. Also, this question and this question have some information that pertains to IIS but could easily be adapted to express.
Html link rewriting
When you use HTML5 history API mode, you will need different links in different browsers, but all you have to do is specify regular URL links, such as: link
When a user clicks on this link,
In a legacy browser, the URL changes to /index.html#!/some?foo=bar
In a modern browser, the URL changes to /some?foo=bar
In cases like the following, links are not rewritten; instead, the browser will perform a full page reload to the original link.
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
When running Angular in the root of a domain, along side perhaps a normal application in the same directory, the "otherwise" route handler will try to handle all the URLs, including ones that map to static files.
To prevent this, you can set your base href for the app to <base href="."> and then prefix links to URLs that should be handled with .. Now, links to locations, which are not to be routed by Angular, are not prefixed with . and will not be intercepted by the otherwise rule in your $routeProvider.
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)
You can use $route.otherwise() function
In order to decide what to do with undefined
Routes.
If you want to still show a 404 message,
You could simply set a /404.html route both in this Function and in express.
This is actually express handling routing--not angular. Remove the app.get('*', ... that you found to disable that.
Related
I've tried other solutions (including manually setting the response) but I can't get rid of this message or redirect properly.
Explanation:
I have a hardware that comunicates with my "web api" to register new users.
My web app is running behing a NoIP so every resource that gets accessed in
http://my_public_domain:8082 is forwarded to http://my_internal_domain:80.
That being said, I have to build my routes using the overload of Url.RouteUrl() as I need full qualified routes intead of relative ones.
Url.RouteUrl("MyRoute") generates the route http://my_internal_domain/MyRoute
Url.RouteUrl("MyRoute", dataValues, Request.Url.Scheme, hostName) generates the route http://my_public_domain:8082/MyRoute (notice that I have to change the domain because of the dns)
The problem is that when I try to redirect to that route I get a page with the message Object Moved To Here and a link to that route.
Initially I thought that this problem would happend only with external redirects, but if I just do a Redirect("http://www.google.com.br") for example I don't get this page.
I've tried to manually remove the response html body as sugested in another topic (Response.Redirect results in "Object moved to here") but this do not solve the redirect problem and I only get a blank page.
I'm trying to create a web browser using Cocoa and Swift. I have an NSTextField where the user can enter the website he wants to open and a WebView where the page requested is displayed. So far, to improve the user experience, I'm checking if the website entered by the user starts with http:// and add it if it doesn't. Well, it works for most of the cases but not every time, for example when the user wants to open a local web page or something like about:blank. How can I check if adding http:// is necessary and if I should rather add https:// instead of http://?
You need to be more precise in your categorization of what the user typed in.
Here are some examples and expected reactions:
www.google.com: should be translated into http://www.google.com
ftp://www.foo.com: Should not be modified. Same goes to file:// (local)
Barrack Obama: Should probably run a search engine
about:settings: Should open an internal page
So after you figure out these rules with all their exceptions, you can use a regex to find out what should be done.
As for HTTP vs. HTTPS - if the site supports HTTPS, you'll get a redirect response (307 Internal Redirect, 301 Moved Permanently etc) if you go to the HTTP link. So for example, if you try to navigate to http://www.facebook.com, you'll receive a 307 that will redirect you to https://www.facebook.com. In other words, it's up to the site to tell the browser that it has HTTPS (unless of course you navigated to HTTPS to begin with).
A simple and fairly accurate approach would simply be to look for the presence of a different schema. If the string starts with [SomeText]: before any slashes are encountered, it is likely intended to indicate a different schema such as about:, mailto:, file: or ftp:.
If you do not see a non-http schema, try resolving the URL as an HTTP URL by prepending http://.
I have an HTML5 app written in static html/js/css (it's actually written in Dart, but compiles down to javascript). I'm serving the application files via CDN, with the REST api hosted on a separate domain. The app uses client-side routing, so as the user goes about using the app, the url might change to be something like http://www.myapp.com/categories. The problem is, if the user refreshes the page, it results in a 404.
Are there any CDN's that would allow me to create a rule that, if the user requests a page that is a valid client-side route, it would just return the (in my case) client.html page?
More detailed explanation/example
The static files for my web app are stored on S3 and served via Amazon's CloudFront CDN. There is a single HTML file that bootstraps the application, client.html. This is the default file served when visiting the domain root, so if you go to www.mysite.com the browser is actually served www.mysite.com/client.html.
The web app uses client-side routing. Once the app loads and the user starts navigating, the URL is updated. These urls don't actually exist on the CDN. For example, if the user wanted to browse widgets, she would click a button, client-side routing would display the "widgets" view, and the browser's url would update to www.mysite.com/widgets/browse. On the CDN, /widgets/browse doesn't actually exist, so if the user hits the refresh button on the browser, they get a 404.
My question is whether or not any CDNs support looking at the request URI and rewriting it. So, I could see a request for /widgets/browse and rewrite it to /client.html. That way, the application would be served instead of returning a 404.
I realize there are other solutions to this problem, namely placing a server in front of the CDN, but it's less ideal.
I do this using CloudFront, but I use my own server running Apache to accomplish this. I realize you're using a server with Amazon, but since you didn't specify that you're restricted to that, I figured I'd answer with how to accomplish what you're looking to do anyway.
It's pretty simple. Any time you query something that isn't already in the cache on CloudFront, or exists in the Cache but is expired, CloudFront goes back to your web server asking it to serve up the content. At this point, you have total control over the request. I use the mod_rewrite in Apache to capture the request, then determine what content I'm going to serve depending on the request. In fact, there isn't a single file (spare one php script) on my server, yet cloudfront believes there are thousands. Pretty sure url rewriting is standard on most web servers, I can only confirm on lighttp and apache from my own experience though.
More Info
All you're doing here is just telling your server to rewrite incoming requests in order to satisfy them. This would not be considered a proxy or anything of the sort.
The flow of content between your app and your server, with cloudfront in between is like this:
appRequest->cloudFront
if cloudFront has file, return data to user without asking your server
for the file.
If cloudFront DOESN'T have the file (or it has expired), go back to
the origin server and ask it for a new copy to cache.
So basically, what is happening in your situation is this:
A)app->ask cloudfront for url cloud front doesn't have
B)cloudfront
then asks your source server for the file
C)file doesn't exist there,
so the server tells cloudFront to go fly a kite
D)cloudFront comes back empty handed and makes your app 404
E)app crashes and
burns, users run away and use something else.
So, all you're doing with mod_rewrite is telling your server how it can re-interpret certain formatted requests and act accordingly. You could point all .jpg requests to point to singleImage.jpg, then have your app ask for:
www.mydomain.com/image3.jpg
www.mydomain.com/naughtystuff.jpg
Neither of those images even have to exist on your server. Apache would just honor the request by sending back singleImage.jpg. But as far as cloudfront or your app is concerned, those are two different files residing at two different unique places on the server.
Hope this clears it up.
http://httpd.apache.org/docs/current/mod/mod_rewrite.html
I think you are using the URL structure in a wrong way. the path which is defined by forward slashes is supposed to bring you to a specific resource, in your example client.html. However, for routing beyond that point (within that resource) you should make use of the # - as is done in many javascript frameworks. This should tell your router what the state of the resource (your html page or app) is. if there are other resources referenced, e.g. images, then you should provide different paths for them which would go through the CDN.
I'm using play's javascript routing (http://www.playframework.com/documentation/2.2.x/ScalaJavascriptRouting). I have a nginx front-end server, which I configured to force https.
When I try to make an ajax call, I get an error, since the javascript routing generates http urls and my browser blocks them because of cross origin access.
How can I configure the javascript routing to serve https urls?
How are you making the calls? The Play JavaScript router returns objects with two properties, one is a method property which contains the method, eg POST, GET etc. The other is a url property which contains the relative URL.
There are also two methods on the returned object, one called absoluteURL, the other called webSocketURL. These will return absolute URLs according the hostname of the current page, and they will be http or https (or ws or wss) based on whether the current page is secure or not.
So, if using with jQuery for example, you should be able to just:
$.ajax(jsRoutes.controllers.MyController.myAction("foo")).then(...)
And it will use the relative URL, or you can also:
var route = jsRoutes.controllers.MyController.myAction("foo")
$.ajax({
method: route.method,
url: route.url,
...
}).then(...)
If you're trying to make that requests using absolute urls, Hostnames, protocols and ports should match because of same-origin policy. You have at least two options:
Use relative urls on ajax requests like href="/your/page"
Use the protocol relative url's. Example: href="//your.domain.com/your/page.
Here is a detailed article by Paul Irish about protocol relative url's:
http://www.paulirish.com/2010/the-protocol-relative-url/
You have to pass it as a parameter to absoluteURL/websocketURL.
Frome Chrome dev tools:
> jsRoutes.controllers.CalendarController.getEvents().absoluteURL
function (s){return _s('http',s)+'localhost:9000'+r.url}
> jsRoutes.controllers.CalendarController.getEvents().absoluteURL()
"http://localhost:9000/events"
> jsRoutes.controllers.CalendarController.getEvents().absoluteURL(true)
"https://localhost:9000/events"
> jsRoutes.controllers.CalendarController.getEvents().webSocketURL
function (s){return _s('ws',s)+'localhost:9000'+r.url}
> jsRoutes.controllers.CalendarController.getEvents().webSocketURL()
"ws://localhost:9000/events"
> jsRoutes.controllers.CalendarController.getEvents().webSocketURL(true)
"wss://localhost:9000/events"
_s is defined at https://github.com/playframework/playframework/blob/master/framework/src/play/src/main/scala/play/api/routing/JavascriptReverseRouter.scala#L54 as
function(p,s){return p+((s===true||(s&&s.secure))?'s':'')+'://'}
We have a service located at a url like services.example.com/123456/*. We'd like to provide that same service at a url like www.example.com. The original service is provided by a PaaS provider which doesn't work with custom domains.
We want a request to go something like this: browser requests www.example.com/path, we tell it to go to services.example.com/123456/path, and it gets the data from there, but treats the response as if it came from www.example.com/path - so doesn't update document.location or the url at the top, and treats links as relative to the original path.
Is this possible, or would we have to have a own proxy-like website?
You need a proxy that do url rewriting.