自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

借一古老技術(shù)考察你對(duì)SpringBoot掌握程度

開發(fā) 前端
我們的服務(wù)端又該實(shí)現(xiàn)呢?直接在對(duì)應(yīng)的接口中進(jìn)行修改嗎?如果直接修改接口,那么當(dāng)我又希望返回的是數(shù)據(jù)又該如何,重新再來一個(gè)接口嗎?

環(huán)境:Spring3.2.5

本篇文章將通過一個(gè)古老的技術(shù)JSONP來考察在座的對(duì)SpringBoot中某些技術(shù)的掌握程度。

1. 簡(jiǎn)介

JSONP(JSON with Padding)是一種非官方的協(xié)議,主要用于解決瀏覽器跨域數(shù)據(jù)訪問的問題。它利用HTML的<script>標(biāo)簽可以跨域加載資源的特性,通過服務(wù)器端生成包含JSON數(shù)據(jù)的JavaScript函數(shù)調(diào)用,并返回給客戶端執(zhí)行??蛻舳诵枰A(yù)先定義好回調(diào)函數(shù),以便在數(shù)據(jù)加載完畢后接收并處理數(shù)據(jù)。JSONP簡(jiǎn)單易用,但僅支持GET請(qǐng)求,且存在安全風(fēng)險(xiǎn),如XSS攻擊和CSRF攻擊。隨著技術(shù)的發(fā)展,CORS等更安全的跨域解決方案逐漸取代了JSONP。

關(guān)于JSONP的應(yīng)用示例

現(xiàn)有如下接口地址:http://localhost:9100/jsonps,返回?cái)?shù)據(jù)如下:

[{"id":1,"name":"張三"},{"id":2,"name":"李四"},{"id":3,"name":"王五"}]

JSONP需要我們傳遞一個(gè)類似回調(diào)的參數(shù),服務(wù)端拿到值后會(huì)將最終的響應(yīng)數(shù)據(jù)拼接成javascript函數(shù)調(diào)用的形式,如下:

<script src="http://localhost:9100/jsonps?callback=getUsers"></script>

通過<script>標(biāo)簽引用上面的即可地址,同時(shí)傳遞了callback參數(shù),當(dāng)請(qǐng)求到達(dá)服務(wù)端后會(huì)拿到callback參數(shù)對(duì)應(yīng)的getUsers值,與真正的數(shù)據(jù)做拼接,如下:

getUsers([{"id":1,"name":"張三"},{"id":2,"name":"李四"},{"id":3,"name":"王五"}]);

上面將是服務(wù)端響應(yīng)的最終結(jié)果。這就是javascript函數(shù)的調(diào)用,我們只要保證前端頁(yè)面中有g(shù)etUsers函數(shù)即可,它會(huì)自動(dòng)的執(zhí)行該函數(shù)。

以上就是JSONP實(shí)現(xiàn)的基本原理。

思考:我們的服務(wù)端又該實(shí)現(xiàn)呢?直接在對(duì)應(yīng)的接口中進(jìn)行修改嗎?如果直接修改接口,那么當(dāng)我又希望返回的是數(shù)據(jù)又該如何,重新再來一個(gè)接口嗎?

接下來我們通過HttpMessageConverter和ResponseBodyAdvice來實(shí)現(xiàn)即支持原始數(shù)據(jù)又支持JSONP格式的數(shù)據(jù)響應(yīng)。

2. 實(shí)戰(zhàn)案例

2.1 Rest接口定義

@RestController
@RequestMapping(("/jsonps"))
public class JsonpController {
  
  static List<User> DATAS = List.of(new User(1L, "張三"), new User(2L, "李四"), new User(3L, "王五")) ;
  
  @GetMapping("")
  public List<User> queryUsers() {
    return DATAS ;
  }
}

接口非常簡(jiǎn)單直接返回List集合。

2.2 自定義JSON包裝器

public class JsonpMappingJacksonValue extends MappingJacksonValue {


  private String jsonpFunction ;
  
  public JsonpMappingJacksonValue(Object value) {
    super(value);
  }
  // getters, setters
}

該類繼承了MappingJacksonValue,同時(shí)增加了jsonpFunction的屬性,后面會(huì)根據(jù)該屬性是否有值對(duì)結(jié)果進(jìn)行處理,如果沒有值則原始返回。而MappingJacksonValue類的作用就是一個(gè)POJO序列化到JSON時(shí)提供額外的序列號(hào)指令。

SpringBoot默認(rèn)響應(yīng)JSON數(shù)據(jù)是通過MappingJackson2HttpMessageConverter類,在該類中的writeInternal方法中會(huì)判斷當(dāng)前輸出的值是否是MappingJacksonValue,如果是最終也會(huì)獲取其中的Value進(jìn)行輸出客戶端的。

2.3 自定義ResponseBodyAdvice

@ControllerAdvice
public class JsonpControllerAdvice implements ResponseBodyAdvice<Object> {


  // 參數(shù)值必須滿足該正則
  private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
  // 參數(shù)名稱默認(rèn)callback,你也可以通過配置方式設(shè)置
  private String jsonpQueryParamName = "callback" ;
  
  @Override
  public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    // 只要轉(zhuǎn)換器是jackson(json數(shù)據(jù)輸出)
    // 當(dāng)然你也可以自定義實(shí)現(xiàn),比如:方法上有具體的某個(gè)注解等
    return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
  }
  @Override
  public Object beforeBodyWrite(
      Object body, MethodParameter returnType, 
      MediaType selectedContentType,
      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
      ServerHttpResponse response) {
    // 創(chuàng)建MappingJacksonValue對(duì)象(包裝原始的數(shù)據(jù))
    JsonpMappingJacksonValue container = this.getOrCreateContainer(body) ;
    // 取得請(qǐng)求的callback參數(shù)值
    HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest() ;
    String value = servletRequest.getParameter(jsonpQueryParamName) ;
    // 如果不存在直接返回,不做任何處理
    if (value != null) {
      // 不滿足條件也直接返回
      if (!CALLBACK_PARAM_PATTERN.matcher(value).matches()) {
        return container ;
      }
      // 設(shè)置響應(yīng)頭為:application/javascript;charset=utf-8
      MediaType contentTypeToUse = new MediaType("application", "javascript", StandardCharsets.UTF_8) ;
      response.getHeaders().setContentType(contentTypeToUse) ;
      // 設(shè)置jsonp函數(shù)名,后面就會(huì)根據(jù)該值判斷是否要進(jìn)行處理
      container.setJsonpFunction(value) ;
    }
    return container ;
  }
  // ...
}

自定義ResponseBodyAdvice的作用是將返回客戶端的數(shù)據(jù)包裝為MappingJacksonValue對(duì)象,然后設(shè)置jsonp會(huì)調(diào)用函數(shù)名。

接下來就是最重要的,如何在寫入客戶端時(shí),將數(shù)據(jù)改造成JSONP所需要的格式。

2.4 重寫HttpMessageConverter

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
  MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter() {
    protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
      // 我們上面設(shè)置的值在這里用上了,關(guān)鍵就在該值是否有
      // 只有有值的情況下我們才會(huì)進(jìn)行JSONP的處理
      String jsonpFunction =
          (object instanceof JsonpMappingJacksonValue ? ((JsonpMappingJacksonValue) object).getJsonpFunction() : null);
      if (jsonpFunction != null) {
        generator.writeRaw("/**/");
        generator.writeRaw(jsonpFunction + "(");
      }
    }
    protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
      String jsonpFunction =
          (object instanceof JsonpMappingJacksonValue ? ((JsonpMappingJacksonValue) object).getJsonpFunction() : null);
      if (jsonpFunction != null) {
        generator.writeRaw(");") ;
      }
    }
  } ;
  return converter ;
}

在這里我們自定義了MappingJackson2HttpMessageConverter 的writePrefix和writeSuffix方法,這兩個(gè)方法都進(jìn)行判斷,如果期望輸出的是JSONP格式才會(huì)進(jìn)行數(shù)據(jù)處理。

到此就完成了所有處理過程,每一步你都懂嗎?

說明:本篇文章不是教你實(shí)現(xiàn)JSONP這個(gè)技術(shù)并使用它,JSONP本就是用來解決跨域的問題,我用CORS技術(shù)不比它簡(jiǎn)單,安全。這里只是借用這個(gè)JSONP來檢驗(yàn)?zāi)銓?duì)其它知識(shí)的掌握程度。

驗(yàn)證上面的代碼

不使用callback參數(shù)請(qǐng)求

圖片圖片

使用callback參數(shù)請(qǐng)求

圖片圖片

成功,當(dāng)你的頁(yè)面中有g(shù)etUsers方法時(shí),會(huì)自動(dòng)調(diào)用getUsers方法。

通過HTML頁(yè)面進(jìn)行測(cè)試

<html>
  <head>
    <script>
      function getUsers(users) {
        alert(JSON.stringify(users))
      }
    </script>
    <script src="http://localhost:9100/jsonps?callback=getUsers"></script>
  </head>
</html>

訪問上面的頁(yè)面,輸出結(jié)果:

圖片 圖片

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2024-11-01 08:34:18

Spring配置@Bean

2015-09-15 09:50:12

2020-11-09 07:25:20

函數(shù) JavaScript數(shù)據(jù)

2021-10-21 08:13:11

Springboot

2018-02-08 10:47:19

存儲(chǔ)技術(shù)列存儲(chǔ)

2020-12-10 11:00:37

JavaJVM命令

2015-09-29 09:24:22

Node.js面試題

2023-03-23 08:11:59

2022-02-18 07:32:13

Linux項(xiàng)目代碼

2023-10-24 11:44:21

2020-05-06 14:54:59

技術(shù)人工智能大數(shù)據(jù)

2018-02-02 16:41:01

程序員編程Web

2024-04-12 09:01:08

2019-09-29 15:30:58

JavaScript框架V8

2016-12-26 16:34:41

技術(shù)

2010-08-04 15:01:00

2020-02-03 13:55:49

技術(shù)研發(fā)指標(biāo)

2022-07-24 09:56:40

大數(shù)據(jù)技術(shù)

2019-04-03 15:07:11

便利店物聯(lián)網(wǎng)傳感器

2015-08-17 15:12:56

新技術(shù)語(yǔ)言框架
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)