Проблема с JSONP - PullRequest
       6

Проблема с JSONP

0 голосов
/ 12 октября 2011

Как часть изучения JSONP, я делаю вызов ajax следующим образом:

var jsonp_url = "http://localhost:8080/test/ad";
$.getJSON(jsonp_url, function(data) {
   $('#example-widget-container').html(data.html);
});

Когда я нажимаю http://localhost:8080/test/ad,, он возвращает:

? ( {'html': '<strong>Hello World!</strong>' } )

Код Spring, возвращающий это:

@RequestMapping(method = RequestMethod.GET, value = "ad")
public void getAd(HttpServletResponse response){
    PrintWriter out = null;
    response.setContentType("text/javascript");
    try {
        out = response.getWriter();
    } catch (IOException e) {
        e.printStackTrace();
    }
    out.write("? ( {'html': '<strong>Hello World!</strong>' } )");
}

При выполнении я ожидаю, что Hello World! будет отображаться в <div id="example-widget-container"></div>, но это не так, поскольку обратный вызов не происходит.

Чего мне не хватает?

Ответы [ 6 ]

2 голосов
/ 12 октября 2011

$. GetJSON, за исключением правильного формата JSON и callme ({'html': 'hello world'}) не является форматом json

правильный формат json: {'html': 'hello world'}

1 голос
/ 12 октября 2011

Если вы возвращаете JSONP, не получайте JSON ... Попробуйте это

$.get("http://localhost:8080/test/ad", function(data) {
   alert(data.html);
}, 'jsonp');
1 голос
/ 12 октября 2011

ты просто не хочешь return "{'html': 'hello world' }";?

0 голосов
/ 12 января 2012

Это на самом деле довольно просто, используя Spring 3.0 и выше.Однако, глядя на приведенные выше примеры, я не понимаю, почему вы относитесь к пружинному контроллеру как к простому сервлету и печатаете напрямую в поток ответов.Этого следует избегать.Было бы намного лучше, если бы вы просто возвращали POJO, представляющий ваш массив JSON, и позволяли анализатору JSON создать ответ.

Первая задача - заставить Spring вернуть JSON.Это легко сделать, добавив @ResponseBody к вашему контроллеру, сказав контроллеру сериализовать POJO клиенту.Если в вашем пути к классам есть Джексон, он будет автоматически отправлен как JSON с помощью MappingJacksonHttpMessageConverter, который включен с помощью mvc: annotation-driven.

Но JSON недостаточно.Вы хотите JSON-P, предполагая, что клиент хочет использовать JSON в междоменном сценарии.Это может быть достигнуто несколькими различными способами.Вы можете реализовать фильтр сервлетов с помощью Springating DelegationFilterProxy.Фильтр может определить, запрашивается ли JSON-P, и вы можете соответствующим образом настроить ответ.

Так что для моего использования я предпочитаю расширять Spring 3.0 (+) MappingJacksonJsonView вместо фильтра и проверять, если параметр запросасодержал ключ «обратного вызова».Если я хочу JSON или JSONP, я могу просто добавить второе отображение сервлета в * .jsonp и отправить либо JSON, либо JSONP в зависимости от наличия параметра обратного вызова.

Вот код:

Поместите в свой контроллер следующее:

@RequestMapping(value="/ad", method=RequestMethod.GET)
public ModelMap getAvailabilityModel(@RequestParam(required = false) String callback) {
    ModelMap modelMap = new ModelMap();
    modelMap.addAttribute("html", "<strong>Hello World!</strong>");
    return modelMap;
}   

Возвращение ModelMap или даже ModelAndView позволяет мне делать разные вещи на основе сервлетаmapping.

Вот пользовательское представление для обработки расширения JSON-P (только для кишок, пропущено несколько переопределений для краткости):

public class MappingJacksonJsonpView extends MappingJacksonJsonView {

@Override 
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

if("GET".equals(request.getMethod().toUpperCase())) {

    Map<String, String[]> params = request.getParameterMap();

    if(params.containsKey("callback")) {
    String callbackName = params.get("callback")[0];
    response.getOutputStream().write(new String(callbackName + "(").getBytes());
    log.info("GETTER Found for a .jsonp method, callback name is : " + callbackName);
    super.render(model, request, response);
    response.getOutputStream().write(new String(");").getBytes());
    response.setContentType("application/javascript");
    }

    else {
    super.render(model, request, response);
    }
}

else {
    super.render(model, request, response);
}
  }
 }

Если запрос сопоставлен с * .jsonpи я вижу параметр запроса с «обратным вызовом» в качестве ключа, я предполагаю, что JSON-P, и я обертываю JSON информацией обратного вызова прямо над потоком ответа.Я позволю процессору JSON Джексона обрабатывать особенности преобразования POJO в надлежащий JSON во всех случаях.В моем контроллере я просто возвращаю POJO или ModelMap;не возиться писать свой собственный JSON в ответе.В качестве альтернативы я мог бы использовать модель и представление для возвращаемого типа.Это позволит правильно обрабатывать запросы * .do vs * .json.Используйте Firebug, чтобы убедиться, что вы получаете правильный тип мультимедиа в заголовках ответов, application / json для JSON и application / javascript для JSON-P.

Вот вывод для JSON-P с GET наhttp://localhost:8080/jsonpex/ad.jsonp?callback=xyz (Обратите внимание на оболочку с xyz, для использования JQuery?)

xyz({"html":"<strong>Hello World!</strong>"});

Если вы не укажете параметр запроса, он просто вернет JSON: http://localhost:8080/jsonpex/ad.jsonp

{"html":"<strong>Hello World!</strong>"}

Наконец, убедитесь, что вы правильно подключили новый вид:

<!-- Add our new View to the Application Context -->
<beans:bean  class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <beans:property name="order" value="1" />
    <beans:property name="favorPathExtension" value="true"/>
    <beans:property name="mediaTypes">
        <beans:map>
            <beans:entry key="json" value="application/json"/>
            <beans:entry key="jsonp" value="application/javascript"/>
        </beans:map>
    </beans:property>
    <beans:property name="defaultViews">
        <beans:list>
            <beans:bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
            <beans:bean class="com.yourpackagename.spring.web.servlet.view.jsonp.MappingJacksonJsonpView"/>
        </beans:list>
    </beans:property>
</beans:bean>

Пара замечаний, я бы не стал отсылать обратно весь дополнительный HTML-код и максимально сократил бы его, оставив суть работы.должно быть сделано поверх возвращаемых данных.Я провел много исследований и попробовал много подходов;этот выше работал лучше всего для меня.В качестве альтернативы и дополнительного прочтения я предлагаю следующее, поскольку именно они привели меня к этому окончательному решению (я не могу вспомнить их всех, иначе я бы отдал должное):

http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/

и для фильтрационного решения:

http://jpgmr.wordpress.com/2010/07/28/tutorial-implementing-a-servlet-filter-for-jsonp-callback-with-springs-delegatingfilterproxy/

Отключение

0 голосов
/ 24 октября 2011

Я обнаружил, что многие путают JSONP с JSON.Поэтому я делюсь ответом, который я нашел правильным:

Java

@RequestMapping(method = RequestMethod.GET, value = "ad")
public void getAd(HttpServletRequest request, HttpServletResponse response){
   ServletOutputStream out;
   try {
      out = response.getOutputStream();
      response.setContentType("text/javascript; charset=utf-8");
      out.println(request.getParameter("callback")+" ( {'html': '<strong>Hello World!</strong>' } )");
      out.close();
   } catch (IOException e) {
      e.printStackTrace();
   }
}

Javascript

var jsonp_url = "http://localhost:8080/AppName/ad?callback=?";
$.getJSON(jsonp_url, function(data) {
   $('#example-widget-container').html(data.html);
});
0 голосов
/ 12 октября 2011

что делает функция callme?если вы возвращаете "callme ({'html': 'hello world'})";тогда в вашем js-коде оповещение не будет предупреждать "Hellow world".Вам нужно иметь data (), т.е. запустить функцию callme

...