Laravel 5 + AngularJS Cross Domain CORS

2019-02-07 11:12发布

I have searched everywhere for an answer but nothing have worked so far. All the listed solutions on stack have not proven to be sufficient.

I get nothing in my laravel log in form of errors and I only get the standard:

XMLHttpRequest cannot load http://api.domain.dev/post/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://domain.dev' is therefore not allowed access.

Laravel controller:

<?php namespace App\Http\Controllers;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Post;
use App\Tag;
use Illuminate\Http\Request;

class PostController extends Controller {

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {

        $posts = Post::with('user', 'tags')->get();

        return response()->json($posts);
    }
}

Laravel Routes:

<?php

Route::resource('user', 'UserController');
Route::resource('post', 'PostController');
Route::get('post/tag/{tag}', 'PostController@postsWithTag');
Route::resource('tag', 'TagController');

Route::controllers([
    'auth' => 'Auth\AuthController',
    'password' => 'Auth\PasswordController',
]);

Kind of bloated not organized angular:

//App
var app = angular.module('app', [
    'ngRoute',
    'ngAnimate'
    ]);

//Config
app.config(['$routeProvider', '$locationProvider', '$animateProvider', function($routeProvider, $locationProvider, $animateProvider) {

    $locationProvider.html5Mode(true).hashPrefix('!');

    $routeProvider.
    when('/', {
        templateUrl: 'partials/home.html',
        controller: 'PageController'
    }).
    when('/about', {
        templateUrl: 'partials/about.html',
        controller: 'AboutController'
    }).
    when('/contact', {
        templateUrl: 'partials/contact.html',
        controller: 'ContactController'
    }).
    when('/blog', {
        templateUrl: 'partials/blog.html',
        controller: 'PostsController'
    }).
    when('/blog/post/:postId', {
        templateUrl: 'partials/post.html',
        controller: 'PostController'
    }).
    otherwise({
        redirectTo: '/'
    });


}]);

//Factory
app.factory('Data', function Data($http) {

    return {
        getPosts: function getPosts() { return $http.get('http://api.domain.dev/post/'); },
        getPost: function getPost(id) { return $http.get('http://api.domain.dev/post/' + id); },
        addPost: function addPost(data) { return $http.post('http://api.domain.dev/post/', data); },
        removePost: function removePost(id) { return $http.delete('http://api.domain.dev/post/'+ id); },

        getTags: function getTags() { return $http.get('http://api.domain.dev/tag/'); },
        getTag: function getTag(id) { return $http.get('http://api.domain.dev/tag/' + id); },
        addTag: function addTag(data) { return $http.post('http://api.domain.dev/tag/', data); },
        removeTag: function removeTag(id) { return $http.delete('http://api.domain.dev/tag/'+ id); },

    } 
}); 

//Posts Controller
app.controller('PostsController', function PostsController($scope, Data) {



    Data.getPosts().success(parsePosts);

    function parsePosts(data) { 
        $scope.posts = data; 
    }

    //AddPost
    $scope.newPost = { title: '', content: '', resume: '' };

    $scope.addPost = function addPost(){Data.addPost({ title: $scope.newPost.title, content: $scope.newPost.content, resume: $scope.newPost.resume, user_id: $scope.newPost.user_id }).success(postAddSuccess).error(postAddError);}

    function postAddSuccess(data) {
        $scope.error = null;
        $scope.posts.push(data);
        $scope.newPost = { title: '', content: '', resume: '' }; 
    }

    function postAddError(data) { 
        $scope.error = data; 
    }

    //RemovePost
    $scope.removePost = function removePost(id) {
        if (confirm('Do you really want to remove this post?')) {
            Data.removePost(id).success(postRemoveSuccess); 
        } 
    }

    function postRemoveSuccess(data) {
        var i = $scope.posts.length;
        while (i--) {
            if ($scope.posts[i].id == data) {
                $scope.post.splice(i, 1);
            }
        }
    }

});

//Post Controller
app.controller('PostController', function PostController($scope, $routeParams, Data) {
    Data.getPost($routeParams.id).success(parsePost);

    function parsePost(data) {
        $scope.post = data;
    }

    Data.getTags($routeParams.id).success(parsePostsTags);

    function parsePostsTags(data) {
        $scope.tags = data;
    }

    $scope.newTag = { tag: '' };

    $scope.addTag = function addTag() {
        $scope.newTag.post_id = $scope.post.id;
        Data.addTag($scope.newTag).success(tagAddSuccess).error(tagAddError);
    }

    function tagAddSuccess(data) {
        $scope.error = null;
        $scope.tags.push(data);

        $scope.newTag = { tag: '' };
    }

    function tagAddError(data) {
        $scope.error = data;
    }

    $scope.removeTag = function removeTag(id) {
        if (confirm('Do you really want to remove this tag?')) {
            Data.removeTag(id).success(tagRemoveSuccess);
        }
    }

    function tagRemoveSuccess(data) {
        var i = $scope.tags.length;
        while (i--) {
            if ($scope.tags[i].id == data) {
                $scope.tags.splice(i, 1);
            }
        }
    }
});

//About Controller
app.controller('AboutController', function AboutController($scope, Data) {



});

//Portfolio Controller
app.controller('PortfolioController', function PortfolioController($scope, Data) {



});

//Contact Controller
app.controller('ContactController', function ContactController($scope, Data) {



});

//Page Controller
app.controller('PageController', function PageController($scope, Data) {



});

I have no clue where to go from here. I have tried everything from the normal header() implementation to using laravel-cors package to implement via filters and the _construct in the controller. I have also gone the server config route and tried adding the header to the .htaccess and the virtualhost config.

7条回答
倾城 Initia
2楼-- · 2019-02-07 11:44

I don't have good knowledge in laravel.but My suggestion is to the request headers to access REST Methods(GET,POST,PUT,DELTE) and origin to specific domain from which domain your making request in the following way or else set to '*'(it allows any domain)

header('Access-Control-Allow-Origin', 'some url');
header('Allow', 'GET, POST, OPTIONS');
header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization, X-Request-With');
header('Access-Control-Allow-Credentials', 'true');

At angular js.If you use <1.2 you can set CORS in controller file like following way.In latest version not required it will set default.you need to set what content type your expecting from server default is json.If your expecting other type of content you can set manually in the request.

myApp.config(['$httpProvider', function($httpProvider) {
            $httpProvider.defaults.useXDomain = true;
            delete $httpProvider.defaults.headers.common['X-Requested-With'];
        }
    ]);
查看更多
看我几分像从前
3楼-- · 2019-02-07 11:44

what i have done but not sure if that is the best solution but there are no cors with that one

1.build the angular normally using ng build --prod

2.move the content of angular/dist to laravel/public

3.then use this simple code in laravel/routes/web.php

Route::prefix('api')->group(function () {
    Route::get('/', function () {
    return view('welcome');
    });
});
Route::get('/{params?}', function () {
//    return view('welcome');
    include_once '../public/index.html';
})->where('params', '(.*)');

now all requests comes to Laravel and if it can catch the request with a route this route will work otherwise it will pass it to angular

查看更多
Fickle 薄情
4楼-- · 2019-02-07 11:52

I had the same problem, but with jQuery and took me weeks to get a good solution.

I my case create an middleware to set headers was the perfect solution.

Create a Cors middleware: App\Http\Middleware\Cors.php

namespace App\Http\Middleware;

use Closure;

class Cors
{

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', $_SERVER['HTTP_ORIGIN'])
            // Depending of your application you can't use '*'
            // Some security CORS concerns 
            //->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'POST, OPTIONS')
            ->header('Access-Control-Allow-Credentials', 'true')
            ->header('Access-Control-Max-Age', '10000')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
    }
}

Remember to set Cors alias inside App\Http\Kernel

protected $routeMiddleware = [
    ...
    'cors' => \App\Http\Middleware\Cors::class,
];

Inside Routes you can use middleware with group or direct to an specific route, in e.g.:

Route::match(['post', 'options'], 'api/...', 'Api\XController@method')->middleware('cors');

If someone have this problem with jQuery, I recommend to use $.ajax, instead of $.get, $.post. When you use this methods, jQuery send data using XMLHttpRequest and set content-type to application/x-www-form-urlencoded, it's impossible to change that, so, use Ajax.

e.g.:

        $.ajax({
            type: 'POST',
            url: 'www.foo.bar/api',
            contentType: "application/json",
            xhrFields: {
                // The 'xhrFields' property sets additional fields on the XMLHttpRequest.
                // This can be used to set the 'withCredentials' property.
                // Set the value to 'true' if you'd like to pass cookies to the server.
                // If this is enabled, your server must respond with the header
                // 'Access-Control-Allow-Credentials: true'.
                withCredentials: true

            },

            headers: {
                // Set any custom headers here.
                // If you set any non-simple headers, your server must include these
                // headers in the 'Access-Control-Allow-Headers' response header.
                'Accept': 'application/json'
            },



            data: '{"some":"json data"}',
            success: function (data) {
                console.log('AJAX version');
                console.log("Works!")
            },
        });

Remember: If you are using application/json on request header you must provide "OPTIONS" method, to do a preflight.

More info about CORS: http://www.html5rocks.com/en/tutorials/cors/

查看更多
放荡不羁爱自由
5楼-- · 2019-02-07 11:53

When you invoke a cross origin XHR request, javascript first fires an OPTIONS request to the given URL. If this method hasnt added in your routes, then it pops a 404 page which is served without the ACAO header so the concrete POST request wont be sent, as javascript see it not allowed to.

查看更多
来,给爷笑一个
6楼-- · 2019-02-07 11:57

Add these lines in your public/index.php:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token'); // allow certain headers 

See, if that works.

查看更多
相关推荐>>
7楼-- · 2019-02-07 11:59

add

<?php header("Access-Control-Allow-Origin: *"); ?>

to public/index.php if it doesn't work in the function like blue bells suggested

查看更多
登录 后发表回答