Custom 404 error without server - html

Is possible to intercept 404 error without using web server (browsing html file in the filesystem) ?
I tried with some javascript, using an hidden iframe that preload the destination page and check for the result and then trigger a custom error or redirect to the correct page.
This work fine but is not good on perfomance.

A 404 error is an HTTP status response. So unless you are trying to retrieve this file using an HTTP request/response, you can't have a genuine 404 error. You can only mimic one in something like the way you suggest. Any "standard" way of handling a 404 error is dependent on your flavour of web server anyway...

404 is a HTTP response code, and as such only delivered through the HTTP protocol by servers that speak it. The file:// extension isn't a real protocol response as such, it's a hack built into clients (like browsers) that enable local file support, however it's up to browsers / clients themselves whether they expose any response codes from their file:// implementation. In theory they could report them in the DOM, for example, but they would be response codes exposed to themselves, and as such rarely implemented. Most don't, and there isn't a standard way for it. You may look into browser extensions, like Firefox, and see if they support it, but then, this is highly unstandard and will likely break if you pop it on the web.

Why don't you want to use the server?
I don't believe that it's possible to handle a 404 error client-side, because a 404 error is server-side.
Whenever you load a webpage, you make a request to the server. Thus, when you ask for a file that's not there, it's the server that handles the error. Regular HTML/CSS/JavaScript only come into the picture when the server sends back a response to tell you that it can't find the file.
Steve

Because I was looking for this today. You can now do this without a server by using a Service Worker to cache the custom 404 page, and then serve it when a fetch request status is 404. Following the instructions on the google cache lab, the worker files looks as follows:
const filesToCache = [
'/',
'404.html'
];
const staticCacheName = 'pages-cache-v1';
self.addEventListener('install', event => {
console.log('Attempting to install service worker and cache static assets');
event.waitUntil(
caches.open(staticCacheName).then(cache => {
return cache.addAll(filesToCache);
});
);
});
self.addEventListener('fetch', event => {
console.log('Fetch event for ', event.request.url);
event.respondWith(
caches.match(event.request).then(response => {
if (response) {
console.log('Found ', event.request.url, ' in cache');
return response;
}
console.log('Network request for ', event.request.url);
return fetch(event.request).then(response => {
console.log('response.status:', response.status);
// fetch request returned 404, serve custom 404 page
if (response.status === 404) {
return caches.match('404.html');
}
});
});
);
});

Related

Service Worker not caching API content on first load

I've created a service worker enabled application that is intended to cache the response from an AJAX call so it's viewable offline. The issue I'm running into is that the service worker caches the page, but not the AJAX response the first time it's loaded.
If you visit http://ivesjames.github.io/pwa and switch to airplane mode after the SW toast it shows no API content. If you go back online and load the page and do it again it will load the API content offline on the second load.
This is what I'm using to cache the API response (Taken via the Polymer docs):
(function(global) {
global.untappdFetchHandler = function(request) {
// Attempt to fetch(request). This will always make a network request, and will include the
// full request URL, including the search parameters.
return global.fetch(request).then(function(response) {
if (response.ok) {
// If we got back a successful response, great!
return global.caches.open(global.toolbox.options.cacheName).then(function(cache) {
// First, store the response in the cache, stripping away the search parameters to
// normalize the URL key.
return cache.put(stripSearchParameters(request.url), response.clone()).then(function() {
// Once that entry is written to the cache, return the response to the controlled page.
return response;
});
});
}
// If we got back an error response, raise a new Error, which will trigger the catch().
throw new Error('A response with an error status code was returned.');
}).catch(function(error) {
// This code is executed when there's either a network error or a response with an error
// status code was returned.
return global.caches.open(global.toolbox.options.cacheName).then(function(cache) {
// Normalize the request URL by stripping the search parameters, and then return a
// previously cached response as a fallback.
return cache.match(stripSearchParameters(request.url));
});
});
}
})(self);
And then I define the handler in the sw-import:
<platinum-sw-import-script href="scripts/untappd-fetch-handler.js">
<platinum-sw-fetch handler="untappdFetchHandler"
path="/v4/user/checkins/jimouk?client_id=(apikey)&client_secret=(clientsecret)"
origin="https://api.untappd.com">
</platinum-sw-fetch>
<paper-toast id="caching-complete"
duration="6000"
text="Caching complete! This app will work offline.">
</paper-toast>
<platinum-sw-register auto-register
clients-claim
skip-waiting
base-uri="bower_components/platinum-sw/bootstrap"
on-service-worker-installed="displayInstalledToast">
<platinum-sw-cache default-cache-strategy="fastest"
cache-config-file="cache-config.json">
</platinum-sw-cache>
</platinum-sw-register>
Is there somewhere I'm going wrong? I'm not quite sure why it works on load #2 instead of load #1.
Any help would be appreciated.
While the skip-waiting + clients-claim attributes should cause your service worker to take control as soon as possible, it's still an asynchronous process that might not kick in until after your AJAX request is made. If you want to guarantee that the service worker will be in control of the page, then you'd need to either delay your AJAX request until the service worker has taken control (following, e.g., this technique), or alternatively, you can use the reload-on-install attribute.
Equally important, though, make sure that your <platinum-sw-import-script> and <platinum-sw-fetch> elements are children of your <platinum-sw-register> element, or else they won't have the intended effect. This is called out in the documentation, but unfortunately it's just a silent failure at runtime.

How get data from rest api URL in angularJs? While i'm try to get its show me as 0kb file

function Hello($scope, $http) {
$http.get('http://localhost/api/Country')
.success(function(data, status) {
$scope.greeting = data;
}).error(function(data, status){
alert('Error');
});
}
URL: Try pull the data from url, it shown me as a 0kb file. when i click that URL directly in shown some data.
Tried on my application before you replace the url by localhost (I guess changed for security reason), seems to come from a wrong configuration on server side, not angular.
Firefox trigger an error saying :
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/api/Country. This can be fixed by moving the resource to the same domain or enabling CORS.
If you are in charge of this server, you should give a look at Cross-Origin Request, but your angular code is correct, sorry :D

Dojo 1.9 JsonRest remote url on a different server

I'm trying to get dojo to show Json data that comes from a remote web service. I need to be clear though - the web server hosting the html/dojo page I access isn't the same server as the one that's running the web service that returns the json data - the web service server just can't serve html pages reliably (don't ask!!).
As a test I move the page into the same web server as the web service and the below works. As soon as I move it back so that the html/dojo is served from Apache (//myhost.nodomain:82 say) and the web service sending the json is "{target:http://myhost.nodomain:8181}", then it stops working.
I've used FFox to look at the network & I see the web service being called ok, the json data is returned too & looks correct (I know it is from the previous test), but the fields are no longer set. I've tried this with DataGrid and the plain page below with the same effects.
Am I tripping up over something obvious???
Thanks
require([
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dojox/grid/DataGrid",
"dojo/data/ObjectStore",
"dojo/query",
"dojo/domReady!"
],
function(JsonRest, Memory, Cache, DataGrid, ObjectStore, query) {
var myStore, dataStore, grid;
myStore = JsonRest(
{
target: "http://localhost:8181/ws/job/definition/",
idProperty: "JOB_NAME"
}
);
myStore.query("JOB00001"
).then(function(results) {
var theJobDef = results[0];
dojo.byId("JOB_NAME").innerHTML = theJobDef.JOB_NAME;
dojo.byId("SCHEDULED_DAYS").innerHTML = theJobDef.SCHEDULED_DAYS;
});
}
);
Its true what Frans said about the cross domain restriction but dojo has this link to work around the problem.
require(["dojo/request/iframe"], function(iframe){
iframe("something.xml", {
handleAs: "json"
}).then(function(xmldoc){
// Do something with the XML document
}, function(err){
// Handle the error condition
});
// Progress events are not supported using the iframe provider
});
you can simply use this and the returned data can be inserted into a store and then into the grid.
Are you familiar with the Same Origin Policy:
http://en.wikipedia.org/wiki/Same-origin_policy
Basically it restricts websites to do AJAX requests to other domains than the html page was loaded from. Common solutions to overcome this are CORS and JSON-P. However, remember that these restrictions are made for security reasons.

getJson Access-Control-Allow-Origin

I just learned that using getJson with a url from another domain/port will usually lead to cross domain policy problem.
With this code:
var appGetApi = "http://localhost:30028/api/values";
$.getJSON(appGetApi, function (_returnedJson) {
...
});
I get this error:
XMLHttpRequest cannot load http://localhost:30028/api/values.
Origin http://localhost:17437 is not allowed by Access-Control-Allow-Origin.
After searching the web for answers, it seems that adding &callback=? is a famous fix. So i did that.
var appGetApi = "http://localhost:30028/api/values&callback=?";
$.getJSON(appGetApi, function (_returnedJson) {
...
});
But I still get an error:
Failed to load resource: the server responded with a status of 400 (Bad Request)
http://localhost:30028/api/values&callback=jQuery11020629610788077116_1373178114158?_=1373178114159
This is my first time with API and I am completely clueless right now on how to solve this issue. Please help me guys. Thanks.
By default, IIS in W2K3 and above won't serve files that aren't of a MIME type that it knows about (instead returning 404 errors).
You need to add a MIME type to IIS to allow it to serve that type of file. You can set it at the site level or at the server level.
To set this for the entire server:
Open the properties for the server in IIS Manager and click MIME Types
Click "New". Enter "JSON" for the extension and "application/json" for the MIME type.

Angularjs issue $http.get not working

I am a novice to Angularjs and tried to follow example given for $http.get on angularjs website documentation.
I have a REST service, which when invoked returns data as follows:
http://abc.com:8080/Files/REST/v1/list?&filter=FILE
{
"files": [
{
"filename": "a.json",
"type": "json",
"uploaded_ts": "20130321"
},
{
"filename": "b.xml",
"type": "xml",
"uploaded_ts": "20130321"
}
],
"num_files": 2}
Part of the contents of my index.html file looks like as follows:
<div class="span6" ng-controller="FetchCtrl">
<form class="form-horizontal">
<button class="btn btn-success btn-large" ng-click="fetch()">Search</button>
</form>
<h2>File Names</h2>
<pre>http status code: {{status}}</pre>
<div ng-repeat="file in data.files">
<pre>Filename: {{file.filename}}</pre>
</div>
And my js file looks as follows:
function FetchCtrl($scope, $http, $templateCache) {
$scope.method = 'GET'; $scope.url = 'http://abc.com:8080/Files/REST/v1/list?&filter=FILE';
$scope.fetch = function() {
$scope.code = null;
$scope.response = null;
$http({method: $scope.method, url: $scope.url, cache: $templateCache}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}
But when I run this, I do not see any result for filenames and I see http status code = 0
When I run , http://abc.com:8080/Files/REST/v1/list?&filter=FILE in browser, I still can see desired results (as mentioned above)
I even tried to debug using Firebug in firefox, I see the above URL gets invoked when I hit "Search" button but response looks to be empty. And interestingly in Firebug under URL, it shows
OPTIONS "Above URL"
instead of
GET "Above URL"
Can you please let me know, what I am doing wrong and why I am not able to access JSON data ?
Thanks,
This is because how angular treats CORS requests (Cross-site HTTP requests). Angular adds some extra HTTP headers by default which is why your are seeing OPTIONS request instead of GET. Try removing X-Requested-With HTTP header by adding this line of code:
delete $http.defaults.headers.common['X-Requested-With'];
Regarding CORS, following is mentioned on Mozilla Developer Network:
The Cross-Origin Resource Sharing standard works by adding new HTTP
headers that allow servers to describe the set of origins that are
permitted to read that information using a web browser.
I have been having the issue using $resource, which also uses $http.
I noticed that when I used AngularJS 1.0.6 the request would not even show up in Firebug, but when using AngularJS 1.1.4 Firebug would show the GET request and the 200 OK response as well as the correct headers, but an empty response. In fact, the headers also showed that the data was coming back as shown by the "Content-Length" header having the correct content length, and comparing this against a REST Client plugin I was using that was successfully retrieving the data.
After being even further suspicious I decided to try a different browser. I had originally been using Firefox 16.0.1 (and also tried 20.0.1), but when I tried IE 9 (and AngularJS 1.1.4) the code worked properly with no issues at all.
Hopefully this will help you find a workaround. In my case, I noticed that I never had this problem with relative URLs, so I'm changing my app around so that both the app and the API are being served on the same port. This could potentially be an AngularJS bug.
I had the same problem today with firefox. IE worked fine. I didn't think it was cors at first because like you I got no errors in the console and got a status of 0 back in my error method in angular. In the firefox console I was getting a 200 response back in my headers and a content length, but no actual response message. Firefox used to give you a warning about cross site scripting that would point you in the right direction.
I resolved the issue by setting up cors on my api. This is really the best way to go.
If you are only using GET with your api you could also try using jsonp this is built right into angular and it is a work around for cors when you do not control the api you are consuming.
$http.jsonp('http://yourapi.com/someurl')
.success(function (data, status, headers, config) {
alert("Hooray!");
})
.error(function (data, status, headers, config) {
alert("Dang It!");
});
It's cross-site-scripting protection.
Try starting google chrome with --disbable-web-security (via command line).
If that isn't working also try to put your angular stuff into an http server instead of using the file protocol. (Tip: use chrome canary if you want to have a browser dedicated to --disable-web-security - of course you'll have to set the command line argument too, but both chrome versions run simultaneously). For release you'll have to set some http headers on the server providing the AngularJS-stuff to allow access to the twitter api or whatever you want to call.