0

Update 6 July: Thanks to @gettleto I've managed to find the response header options and OPTIONS route that works without errors, but I kind of hacked my way around and frankly don't understand which response headers should be listed, what might be missing, if any are extraneous or if the API is vulnerable.

If there is a 'best practices' or a guide anyone can refer me to, that would be terrific. I've spent a few hours trying to find a guide for the uninitiated, but no love.

Update 27 June: I discovered that Backbone sends a preflight request to the server before the POST, which led me to this SO answer. I added the OPTIONS route to the API (see code below), and get XMLHttpRequest cannot load http://foxworkx.dev/bookAPI.php/books. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8081' is therefore not allowed access. in the console, BUT the API allowed the POST to work!?

Both the OPTIONS and POST requests are getting a status:200 OK.

So, It seems odd that I set CorsOptions and add to $app, but need an OPTIONS route with defined response headers.

Any help appreciated. /Update 27 June

I am working thru a Backbone tutorial, and built a simple MySQL/PHP API using Slim3 and CorsSlim3. The GET is working with no errors. The weird part is the POST is INSERTing into MySQL, so I'm thinking it's a callback issue, but I have no idea how to fix it.

Any help greatly appreciated.

My PHP API code

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require 'vendor/autoload.php';

$app = new \Slim\App;

// CORS
$corsOptions = array(
    "origin" => array( "http://127.0.0.1:8081" ),

    "allowMethods" => array('GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'),
    "exposeHeaders" => array( "X-Requested-With", "Content-Type", "Accept", "Origin", "Authorization" ),
    "allowHeaders" => array( "X-Requested-With", "Content-Type", "Accept", "Origin", "Authorization" )
);
$cors = new \CorsSlim\CorsSlim( $corsOptions );
$app->add( $cors );
// END CORS

/* 
ROUTES 
*/
// GET all
$app->get( '/books', function ( Request $request, Response $response ) {
    $sql = "select * FROM bookDB ORDER BY title";
    try {
        $db = getConnection();
        $stmt = $db->query($sql);  
        $books = $stmt->fetchAll(PDO::FETCH_OBJ);
        $db = null;
        echo json_encode($books);
    } catch( PDOException $e ) {
        echo '{"error":{"text":'. $e->getMessage() .'}}'; 
    }
} );
// OPTIONS ( preflighted requests )------NEW
$app->options( '/books',
    function( Request $request, Response $response, $args ) {
        return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8081')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    }
);
// ADD one 
$app->post('/books', function ( Request $request, Response $response ) {
    $data = $request->getParsedBody();
    $book = [];
    $book[ 'title' ] = filter_var( $data[ 'title' ] , FILTER_SANITIZE_STRING );
    $book[ 'author' ] = filter_var( $data[ 'author' ] , FILTER_SANITIZE_STRING );
    $book[ 'releaseDate' ] = filter_var( $data[ 'releaseDate' ] , FILTER_SANITIZE_STRING );
    $book[ 'keywords' ] = filter_var( $data[ 'keywords' ] , FILTER_SANITIZE_STRING );

    $sql = "INSERT INTO bookDB ( title,author,releaseDate,keywords ) VALUES ( '" . $book[ 'title' ]  . "', '" . $book[ 'author' ]  . "', '" . $book[ 'releaseDate' ] . "', '" . $book[ 'keywords' ] . "' )";
    try {
        $db = getConnection();
        $stmt = $db->prepare($sql);  
        $stmt->execute();
    //  $book[ 'id' ] = $db->lastInsertId();
    //  error_log("book id:", $book[ 'id' ] );
        $db = null;
    //  echo json_encode($book ); 



    } catch(PDOException $e) {
        error_log( $e->getMessage() );
        echo '{"error":{"text":'. $e->getMessage() .'}}'; 
    }

    // return $response
    //  ->withJson($book)
    //  ->withJson( array( error => array( "text" => $e->getMessage() ) ) );



} );

And my Backbone.view

        var app = app || {};

app.LibraryView = Backbone.View.extend({

    events: {
        'click #add' : 'addBook'
    },

    el: '#books',

    initialize: function() {

        this.collection = new app.Library();
        this.collection.fetch( { reset:true } );
        this.render();

        this.listenTo( this.collection, 'add', this.renderBook );
        this.listenTo( this.collection, 'reset', this.render );
    },

    // render library by rendering each book in its collection
    render: function() {
        this.collection.each(function( item ) {
            this.renderBook( item );
        }, 
        this );
    },

    // render a book by creating a BookView and appending the
    // element it renders to the library's element
    renderBook: function( item ) {
        var bookView = new app.BookView({
            model: item
        });
        this.$el.append( bookView.render().el );
    },

    addBook: function( e ) {

        e.preventDefault();

        var formData = {};

        $( '#addBook div' ).children( 'input' ).each( function( i, el ) {
            if( $( el ).val() != '' ) {
                formData[ el.id ] = $( el ).val();
            }
            // Clear input field value
                $( el ).val('');
        } );
        // add to this.collection AND POST the data
        this.collection.create( formData );

    }

});

And my PHP error_log:

[24-Jun-2016 09:59:15 America/Los_Angeles] PHP Deprecated:  Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in Unknown on line 0
[24-Jun-2016 09:59:15 America/Los_Angeles] PHP Warning:  Cannot modify header information - headers already sent in Unknown on line 0
Community
  • 1
  • 1
royhink
  • 49
  • 1
  • 9

1 Answers1

0

Can you retry this with returning the response objects from your route actions...

return $response->withJson($book);
return $response->withJson(array(error => array("text" => $e->getMessage())));

It would be helpful if you added more of your bootstrapping code on the Server side...

Edit You need the headers on both requests [options and actual resource]

geggleto
  • 2,605
  • 1
  • 15
  • 18
  • @gettleto: I'm not getting anything back using this code. – royhink Jun 27 '16 at 16:39
  • Oh I am sorry. You need to attach the OPTIONS headers to both the OPTIONS Request and the actual resource! – geggleto Jun 27 '16 at 18:38
  • Yes, but I thought `$cors = new \CorsSlim\CorsSlim( $corsOptions ); $app->add( $cors );` was attaching it to the $app instance. No? – royhink Jun 27 '16 at 19:54
  • hmm... yes it should be adding the cors middleware to the entire app... I am not very familar with CorsSlim, I'm just a Slim core developer... – geggleto Jun 27 '16 at 20:14
  • Check out this thread, someone had a similar issue. http://stackoverflow.com/questions/38005829/cors-post-request-fails/38035128#38035128 – geggleto Jun 27 '16 at 20:15