Cross Origin $http Requests | Resource - php

So I was writing an app when I got across this issue.
This is the PHP : Slimframework Corresponding :
$app->delete('/products/:id',function($id) use($app){
$db = new mysqli('notsocoolhost','verycooluser','verycoolpassword','verycooldatabase');
$db->query("DELETE from products WHERE id='$id'");
});
I removed the part where I confirm that you can actually delete it from the database.
This is Angular.JS :
$scope.del = function(product){
$http({
method: "DELETE",
url: baseUrl + product.id
}).success(function(){ ...... //Returns 0 -> WTF?
This buddy here returns in error status : 0
and this one below returns 405:
$scope.delete(baseUrl + product.id).success ... //Returns 405 : Method Not Allowed
To sum it up, I added couple of tests on Hurl.it and the RESTApi from Slimframework is fully functioning. which leaves it as Angular.js problem ? I guess?
UPDATE:
After further inspection I've revealed the following:
1) Mysteriously the : Request Method (Field by Firefox) is OPTIONS.
2) Access-Control-Request-Method : "DELETE"
3) Access-Control-Allow-Methods: "GET,POST,DELETE,PUT"

I hope this serves people in the future.
Back to basics, having trouble sending $http requests in cross-origin requests has nothing to do with the server nor Angular.js.
If you are like me hosting your webapp on:
https:\\www.beautifuldomain.com
and your API on :
https:\\api.beautifuldomain.com
Whenever you try to perform a request between Webapp and API you are performing Cross-Origin Request.
What does it mean?
It means that your message will be considered as Cross-Origin and it will be preflighted.
Preflighted?
It means that when you use any method other than GET,HEAD or POST.
Also POST if used to send request data with Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g.
It will be sent as method: OPTIONS. -- That is preflighted.
OK, OK I understand, but what do i do?
Now that is clear we have two options to move on:
First Option:
Leaving the web-server structure as is i.e:
www.example.com -> Angular Web-App
api.example.com -> API - subdomain
FOR POST:
And add a transformRequest setting to $httpProvider like so:
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
(Remember preflighted, well it does allow us to send x-www-form-urlencoded.)
What is left from there is make sure you set your data in x-www-form-urlencoded format looks like so :
name=Andy&nickname=RainbowWarrior&....
FOR DELETE:
This one is a bit more complicated since you have to do some server side tweak.
If you are using Slimframework for PHP like I do, all you got to do is:
$response = $app->response();
$response->header('Access-Control-Allow-Origin', '*');
$app->options('/path/to/resource',function(){}); // This one just so you can accept OPTIONS it does nothing.
$app->delete('/path/to/resource',function()
{//your delete code is here
});
Now whenever you try to perform DELETE from angular you will see on XHR tab in w/e browser you are using that There is OPTIONS request that was made and right after DELETE.
Second Option:
Much less of a headache .
Move your API into the same domain i.e
www.example.com - Webapp
www.example.com/api - API
And you are protected from all of that above.
This took me 7 hours of research I hope it will help you guys and save you time!.

Related

Confusion in Angular 2 Http Post Request

First of all. Please bear with my questions.
What I am doing is just performing an ajax request which will return a response data of string.
Here's my php
<?php
header('Access-Control-Allow-Origin: *');
echo 'Peenoise Crazy';
?>
Http request
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
return this.http.post('./app/parsers/peenoise.php', '', options)
.then(val => console.log(val))
This doesn't work. But if I change the url into this http://localhost:8912/app/parsers/peenoise.php. It works.
Questions
Why ./app/parsers/peenoise.php doesn't work?
If I change the url to http://localhost:8912/app/parsers/peenoise.php. Why do I need to put header('Access-Control-Allow-Origin: *')? It is on the same folder of my app.
How to use http.post without passing an url of http://localhost:8912/route/to/phpfolder.php?
Any help would be appreciated. Please guide me to this. Thanks
Post requires a full or a relative URL to work. You can use:
this.http.post('/parsers/peenoise.php', '', options)
Actually Angular looks for a backend server to post the data, so Imagine in your post request is like below
this.http.post('./app/parsers/peenoise.php', '', options).then(val => console.log(val))
It will try to make the post request at the location
imagine you are serving angular on localhost:4200
http://localhost:4200/app/parsers/peenoise.php
Now to answer your Questions :
1)./app/parsers/peenoise.php doesn't work because it's actually trying to find it from your backend service
2)You need to put header('Access-Control-Allow-Origin: *') whenever you are making an http request to a different server than from the one which your site is currently using, if you don't use that you'll end up with Connection_refused_Error.
3)If you want to use just angular then there are mockAckends, in-memory-web-api
An example using mockBackEnd is
here

SessionID changes with each HTTP request

I've just started using the fetch API to send my data as ReactJS recommends. Prior to this, to send API requests to my server I used AngularJS. I am currently encountering the most annoying bug, sending HTTP requests fails to load the correct session.
Firstly, my web application uses a PHP API to expose itself and is (basically for now) a bunch of .php files to get parts of the app. One of them is authenticateUser.php which authenticates and stores the session. However, I noticed that my HTTP requests with fetch weren't working as expected as other stuff like getCurrentUser.php returned NotLoggedIn, despite just authenticating. I thought it might have something to do with the sessions, so on authenticateUser.php, I set my .php file to output the current session_id after authenticating via.: (echo array('session_id' => session_id());
I have noticed that basically, when I myself load the url myself in the browser: myapp.com/php/http/user/authenticateUser.php, the session_id I get is completely different from the one I get when using fetch:
return fetch("/php/http/user/authenticateUser.php", {
method: "post",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(successResponse) {
return successResponse.json();
}, function(errorResponse) {
console.log(errorResponse);
}).then(function(data) {
console.log(data);
});
I checked further and thought maybe it was because fetch was querying http version of my site instead of the non-HTTP version of my site. So I changed the logic on authenticateUser.php such that if it detects a session existing, it prints out that session_id, else it creates and prints out the new one. Everytime I sent my HTTP request with fetch I got a different session_id meaning that my session_id was changing with each request. Here is the code I used:
header("Content-Type: application/json");
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
http_response_code(200);
echo json_encode(array('session_id' => session_id()),JSON_PRETTY_PRINT);
Am I missing something completely different with fetch API, or headers? Or does anyone have any experience with this that could possibly enlighten me?
The answer is that fetch by default does not send cookies, you need to specify it via:
credentials: same-origin

jquery fileupload cross domain

I have a small problem i need to create a fileuploader to a remote server using jquery Blueimp Fileupload, if i work locally for testing it is working perfectly now when I tested it on live, Im having a problem with cross origin resource sharing.
Now, how can I retrieve the json response from another domain without using jsonp because I tried jsonp and it does not work with the fileuploader so now I want to do it using json alone and get the response that i need if thats possible
I also tried putting callback=? at the end of url .. also did not work
Or if its possible how can I integrate jsonp with this fileuploader
$( '#fileuploader' ).fileupload( {
sequentialUploads: true,
url: 'http://www.domain.com/test/upload?callback=?',
dropZone: $( '#fileuploader' )
} );
Server Side this is on another domain
echo json_encode( array( 'test' => 'value1') );
Also: i am not allowed to use ftp / curl for this.. thanks
you can allow CORS request at server as:
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
When CORS is enabled at server, Ajax first send OPTIONS request to detect whether server allow CORS request or not. if enabled, it send actual request.
If you have allowed the CORS policy on the remote server as suggest above and you still get the Cross Origin error it could be that there is something else not working in your code. Many times Firebug or similar tools show a Cross Origin error and in reality it was a 404 or something else. First question to answer is if you actually at a CORS pre-flight request/response. That's your permission ticket. Check out these posts here here and here
You might consider using the iframe transport option. This will let you keep away from issues with browser that doesn't support cross-domain file uploads, like our old (but still widely used) friend IE 9 or previous versions.
Hope this helps.

REST PUT request returning a GET 500 error, is that plausible?

I am not any kind of RESTful API expert, but I have a simple PUT/DELETE function in an AngularJS app that has been functioning as expected until now. I am trying to work out whether this problem is likely to lie in my app, or in the (php) back-end that is running the endpoint. Other REST services are functioning normally & the server appears to be running fine.
This function only ever calls PUT or DELETE, assigned as var method:
if (food.favourite === true) {
method = "PUT";
console.log("method is " + method)
} else if (food.favourite === false) {
method = "DELETE";
console.log("method is " + method)
}
$http({
method: method,
url: $scope.URL
}).success(function (data, status, headers, config) {
console.log(method + " successful")
}).error(function (data, status, headers, config) {
console.log(method + " not successful")
});
I have one $http GET in my app that uses a different endpoint. There is no $http GET pointing to this endpoint anywhere in my app- I have searched extensively.
When I trigger the function containing the $http above, the console shows:
method is PUT
GET http://localhost:8888/api/ext/51/ 500 (Internal Server Error)
PUT not successful
Why would I be receiving a GET error on an unsuccessful PUT request? Does this point to a problem in my function, or a problem with the endpoint?
Thank you for any help in understanding this problem.
Update 1
Info from the Network panel: calling the $http function above triggers two simultaneous requests, one 'PUT' and one 'GET'. The 'PUT' returns a 301 code, and the 'GET' returns a 500 server error (which I think is to be expected, as this endpoint is not set up to respond to 'GET', only to 'PUT' and 'DELETE').
So: why would my code be generating two simultaneous requests with different methods?
For future seekers of answers to similar questions: it is a standard behaviour (of REST in general or this implentation? Not sure) to try to return a GET for every action that is called. Evidence for this is that if I check the Network panel for all the other (successful) $http functions, they also have two actions visible: the original PUT/GET/DELETE etc, + a GET.
We are seeing a 500 Error on the GET for these particular requests because the configuration of this particular endpoint does not allow for a GET. This should not have any effect on the PUT/DELETE actions on this endpoint- the 500 Error is not related to the reason why the PUT/DELETE actions weren't working. In terms of trying to solve this specific problem, it's a red herring.
The reason the PUT/DELETE was not working was because the service was broken on the server-side.
I ran into this and the issue was in how I was outputting the errors in the first argument for header. They need to be in this format:
header('400: Error', true, 400);
or in your case, of course:
header('301: Moved Permanently', true, 301);
header('Location: ' . $url);
Note that this WILL NOT work:
header('Some Random Text', true, 400); // $http.error shows status of 500

Ajax GET request turning into OPTION request

I'm experiencing a weird behavior with an ajax request on a godaddy shared linux server.
The request works perfectly on many other servers I've tested it on, but on this one, the GET request turns into an OPTIONS request for some reason.
Here's the js code (using mootools 1.1):
var a = new Ajax(myurl,{
method: 'get',
onComplete: function( response ){
$('my_div').style.display="none";
output_display( response );
}
});
a.request();
You can see that the method is defined as GET. Yet when I watch the request happen with Firebug, it gets passed as an OPTIONS request. Any thoughts on how or why this would happen?
usually, there are two reasons for this sort of behaviour during XHR (ajax) requests.
protocol bridging (from https to http or vice versa) whereby request url protocol differs to requested url
subdomain difference (eg, domain.com requests from www.domain.com)
bottom line: for XHR to work, protocol and hostnames need to match due to access control restrictions.
reads:
http://www.w3.org/TR/access-control/#cross-origin-request-with-preflight0
ways around cross-domain policy restrictions:
http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
etc etc.

Categories