Как мне обойти проблему между доменами при анализе XML с другого сервера/домена? Может ли кто-нибудь предоставить мне пример? Пример не должен ограничиваться только jQuery, так как JavaScript также будет достаточным.
Как обрабатывать XML-кросс-домен в jQuery?
Ответ 1
Чтобы полностью понять, почему чистый междоменный XML не будет работать, он помогает сначала посмотреть, как облегчается междоменный JSON.
Во-первых, давайте посмотрим, что произойдет, когда вы выполните запрос AJAX в jQuery:
$.ajax({
url: '/user.php?userId=123',
success: function(data) {
alert(data); // alerts the response
});
В приведенном выше примере запрос AJAX сделан относительно домена. Мы знаем, что если мы попытаемся добавить другой путь до пути, запрос завершится с исключением безопасности.
Однако это не означает, что браузер не может отправлять запросы другому домену. Вот пример, который вам может быть знаком:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
Основываясь на наших знаниях о том, как импортировать JavaScript на странице, мы видим, что можно загрузить ресурс, который существует в другом домене!
JSONP - это концепция, которая использует это знание. JSONP означает "JSON with padding", и успех этого успеха зависит от того, что объекты JavaScript могут быть выражены с использованием строковой нотации, а также то, что теги JavaScript script могут загружать и запускать контент из внешних доменов.
Под капотом jQuery JSONP выглядит примерно так, хотя это может быть и не так:
// programmatically load a script tag on the page using the given url
function loadRemoteData(url) {
var script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(script);
}
Кроме того, на странице где-то мы определяем обработчик обратного вызова:
function processData(jsonResult) {
alert(JSON.stringify(jsonResult)); //alert the JSON as a string
}
Здесь мы делаем запрос:
// make a request for the data using the script tag remoting approach.
loadRemoteData("http://example.com/users.php?userId=123&callback=processData");
Для того, чтобы это правильно работало, наш PHP script должен как вернуть данные в формате JSON, так и добавить "дополнение" вокруг строки в виде имени функции JavaScript, которое мы можем передать в качестве параметра (т.е. "обратный вызов" )
Таким образом, ответ с сервера может выглядеть примерно так, если бы мы посмотрели его на вкладке Firebug или Chrome NET:
processData( { "userId" : "123" , "name" : "James" , "email" : "[email protected]" } );
Поскольку мы знаем, что содержимое JavaScript запускается сразу после его загрузки, наша функция processData
, которую мы определили ранее, немедленно вызывается и передается нашей строкой JSON в качестве параметра. Затем он предупреждается, используя JSON.stringify для преобразования объекта обратно в строку.
Так как это объект, я мог бы также получить доступ к его свойствам, например:
function processData(jsonResult) {
alert(JSON.stringify(jsonResult)); //alert the JSON as a string
// alert the name and email
alert("User name is " + jsonResult.name + " and email is " + jsonResult.email);
}
Наконец, перейдем к основному вопросу: Может ли JSONP использоваться для извлечения XML или мы можем проанализировать XML-домен? Ответ, как указывали другие, - это громкое НЕТ, но давайте посмотрим, почему, используя пример:
processData(<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>[email protected]</email></user>);
Теперь, что произойдет, если исходный XML будет передан в функцию? Он сломается, поскольку JavaScript не имеет возможности преобразовать XML в JSON.
Однако предположим, что мы помещаем XML в кавычки:
processData("<?xml version="1.0"><user><userid>12345</userid><name>James</name><email>[email protected]</email></user>");
Теперь в этом примере переменная jsonResult фактически принимает строку, с которой мы можем работать. Используя некоторые утилиты для анализа XML JavaScript, мы могли бы загрузить эту строку в XML DOM Parser и делать с ней все!
Однако, это не чистый XML, это еще ответ JavaScript под капотом. Тип ответа с сервера PHP по-прежнему является текстовым /javascript, и мы все еще используем тег script для загрузки того, что на самом деле является просто обычным JavaScript.
В общем, вы можете работать с XMLP или XML с дополнением (я только что сделал это, это не реально!), но если вы собираетесь решить все проблемы, связанные с фактическим изменением вашего ответа на возврат вы можете просто преобразовать свой вывод в JSON и позволить браузеру обрабатывать преобразования автоматически и изначально и избавить себя от необходимости использовать XML-парсер.
Но если по какой-то причине проще хранить данные в формате XML, вы можете изменить ответ и дать ему оболочку JavaScript.
Случаи, когда я мог бы заметить, что это полезно, могут быть, если у вас есть данные XML из устаревшего приложения, хранящиеся в базе данных, и вы возвращаете его на стороне клиента, используя script -tag remoting или JSONP-вызовы.
Ответ 2
Я нашел очень хорошее решение для извлечения xml из запроса ajax междоменной области.
Так как jQuery 1.5 вы можете использовать dataType "jsonp xml" (http://api.jquery.com/jQuery.ajax/)!
Итак, я использовал это:
$.ajax({
type: "GET",
url: "http://yoururl",
dataType: "jsonp xml",
success: function(xmlResponse) { // process data }
});
На стороне сервера для моих веб-служб я использовал для инкапсуляции результата строки xml в обратном вызове, созданном jQuery:
private static Stream GetXmlPStream(string result, string callback)
{
if (result == null)
result = string.Empty;
result = EncodeJsString(result);
if (!String.IsNullOrEmpty(callback))
result = callback + "(" + result + ");";
byte[] resultBytes = Encoding.UTF8.GetBytes(result);
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
return new MemoryStream(resultBytes);
}
и волшебный метод (я нашел его в другом потоке стека), что вам нужно будет дезинформировать вашу XML-строку (поэтому javascript может ее разобрать):
private static string EncodeJsString(string s)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char c in s)
{
switch (c)
{
case '\"':
sb.Append("\\\"");
break;
case '\\':
sb.Append("\\\\");
break;
case '\b':
sb.Append("\\b");
break;
case '\f':
sb.Append("\\f");
break;
case '\n':
sb.Append("\\n");
break;
case '\r':
sb.Append("\\r");
break;
case '\t':
sb.Append("\\t");
break;
default:
int i = (int)c;
if (i < 32 || i > 127)
{
sb.AppendFormat("\\u{0:X04}", i);
}
else
{
sb.Append(c);
}
break;
}
}
sb.Append("\"");
return sb.ToString();
}
Надеюсь, это поможет!