Laravel 5.5 - CORS GET 请求更改为 OPTIONS

Laravel 5.5 - CORS GET request changed into OPTIONS

我正在尝试在客户的网站上显示我的小部件。但是我无法在 Laravel 5.5 中使用 CORS 使其工作。这是我的代码:

public/js/cb.js JavaScript 文件加载到客户的网站上。

window.onload = do_request();

function do_request()
{
    var url = "http://cb.dev.server-website.com/api/books/";
    var book_id = 0;
    var elementExists = document.getElementById("cb_script");
    if (typeof elementExists != "undefined" && elementExists) {
        var book_id = elementExists.getAttribute('data-id');
    }
    if (typeof book_id != "undefined" && book_id) {
        var parts = book_id.split('_');
        var loc = parts.pop();
        url += loc;
    }
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url);
    xmlhttp.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
    xmlhttp.setRequestHeader("Access-Control-Allow-Origin", "*");
    xmlhttp.setRequestHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
    xmlhttp.setRequestHeader("Access-Control-Allow-Headers", "Content-Type");
    xmlhttp.setRequestHeader("Access-Control-Request-Headers", "X-Requested-With, accept, content-type");
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            var jsondata = JSON.parse(xmlhttp.responseText);
            if (
                typeof(jsondata) != "undefined" && jsondata != ""
                && typeof(jsondata['data']) != "undefined" && jsondata['data']
            ) {
                document.getElementById("stirbook_live").innerHTML = jsondata['data'].html;
                do_modal('myModal');
            }
        }
    };
    xmlhttp.send();
}

routes/api.php Laravel API 路由文件

Route::resource('books', 'Api\BooksapiController')
    ->middleware('preflight');

app/Http/Middleware/PreflightResponse.php Laravel 中间件文件

namespace App\Http\Middleware;
use Closure;
class PreflightResponse
{
    public function handle($request, Closure $next)
    {
        if ($request->getMethod() === "OPTIONS") {
            return response('');
        }
        return $next($request);
    }
}

app/Http/Controllers/ApiBooksapiController.php Laravel 控制器文件

public function show($id)
{
    $data = $book_array = array();
    $output = '';
    $book = DB::table('books')
        ->join('questions', 'books.id', '=', 'questions.book_id')
        ->select('books.user_id', 'books.website', 'questions.id AS question_id', 'questions.question')
        ->get()->toArray();
    if (is_array($book) && count($book)) {
        $questions_html = '';
        for ($i = 0; $i < count($book); $i++) {
            $questions_html .= '<h5>Q: ' . $book[$i]->question . '</h5>';
            $answers = DB::table('answers')
                ->where([
                    ['question_id', $book[$i]->question_id],
                    ['status', 1]
                ])
                ->select('answers.id', 'answers.answer', 'answers.o_quantity', 'answers.o_percentage', 'answers.o_schedule', 'answers.o_overunder', 'answers.o_overunder')
                ->get()->toArray();
            if (is_array($answers) && count($answers)) {
                $questions_html .= '<ul style="list-style-type: disc;">';
                for ($j = 0; $j < count($answers); $j++) {
                    $questions_html .= '<ol>
                        <input type="radio" name="question_' . $book[$i]->question_id . '" value="1" id="answer_' . $j . '_' . $book[$i]->question_id . '" class="radio-class" onclick="do_survey(' . $book[$i]->question_id . ', ' . $answers[$j]->id . ', ' . $answers[$j]->o_quantity . ')" />&nbsp;
                        <label for="answer_' . $j . '_' . $book[$i]->question_id . '">' . $answers[$j]->answer . '</label>
                        <input type="hidden" name="offer_' . $j . '_' . $book[$i]->question_id . '" value="' . $answers[$j]->o_quantity . '" />
                    </ol>';
                }
                $questions_html .= '</ul>';
            }
        }
        $output .= '
            <!-- Trigger/Open The Modal -->
            <button id="myBtn" class="btn btn-primary">Opt-out</button>
            <!-- The Modal -->
            <div id="myModal" class="modal" style="display: none; position: fixed; z-index: 1; padding-top: 100px; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4);">
              <!-- Modal content -->
              <div class="modal-content" style="background-color: #fefefe; margin: auto; padding: 20px; border: 1px solid #888; width: 80%;">
                <span class="close" style="color: #aaaaaa; float: right; font-size: 28px; font-weight: bold;">&times;</span>
                <div id="book_form">
                    <h3>book</h3>
                    <form name="fbook" action="#" method="post">
                    ' . $questions_html . '
                    </form>
                </div>
                <div id="offer_resp" style="display: none;">
                    <h3>Wait!!!</h3>
                    <div id="rm_body">
                        <p>Please don\'t go! How\'s this, we\'ll give you <span class="number-of-months"><numberofmonths></span> free month(s) for trying us out a little longer.</p>
                    </div>
                    <button id="yes_button" class="btn btn-primary" onclick="yes_response()" value="">Yes, give me <span class="number-of-months"><numberofmonths></span> month(s) free!</button>
                    <button id="no_button" class="btn btn-default" onclick="no_response()" value="">No thank you, I wish to cancel now!</button>
                </div>
              </div>
            </div>
        ';
    }
    if (isset($output) && $output) {
        $data['html'] = $output;
    }
    return response()->json(['data' => $data], 200);
}

但它不起作用。当我在 Postman 中尝试时,它工作正常。但是当我在任何其他网站上尝试它时,它会将请求从 GET 更改为 OPTIONS 并且什么也不提供。这可能是因为我的服务网站位于子域中吗?我花了一整天的时间来寻找解决方案,并且我已经应用了互联网上几乎所有可用的解决方案。

OPTIONS 调用是由某些浏览器自己作为飞行前请求进行的,以检查可接受的请求类型(GET、POST、PUT...)请求的 URL.

Chrome 在尝试 CORS 请求时首先发送 OPTIONS 请求。

好的,所以我找到了解决问题的方法。真正的问题出在我用 GET 请求发送的 header 中。原来发送GET Request时需要public/js/cb.js中提到的Headers的none。只需发送一个简单的 GET 请求。

这里是修改后的public/js/cb.js文件代码:

window.onload = do_request();

function do_request()
{
    var url = "http://cb.dev.server-website.com/api/books/";
    var book_id = 0;
    var elementExists = document.getElementById("cb_script");
    if (typeof elementExists != "undefined" && elementExists) {
        var book_id = elementExists.getAttribute('data-id');
    }
    if (typeof book_id != "undefined" && book_id) {
        var parts = book_id.split('_');
        var loc = parts.pop();
        url += loc;
    }
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url);
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            var jsondata = JSON.parse(xmlhttp.responseText);
            if (
                typeof(jsondata) != "undefined" && jsondata != ""
                && typeof(jsondata['data']) != "undefined" && jsondata['data']
            ) {
                document.getElementById("stirbook_live").innerHTML = jsondata['data'].html;
                do_modal('myModal');
            }
        }
    };
    xmlhttp.send();
}

routes/api.php 文件无需更改,但 app/Http/Middleware/PreflightResponse.php 文件.

app/Http/Middleware/PreflightResponse.php 文件的修改代码:

namespace App\Http\Middleware;
use Closure;
class PreflightResponse
{
    public function handle($request, Closure $next)
    {
        if ($request->getMethod() === "OPTIONS") {
            return response('')
                ->header('Access-Control-Allow-Origin', '*')
                ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
        }
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    }
}

一切就绪。 PreflightResponse 中间件将发送响应 header...

Access-Control-Allow-Origin: *

到客户的网站。告诉它服务器接受对 API.

的每个请求的 CORS 请求