2009-11-05 15 views
39

Tengo dos dominios, example1.com y example2.comJavaScript: ¿Cómo creo JSONP?

En example1.com, me gustaría llamar a una API JSON que tengo en example2.com. Sabiendo que esto no está permitido, se me ocurrió - esto es exactamente por qué JSON P fue creado.

pregunta es, ¿Cómo puedo modificar mi API JSON para que sea capaz JSONP?

Básicamente, ¿cómo puedo crear la API de devolución de llamada?

ACTUALIZACIÓN

Mi lenguaje del lado del servidor es PHP

Respuesta

26

Se necesita un lenguaje de servidor, el parámetro de devolución de llamada es simplemente un parámetro GET , lee el parámetro, y ajusta la respuesta JSON en una llamada de función y la imprime así callback(jsonResponse);.

os dejo un ejemplo muy minimalista usando Python ya que no menciona ningún lenguaje de servidor:

import os 
import cgi 

form = cgi.FieldStorage() 
callback = form.getvalue('callback','') 

address = cgi.escape(os.environ["REMOTE_ADDR"]) 

json = '{"ip": "'+address+'", "address":"'+address+'"}' 

#Allow cross domain XHR 
print 'Access-Control-Allow-Origin: *' 
print 'Access-Control-Allow-Methods: GET' 

if callback != '': 
    print 'Content-Type: application/javascript' 
    result = callback+'('+json+');' 
else: 
    print 'Content-Type: application/json' 
    result = json 

print '' 
print result 

Ese es el código de una pequeña JSONP service utilizado para obtener la dirección IP del cliente hecha por Zach y está alojado en el Google App Engine.

+0

¡Tu código funcionó muy bien y es muy elegante! – TruMan1

+0

Esta respuesta debe aceptarse –

73

Es simple. Simplemente acepte un parámetro llamado callback en GET.

luego envolver la devolución de llamada de JavaScript alrededor de sus datos.

Ejemplo en PHP:

<?php 

$data = '{}'; // json string 

if(array_key_exists('callback', $_GET)){ 

    header('Content-Type: text/javascript; charset=utf8'); 
    header('Access-Control-Allow-Origin: http://www.example.com/'); 
    header('Access-Control-Max-Age: 3628800'); 
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); 

    $callback = $_GET['callback']; 
    echo $callback.'('.$data.');'; 

}else{ 
    // normal JSON string 
    header('Content-Type: application/json; charset=utf8'); 

    echo $data; 
} 

Es idea es simplemente devolver un archivo JavaScript que llama a la función de devolución de llamada con el objeto JSON como el primer parámetro de la función de devolución de llamada de JavaScript.

Usted puede utilizar el incorporado en json_encode() función para crear cadenas JSON (que $data en nuestro ejemplo anterior contiene) de las matrices y los objetos en PHP.

Para utilizar el servicio JSONP, puede utilizar la etiqueta <script>:

<script> 
    function receiver(data){ 
     console.log(data); 
    } 
</script> 
<script src="data-service.php?callback=receiver"></script> 
+4

Guau, ¿es realmente así de simple? – Teddi

+0

¿Debo agregar: echo 'Access-Control-Allow-Origin: *' echo 'Access-Control-Allow-Methods: GET' ??? – Teddi

+0

@Teddi: Esos encabezados son compatibles con la próxima especificación XMLHttpRequest 2 (http://www.w3.org/TR/access-control/) y por cierto ya funcionan en Firefox 3.5 (https://developer.mozilla.org/es/HTTP_access_control) – CMS

6

Mauris ya le dio un ejemplo de trabajo. Sólo añadiría que usted debe comprobar si un parámetro callback está presente y no está vacío, y si no es así, devolver los datos JSON tal cual, sin los paréntesis. Así que, básicamente, su API será JSON con la disposición de ser JSON-P cuando se dé callback.

Para consumir el servicio web JSON-P, a menos que utilice un marco como YUI o jQuery, puede simplemente crear un nodo de script dinámicamente y establecer su atributo src para apuntar al servicio web. Recuerde eliminar el nodo del dominio antes de repetirlo nuevamente, ya que este nodo de script dinámico es de uso único.

2

fácil con jQuery, que es el lado del cliente:

$.ajax({ 
     dataType: 'jsonp', 
     data: "somedata="+somevalue, 
     //this is very important since it's the callback we will and that allow cross domain 
     jsonp: 'jsonp_callback', 
     url: 'http://example2.com', 
     //function we trigger on success 
     success: ParseJson 
     //error handling not working with jsonP 
     //error: handleError 
     }); 

function ParseJson(data) 
{ 
for (var key in data) { 
    if (data.hasOwnProperty(key)) { 
    alert(key + " -> " + data[key]); 
    } 
} 
} 

Y asegúrese de que usted obtenga JSON adecuada desde el lado del servidor;
y no olvide devolver el jsonp_callback param, de lo contrario, no funcionará !!!!!
y eso es todo.

3

/// el guión de devolución de llamada debe devolver el nombre de la función de devolución de llamada, es decir, si escribe en el navegador

http://url_to_receiver_script/index.php&param=anything

que debería devolver sólo un texto (nombre de la función de procesamiento existente): addScriptToHead (any_param)

funciona como un reloj en cualquier navegador.

4

Sé que llego tarde a la fiesta, y hubo un comentario sobre la seguridad del código en una de las respuestas. Aquí es un buen artículo sobre esto:

http://www.geekality.net/2010/06/27/php-how-to-easily-provide-json-and-jsonp/

Y aquí está el código que se debe estar ejecutando:

<?php header('content-type: application/json; charset=utf-8'); 

function is_valid_callback($subject) 
{ 
    $identifier_syntax 
     = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; 

    $reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case', 
     'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 
     'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 
     'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 
     'extends', 'super', 'const', 'export', 'import', 'implements', 'let', 
     'private', 'public', 'yield', 'interface', 'package', 'protected', 
     'static', 'null', 'true', 'false'); 

    return preg_match($identifier_syntax, $subject) 
     && ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words); 
} 

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9); 
$json = json_encode($data); 

# JSON if no callback 
if(! isset($_GET['callback'])) 
    exit($json); 

# JSONP if valid callback 
if(is_valid_callback($_GET['callback'])) 
    exit("{$_GET['callback']}($json)"); 

# Otherwise, bad request 
header('status: 400 Bad Request', true, 400); 
+0

Si por casualidad alguien está utilizando angular, esto no funcionará. Angular admite jsonp, pero reemplaza la devolución de llamada con algo como 'angular.callback._0'. ¿Permitiría que una devolución de llamada como esta sea insegura? –

+0

@BobVork No lo creo, puede cambiar '$ identifier_syntax' para incluir también períodos. De hecho, creo que terminé haciendo eso en mi implementación por esa misma razón (pero publiqué esto antes de haber terminado mi implementación). – jkinz

0

Puede utilizar el Simple JSON for PHP a forjarlo! ¡Simplifica todo!

<?php 

    include('../includes/json.php'); 

    $json = new json('callback', 'myCallback'); 

    $object = new stdClass(); 
    $object->FirstName = 'John'; 
    $object->LastName = 'Doe'; 
    $array = array(1,'2', 'Pieter', true); 
    $jsonOnly = '{"Hello" : "darling"}'; 
    // Add objects to send 
    $json->add('status', '200'); 
    $json->add("worked"); 
    $json->add("things", false); 
    $json->add('friend', $object); 
    $json->add("arrays", $array); 
    $json->add("json", $jsonOnly, false); 

    /* 
    Expected result : 
    myCallback({ 
    "status": "200", 
    "worked": true, 
    "things": false, 
    "friend": { 
     "FirstName": "John", 
     "LastName": "Doe" 
    }, 
    "arrays": [ 
     1, 
     "2", 
     "Pieter", 
     true 
    ], 
    "json": { 
     "Hello": "darling" 
    } 
    }); 

    */ 
    $json->send(); 
?>