2

I believe this question has been asked before, but the answer given was not enough direction to see where my code is wrong. I apologize if it's a similar question exists; I've spent a few hours searching and couldn't find a solution.

I built a small RESTful-esque service in PHP to bring over database information into my site. I'm able to make calls from the browser and Fiddler successfully, but when I try to request the data from $.ajax() I bomb out. First thing's first, let me post the PHP code:

I'm calling http://api.horodyski.me

class ApiController
{
    protected $endpoint = '';
    protected $method = '';
    protected $verb = '';
    protected $args = Array();

    public function __construct($request)
    {
        header("Access-Control-Allow-Orgin: *");
        header("Access-Control-Allow-Methods: GET");
        header("Content-Type: application/json");

        $this->args = explode('/', rtrim($request, '/'));
        $this->endpoint = array_shift($this->args);

        if (array_key_exists(0, $this->args) && !is_numeric($this->args[0])) { $this->verb = array_shift($this->args); }

        $this->method = $_SERVER['REQUEST_METHOD'];

        if($this->method != 'GET')
            throw new Exception("Unexpected Header");
        $this->request = $this->_cleanInputs($_GET);               

    }

    public function processAPI()
    {
        $class = $this->endpoint."Controller";
        if(class_exists($class))
        {
            $controller = new $class();
            return $this->_response($controller->getAllItems());
        }
        return $this->_response("No Endpoint: ".$this->endpoint, 404);
    }

    private function _response($data, $status = 200)
    {
        header("HTTP/1.1 ".$status." ".$this->_requestStatus($status));
        // $data comes back from controller in JSON format.
        return json_encode($data);
    }

    private function _cleanInputs($data)
    {
        $cleanInput = Array();
        if(is_array($data))
            foreach($data as $k => $v)
                $cleanInput[$k] = $this->_cleanInputs($v);
        else
            $cleanInput = trim(strip_tags($data));
        return $cleanInput;
    }

    private function _requestStatus($code)
    {
        $status = array(
            200 => 'OK',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            500 => 'Internal Server Error',
        );
        return ($status[$code]) ? $status[$code] : $status[500]; 
    }
} 

I've tried implementing my jQuery call returning jsonp and this is where I get stuck.

Request is made from http://horodyski.me/v2. Code below shows normal json return type:

<p class="test"></p>
    <script>
        $(document).ready(function () {
            $.ajax({
                url: "http://api.horodyski.me/skills",
                method: "GET",
                dataType: 'jsonp',
                success: function (data) {
                    $("p.test").text(data);
                },
                error: function (xhr, textStatus, error) {
                    debugger;
                }
            });
        });
    </script>

I get the following in my error callback:

textStatus = "parseerror" error = "jQuery210008879405278902885_1396911952892 was not called"

Again, Fiddler shows no error -- I'm out of ideas. Am I not handling it correctly on the PHP side, or is my jQuery messed up? Maybe I need to edit my .htaccess file to handle the callback parameter sent from jQuery on jsonp calls? I'm way over my head here, any suggestions?

I can call http://horodyski.me/api/skills and it will work with regular json, but I still can't get CORS working.

Paul
  • 26,170
  • 12
  • 85
  • 119
EHorodyski
  • 774
  • 1
  • 8
  • 30
  • 3
    To make use of `JSONP` your API needs to be able to handle it: With JSONP, the API has to return a method call with the `JSON` data as argument. The method name to call can usually be specified with a parameter like `jsonp` (implementation is up to you). An example is the SO API: http://api.stackoverflow.com/1.1/tags/ravendb/wikis?jsonp=useData (`useData` is the callback method name here) – Lukas Apr 07 '14 at 23:14
  • possible duplicate of [Make cross-domain ajax JSONP request with jQuery](http://stackoverflow.com/questions/11736431/make-cross-domain-ajax-jsonp-request-with-jquery) – Paul Apr 07 '14 at 23:23
  • 1
    Note: JSONP and CORS are separate methods. To understand why JSONP fails with a missing callback function, see the above link. But the code posted has another problem, in that the server side is set up for CORS and the client is using JSONP. Choose one and implement on both sides. I would think with CORS set up on the server, changing the client side to use CORS would be simplest. – Paul Apr 07 '14 at 23:27
  • @Paul -- How could I do that, running on an Apache server? – EHorodyski Apr 07 '14 at 23:39
  • 1
    The issue is the browser/jQuery side not the server. Here's what a jQuery AJAX request looks like for CORS: http://www.html5rocks.com/en/tutorials/cors/#toc-cors-from-jquery – Paul Apr 07 '14 at 23:44
  • @Paul -- Using the suggestion above still fails. I get a `No 'Access-Control-Allow-Origin' header is present on the requested resource.` error, even though the response and request both show it in Fiddler. For now it might be simpler to just implement the non-cross domain way, but I'm learning a lot of new stuff so I'm tempted to keep going! Thanks for all the helpful advice. – EHorodyski Apr 07 '14 at 23:54
  • 1
    That's a step forward. At least both sides of the connection are trying to do the same thing.... – Paul Apr 07 '14 at 23:58
  • Access-Control headers have finicky rules. Apache can add the header by itself as well. See http://stackoverflow.com/questions/5008944/how-to-add-an-access-control-allow-origin-header – Paul Apr 08 '14 at 00:02
  • 1
    For `Access-Control-Allow-Origin` Try sending back the origin top level URL (`http://somewhere.com` -- the hostname where the javascript files are stored) instead of "*". – Paul Apr 08 '14 at 00:06
  • 1
    The other thing you probably need to do to get it working is make sure your request will not require preflight, or ... set up preflight on the server. Better to make simple requests, though. Basically, preflight is like asking the server if it is ok to send the actual request and requires two round trips. Only certain kinds of requests are deemed "safe enough" to avoid preflight. – Paul Apr 08 '14 at 00:10
  • @Paul -- Thanks for all the great information. I'm really not in tune with doing this from the Apache side. Fortunately (and unfortunately), IIS plus SharePoint gives us an out for CORS requests baked in so there isn't much manual work we do to consume our Microsoft based web services. This is my first outside of that area and all this knowledge is great. When I have the time I'll try your suggestions. Thanks again. Very helpful stuff. – EHorodyski Apr 08 '14 at 00:25

1 Answers1

0

I would go for a CORS replacement, this normalizes most of the differences between browsers. I made best experiences with xdomain. It is library agnostic on the client side and you can use jQuery XHR or any other library without changing anything.

ranzwertig
  • 110
  • 6