Обновить всю страницу по запросу Ajax

У меня есть запрос AJAX, который может иметь два возможных результата:

  • Сервер отвечает сообщением, которое я должен разместить в <div>
  • Сервер отвечает HTML-страницей, в этом случае мне нужно заменить текущую страницу на новую и изменить адрес (клиент знает адрес перед запросом).

Каким будет решение, если у меня есть запрос AJAX, который должен обрабатывать оба этих случая?

 url = "http://example.com"
 ajax.request(callback)

 function callback(response) {
     if (case2(response)) {
           history.pushState({}, "New page", url);
           document.innerHTML = response
     } else {
            updateDiv(response)
     }
 }

Мне интересен правильный способ реализации первой ветки, или если сервер может каким-то образом составить заголовки, которые заставят браузер обрабатывать ответ как обычный ответ HTTP и обновлять местоположение и содержимое страницы, что-то вроде перенаправления с данным контентом.

Я понимаю, что сервер может возвращать ссылку вместо страницы, но в этом случае на клиенте потребуется один дополнительный этап - перенаправление и последующее заполнение новой страницы на сервере.

Ответ 1

Совершенно откровенно, я думаю, что этот подход в основном нарушается дизайном. Вы не должны принимать это решение в этом месте. Например, ответ ajax может сигнализировать только о том, что нужно загрузить всю новую страницу, а затем новый контент будет генерироваться на втором (не-ajax) запросе на новый URL-адрес.

Если вы вынуждены пойти по пути, который вы уже прошли, и при условии, что контент ответа не очень велик, вы можете попробовать Javascript-URI. В принципе, URI в форме javascript:"string" будет загружать новую страницу, для которой эта строка является исходным кодом. Итак, если response уже является строкой, достаточно просто назначить javascript:response на window.location.href. Может быть, вам нужно немного ускользнуть. И я не знаю, как кросс-браузер-совместимый этот подход.

<a href="javascript:response">load</a>

также возможно.

Вариант этого заключается в создании URL-адреса не с именем переменной, а с фактическими строковыми данными. Как

function source2url(src) {
    // make valid javascript string from source text
    var esc1 = src
        .replace(/\\/g, '\\\\')
        .replace(/\'/g, '\\\'')
        .replace(/\x0A/g, '\\x0A')
        .replace(/\x0D/g, '\\x0D');

    // make valid url from that
    return "javascript:'" + encodeURIComponent(esc1) + "'";
}

window.location.href = source2url(response);

Это, конечно же, генерирует довольно большие URI. И вы всегда будете иметь Javascript-URI в адресной строке.

UPDATE

Аналогичный подход заключается в использовании кодирования base64 в URI данных. Запись в Википедии объясняет, как это работает, включая пример javascript. Однако вы должны base64-encode контент каким-то образом. (Примечание. Вы можете использовать URI данных с кодировкой base64 или без нее. Вы должны увидеть, что дает более короткие URI для вашего конкретного контента.)

Ответ 2

У меня была аналогичная проблема. Была возвращена полная страница ошибки вместо простого фрагмента HTML. Мы в конце концов зафиксировали это, изменив логику, но вот одно из решений, которые я нашел:

document.open();
document.write(responseText);
document.close();

Причина, по которой мы отказались от этого, заключается в том, что в IE возникли некоторые проблемы. Я не терял времени, чтобы исследовать причину, но при попытке написать строку он выбрал исключение "Доступ запрещен". Я думаю, что есть теги <meta>, которые путают IE, или, может быть, условные комментарии, я не уверен. (Он работал, когда я использовал несколько простых страниц...)

Нижняя строка: вам не нужно это делать, но если вы ничего не можете сделать (например, вернуть строку url), код может работать.

Ответ 3

Это очень просто, если ответ действителен XML.

var new_doc = (new DOMParser).parseFromString(response, "application/xml");
document.replaceChild(document.adoptNode(new_doc.doctype), document.doctype);
document.replaceChild(document.adoptNode(new_doc.documentElement), document.documentElement);

Ответ 4

Поскольку запрос содержит обновленный ответ, здесь мое решение с использованием HTML5 API истории с jQuery. Он должен работать легко, объединяя PHP и HTML-части в один файл.

Мое решение позволяет AJAX возвращать следующее:

  • Сообщение через AJAX, которое обновляет контейнер <div>.
  • URL-адрес, который заставляет браузер перенаправлять URL-адрес
  • Полная HTML-страница, которая вызывает API истории history.pushState(), чтобы добавить текущий URL-адрес в историю браузера и заменяет весь HTML на странице HTML-кодом, возвращенным из AJAX.

PHP

Это всего лишь пример того, что PHP script должен будет вернуть, когда он вызывается через AJAX. Он показывает, как кодировать флаги, чтобы определить, должен ли вызов AJAX обновлять контейнер или загружать новую страницу, и как вернуть его результат через JSON через json_encode. Для полноты я назвал это script test.php.

<?php
// Random messages to return
$messages = array(
    'Stack Overflow',
    'Error Message',
    'Testing'
);

// If the page was requested via AJAX
if( isset( $_POST['ajax']))
{
    $response = array(
        'redirect' => // Flag to redirect
            ( rand() % 2 == 0) ? true : false, 
        'load_html' => // Flag to load HTML or do URL redirect
            ( rand() % 2 == 0) ? true : false,
        'html' => // Returned HTML
            '<html><head><title>AJAX Loaded Title</title></head><body>It works!</body></html>',
        'title' => 'History API previous title',
        'message' => // Random message
            $messages[ (rand() % count( $messages)) ]
    );
    echo json_encode( $response);
    exit;
}

JS

Поскольку я использую jQuery, давайте начнем с этого. Следующее отправляет AJAX POST на сервер, на указанный выше PHP script по адресу URL test.php. Обратите внимание, что он также устанавливает для параметра POST ajax значение true, что позволяет PHP скрипт обнаруживать, что он получил запрос AJAX. Поле dataType сообщает jQuery, что ответ сервера будет в JSON и что он должен декодировать JSON для объекта JSON в ответном обратном ответе. Наконец, обратный вызов success, который запускается при успешном получении ответа AJAX, определяет, что делать на основе флагов, отправленных с сервера.

$.ajax({
    type: 'POST',
    url: "/test.php",
    data: {ajax : true},
    dataType: "json",
    success: function( json) {
        if( json.redirect) {
            if( json.load_html) {   
                // If the History API is available  
                if( !(typeof history.pushState === 'undefined')) {
                    history.pushState( 
                        { url: redirect_url, title: document.title}, 
                        document.title, // Can also use json.title to set previous page title on server
                        redirect_url
                    );
                }
                // Output the HTML
                document.open();
                document.write( json.html);
                document.close();
            }
            else {
                window.location = redirect_url;
            }
        }
        else {
            $('#message').html( json.message);
        }
    },
});

HTML

Вот полный источник HTML моего тестируемого файла. Я тестировал его в FF4 - FF8. Обратите внимание, что jQuery предоставляет метод ready для предотвращения выполнения JS до загрузки DOM. Я также использовал Google хостинг jQuery, поэтому вам не нужно загружать копию jQuery на ваш сервер, чтобы проверить это.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
    <title>Default Page</title>

    <script type="text/javascript"">        
        $( document).ready( function() {
            $('#ajax_link').click(  function() {
                var redirect_url = "/test.php";
                $.ajax({
                    type: 'POST',
                    url: "/test.php",
                    data: {ajax : true},
                    dataType: "json",
                    success: function( json) {
                        if( json.redirect) {
                            if( json.load_html) {   
                                // If the History API is available  
                                if( !(typeof history.pushState === 'undefined')) {
                                    history.pushState( 
                                        { url: redirect_url, title: document.title}, 
                                        document.title, // Can also use json.title to set previous page title on server
                                        redirect_url
                                    );
                                }
                                document.open();
                                document.write( json.html);
                                document.close();
                            }
                            else {
                                window.location = redirect_url;
                            }
                        }
                        else {
                            $('#message').html( json.message);
                        }
                    },
                });
            })
        });
    </script>
</head>

<body>
    <div id="message">The default contents of the message</div>
    <a id="ajax_link" href="#">Fire AJAX</a>

</body>
</html>

Ответ 5

Дайте id телу <body id="page">, а ваш другой div будет <div id="message"></div>, теперь ваш ajax будет выглядеть как

 $.ajax({
      url:'myAjax.php',
      data:{datakey:datavalue},
      dataType:"JSON",
      success: function (response) {
           if(response.message=="your message")
           {
             $('#message').html(response.content);
           }
           else
           {
             $('#page').html(response.content);   
           }
      }
     });

Ответ 6

как говорит T-Bull... весь процесс здесь неправильный....

вы просто слишком усложняете вещи, и вы знаете, что infact:

Я понимаю, что сервер может вернуть ссылку вместо страницы, но в этом случае на клиента потребуется еще один этап - перенаправить и затем заполнить новую страницу на сервере.

перестаньте усложнять и начните делать это хорошо...

  • Клиент открывает страницу в первый раз, поэтому отслеживать ее $_SESSION['tmp_client_id'] = 'client_'.session_id();, очевидно, лучше, если клиент уже подписан, так или иначе, помещает материал в таблицу temp ИЛИ в другой сеанс var и т.д.
  • Клиент заполнит форму;
  • Клиент отправит форму;
  • Сделать запрос AJAX;
  • Хранить $_POST переменную внутри tmp_client_tbl с ее уникальной tmp_client_id ИЛИ просто $_SESSION['client_'.session_id()] = json_encode($_POST);
  • Результат №1? отобразить сообщение в </div>
  • Результат №2? обновить страницу и проверить if( isset($_SESSION['client_'.session_id()])) {, если это позволяет снова отобразить форму с заполненными полями: } else { показать пустую форму;
  • SELECT * FROM tmp_client_tbl WHERE tmp_client_id = '{$_SESSION['tmp_client_id']}' ИЛИ json_decode($_SESSION['client_'.session_id()]);
  • $form_data = $mysql_rows; ИЛИ $json_array;
  • foreach($form_data as $name => $value) { echo "<input name='$name' value='$value' />" } в способе ниндзя, предполагающем, что у вас есть такой массив форм-форм, где $form = array('text' => array('name','lastname'), 'select' => array('countries'), ... ), ИЛИ просто <input name='lastname' value='{$lastname}' />, где значения полей предварительно распределены с пустыми vars;
  • прошло время, произошла ошибка, браузер закрыт? session_destroy(); или unset($_SESSION['client_'.session_id()]);