精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何使用Java和Spring Boot創(chuàng)建短鏈接生成器

譯文
開發(fā) 前端
本文以完整示例的形式,詳細(xì)介紹了如何使用Java和Spring Boot,來創(chuàng)建URL短鏈接生成器。

[[433165]]

【51CTO.com快譯】URL短鏈接生成器是一種根據(jù)冗長的URL,創(chuàng)建短鏈接的服務(wù)。通常,短鏈接的長度只有原始URL的三分之一、甚至四分之一。因此它們更容易被輸入、呈現(xiàn)、以及推送。用戶只需單擊短鏈接,便可被自動(dòng)重定向到原始的URL處。

目前,tiny.cc、bitly.com和cutt.ly都能夠提供在線式的URL縮短服務(wù)。當(dāng)然,您也可以為應(yīng)用系統(tǒng)自行設(shè)計(jì)和開發(fā)出縮短URL的服務(wù)。下面,我和您討論具體的實(shí)現(xiàn)過程。首先,讓我們來探討一下與之相關(guān)的功能性和非功能性的需求。

功能要求:

  • 保存用戶輸入的長URL,并據(jù)此生成相應(yīng)的短鏈接。
  • 允許用戶選擇到期日期,以便生成的短鏈接在該日期后自動(dòng)無效。
  • 方便用戶在單擊短鏈接后,重定向到原始的長鏈接處。
  • 作為可選的方式,允許用戶創(chuàng)建服務(wù)帳戶,并讓生成的短鏈接僅對該賬戶有效。
  • 以可選的方式,允許用戶自行創(chuàng)建短鏈接。
  • 以可選的方式,允許用戶標(biāo)記出那些最常訪問的鏈接。

非功能性要求:

  • 生成服務(wù)具有持續(xù)的有效性和可訪問性。
  • 重定向的用時(shí)應(yīng)不超過2秒。

URL轉(zhuǎn)換的方式

URL短鏈接生成器中最重要的是轉(zhuǎn)換算法。不同的轉(zhuǎn)換方式通常會(huì)產(chǎn)生不同的輸出,而且它們各有優(yōu)、缺點(diǎn)。假設(shè)我們需要一個(gè)最長為7個(gè)字符的短鏈接。那么我們可以采用MD5SHA-2之類的哈希函數(shù),對原始的URL進(jìn)行散列處理。由于散列的結(jié)果會(huì)超過7個(gè)字符,因此我們只取前7個(gè)字符。不過,由于前7個(gè)字符可能已經(jīng)被用于其他短鏈接,并由此會(huì)引發(fā)沖突,因此我們需要依次截取后面的7個(gè)字符,直至找到一個(gè)被使用過的短鏈接為止。

生成短鏈接的第二種方法是使用UUID。UUID被復(fù)制的概率近似為零,因此可以完全忽略沖突的可能。由于UUID是由36個(gè)字符組成,仍然可能遇到上述問題,因此我們應(yīng)當(dāng)截取前7個(gè)字符,然后檢查該組合是否已被占用。

第三種方法是將數(shù)字從Base 10轉(zhuǎn)換為Base 62。Base是可用于表示特定數(shù)字的字符數(shù)。Base 10是我們?nèi)粘I钪惺褂玫臄?shù)字,即:[0-9],而Base 62則是:[0-9][az][AZ]。這意味著,以10為Base的四位數(shù)字,將與以62為Base、但具有兩個(gè)字符的數(shù)字相同。因此在URL轉(zhuǎn)換中,使用最大長度為7個(gè)字符的Base 62,將允許我們?yōu)槎替溄犹峁?2^7個(gè)唯一值。

Base 62的轉(zhuǎn)換機(jī)制

我使用如下算法,將一個(gè)Base為10的數(shù)字轉(zhuǎn)換為Base為62:

  1. while(number > 0)     
  2. remainder = number % 62     
  3. number = number / 62     
  4. attach remainder to start of result collection 

據(jù)此,我們只需要將結(jié)果集中的數(shù)字映射到Base為62的字符 [0,1,2,...,a,b,c...,A,B,C,...]即可。

下面,我通過將1000從Base 10轉(zhuǎn)換為Base 62的例子,來討論其工作機(jī)制。

  1. 1st iteration: 
  2.          number = 1000 
  3.          remainder = 1000 % 62 = 8 
  4.          number = 1000 / 62 = 16 
  5.          result list = [8] 
  6. 2nd iteration: 
  7.          number = 16 
  8.          remainder = 16 % 62 = 16 
  9.          number = 16 / 62 = 0 
  10.          result list = [16,8] 
  11.          There is no more iterations since number = 0 after 2nd iteration 

[16,8] 被映射到Base 62后為g8,即1000base10 = g8base62。

而從Base 62轉(zhuǎn)換為Base 10的過程也很簡單,即:

  1. i = 0     
  2. while(i < inputString lenght) 
  3.          counter = i + 1 
  4.          mapped = base62alphabet.indexOf(inputString[i]) // map character to number based on its index in alphabet          
  5.          result = result + mapped * 62^(inputString lenght - counter) 
  6.          i++ 

所以其對應(yīng)的代碼示例為:

  1. inputString = g8     
  2. inputString length = 2     
  3. i = 0     
  4. result = 0 
  5. 1st iteration 
  6.         counter = 1 
  7.         mapped = 16 // index of g in base62alphabet is 16 
  8.         result = 0 + 16 * 62^1 = 992 
  9. 2nd iteration 
  10.         counter = 2 
  11.         mapped = 8 // index of 8 in base62alphabet is 8 
  12.         result = 992 + 8 * 62^1 = 1000 

實(shí)現(xiàn)

我使用Spring Boot和MySQL來實(shí)現(xiàn)該服務(wù)。請參看我在Github上的具體代碼。我用到了數(shù)據(jù)庫的自動(dòng)遞增功能來實(shí)現(xiàn)Base 62的轉(zhuǎn)換。當(dāng)然,您也可以使用任何其他具有自動(dòng)遞增功能的數(shù)據(jù)庫。

首先,請?jiān)L問Spring initializr,并選擇Spring Web與MySQL Driver。接著,請單擊“生成(Generate)”按鈕,并下載對應(yīng)的zip文件。完成解壓縮之后,我們就可以在自己的IDE中打開該項(xiàng)目了。

我通過創(chuàng)建文件夾:控制器、實(shí)體、服務(wù)、存儲(chǔ)庫、dto和配置,實(shí)現(xiàn)在邏輯上劃分程序代碼。

在“實(shí)體”文件夾中,我創(chuàng)建了一個(gè)具有id、longUrl、createdDate和expiresDate四個(gè)屬性的Url.java類

請注意,此處既沒有短鏈接的屬性,也不會(huì)去保存短鏈接。每次只要有GET請求的出現(xiàn),我們都會(huì)將id屬性從Base 10轉(zhuǎn)換為Base 62,以便節(jié)省數(shù)據(jù)庫中的空間。

用戶在訪問該短鏈接時(shí),應(yīng)根據(jù)longURL屬性重定向到目標(biāo)網(wǎng)站。createdDate則只是為了查看longURL何時(shí)被保存(并不重要)。而如果用戶希望在一段時(shí)間后讓短鏈接失效的話,可以對expiresDate進(jìn)行設(shè)置。

接著,我在“服務(wù)”文件夾中,創(chuàng)建了一個(gè)BaseService.java文件。其中包含了從Base 10到Base 62相互轉(zhuǎn)換的方法。

  1. private static final String allowedString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  2. private char[] allowedCharacters = allowedString.toCharArray(); 
  3. private int base = allowedCharacters.length; 

正如前面所提到的,若要使用Base 62轉(zhuǎn)換,則需要有一個(gè)被稱為allowedCharacters的Base 62的字母表。此外,為了方便按需更改被允許的字符,我們可根據(jù)字符的長度,計(jì)算出基本變量的值。其中,編碼(encode)方法會(huì)將一個(gè)數(shù)字作為輸入,返回一個(gè)短鏈接;而解碼(decode)方法則會(huì)接受一個(gè)字符串(如:短鏈接)作為輸入,并返回一個(gè)數(shù)字。

在存儲(chǔ)庫文件夾中,我創(chuàng)建了UrlRepository.java文件。它只是JpaRepository的一個(gè)擴(kuò)展,并給出了諸如“findById”,“save”等方法。在此,我們無需進(jìn)行任何添加。

然后,我在“控制器”文件夾中創(chuàng)建了一個(gè)URLController.java文件(請參見如下代碼)。它提供一種用于創(chuàng)建短鏈接的POST方法,以及一種被用于重定向到原始URL的GET方法。

  1.  @PostMapping("create-short"
  2.     public String convertToShortUrl(@RequestBody UrlLongRequest request) { 
  3.         return urlService.convertToShortUrl(request); 
  4.     } 
  5.  
  6.     @GetMapping(value = "{shortUrl}"
  7.     public ResponseEntity<Void> getAndRedirect(@PathVariable String shortUrl) { 
  8.         var url = urlService.getOriginalUrl(shortUrl); 
  9.         return ResponseEntity.status(HttpStatus.FOUND) 
  10.         .location(URI.create(url)) 
  11.         .build(); 

其中,POST方法會(huì)將UrlLongRequest作為請求體。它是一個(gè)具有l(wèi)ongURL和expiresDate屬性的類。而GET方法會(huì)將一個(gè)短的URL作為路徑變量,以獲取并重定向到原始的URL處。

在控制器的上層,urlService會(huì)作為依賴項(xiàng)被注入,以便后續(xù)進(jìn)行解釋。

UrlService.java既包含了大量邏輯,又為控制器提供了服務(wù)。ConvertToShortUrl僅供控制器的POST方法所使用。它只是在數(shù)據(jù)庫中創(chuàng)建了一條新的記錄,并獲取一個(gè)id,以便將其轉(zhuǎn)換為Base 62的短鏈接,并返回給控制器。

控制器使用GetOriginalUrl方法,首先將字符串轉(zhuǎn)換為Base 10類型的id。然后,它通過該id從數(shù)據(jù)庫中獲取一條記錄。當(dāng)然,如果該記錄不存在的話,則會(huì)拋出異常。最后,它會(huì)將原始的URL返回給控制器。

下面,我將和您討論Swagger文檔、應(yīng)用的dockerization(容器化)、緩存以及MySQL的計(jì)劃事件。

Swagger的用戶界面

在開發(fā)過程中文檔記錄無疑能夠使得API更易于理解和使用。在該項(xiàng)目中,我使用Swagger UI來記錄API。Swagger UI允許任何人在沒有任何實(shí)現(xiàn)邏輯的情況下,可視化API資源,并與之交互。它不但能夠自動(dòng)生成,而且?guī)в锌梢暬奈臋n,以便于后端的實(shí)現(xiàn)和客戶端的使用。

我通過執(zhí)行如下步驟,在項(xiàng)目中引入了Swagger UI。首先,我在pom.xml文件中添加了Maven依賴項(xiàng):

  1. XML 
  2. <dependency> 
  3.   <groupId>io.springfox</groupId> 
  4.   <artifactId>springfox-swagger2</artifactId> 
  5.   <version>2.9.2</version> 
  6. </dependency> 
  7. <dependency> 
  8.   <groupId>io.springfox</groupId> 
  9.   <artifactId>springfox-swagger-ui</artifactId> 
  10.   <version>2.9.2</version> 
  11. </dependency> 

添加了Maven依賴項(xiàng)后,我們便可以添加Swagger的相關(guān)配置了。我在“配置”文件夾中,創(chuàng)建了一個(gè)新的類--SwaggerConfig.java,請參考如下代碼段。

Java

  1.     @Configuration 
  2.     @EnableSwagger2 
  3.     public class SwaggerConfig { 
  4.  
  5.     @Bean     
  6.     public Docket apiDocket() {    
  7.         return new Docket(DocumentationType.SWAGGER_2)   
  8.             .apiInfo(metadata())     
  9.             .select()     
  10.             .apis(RequestHandlerSelectors.basePackage("com.amarin"))     
  11.             .build();     
  12.     } 
  13.                
  14.     private ApiInfo metadata(){ 
  15.         return new ApiInfoBuilder() 
  16.         .title("Url shortener API")     
  17.         .description("API reference for developers")     
  18.         .version("1.0")     
  19.         .build();     
  20.         }   

在該類的頂部,我添加了如下注釋:

  • @Configuration表示一個(gè)類聲明了一到多個(gè)@Beans方法,并且可以由Spring容器通過處理,在運(yùn)行時(shí)為這些bean生成相應(yīng)的定義和服務(wù)請求。
  • @EnableSwagger2表示應(yīng)該啟用Swagger支持。

接下來,我添加了Docket bean。它提供的主要API配置,帶有各種合理的默認(rèn)值、以及便捷的配置方法。

此處的apiInfo()方法除了可以使用默認(rèn)值,還能夠接受ApiInfo對象,以便我們配置所有必要的API信息。為了使代碼更加簡潔,我們可以創(chuàng)建一個(gè)私有的方法—metadata(),來配置和返回ApiInfo對象,并將該方法作為apiInfo()方法的參數(shù)進(jìn)行傳遞。同時(shí),apis()方法也允許我們過濾那些被文檔化的包。

在完成了Swagger UI的配置后,我們便可以開始文檔化API了。在UrlController內(nèi)部的每個(gè)端點(diǎn)上,我們可以使用@ApiOperation來添加描述性的注釋。當(dāng)然,您也可以按需使用其他類型的注釋

我們還可以文檔化DTO,并使用@ApiModelProperty來添加各種允許的值和描述。

緩存

根據(jù)維基百科的定義,緩存是存儲(chǔ)數(shù)據(jù)的軟、硬件組件,可用來更快地處理后續(xù)對于相同數(shù)據(jù)的請求。而存儲(chǔ)在緩存中的數(shù)據(jù),往往是早期計(jì)算的結(jié)果、或是已存儲(chǔ)在其他地方的數(shù)據(jù)副本。

目前,最常用的緩存類型是內(nèi)存緩存(in-memory cache)。它能夠?qū)⒕彺娴臄?shù)據(jù)存儲(chǔ)到RAM中。當(dāng)被請求數(shù)據(jù)與緩存一致時(shí),它是從RAM、而非從數(shù)據(jù)庫被提取。據(jù)此,我們避免頻繁調(diào)用后端的開銷。

由于URL短鏈接生成器可以被認(rèn)為是一種讀取多于寫入的請求應(yīng)用,因此它是使用緩存的理想應(yīng)用場景。若想在Spring Boot應(yīng)用中啟用緩存,我們只需要在UrlShortenerApiApplication類中添加@EnableCaching注釋即可。

接著,在控制器中,我們需要在GET方法上設(shè)置@Cachable注解,以實(shí)現(xiàn)自動(dòng)將方法調(diào)用的結(jié)果存入緩存中。在@Cachable的注解中,我設(shè)置了緩存名稱的value參數(shù)和緩存鍵的key參數(shù)。鑒于緩存鍵的唯一性,我使用了“shortUrl”,并將Sync參數(shù)設(shè)置為true,以確保只有一個(gè)線程正在構(gòu)建緩存值。

至此,當(dāng)我們首次加載帶有短鏈接的URL時(shí),其結(jié)果將會(huì)被保存到緩存中。后續(xù),任何端點(diǎn)若想調(diào)用相同短鏈接,都會(huì)從緩存、而非從數(shù)據(jù)庫中檢索結(jié)果。

Dockerization

Dockerization是將應(yīng)用程序及其依賴項(xiàng)打包到Docker容器中的過程。一旦配置了Docker容器,我們便可以輕松地在任何支持Docker的服務(wù)器、或主機(jī)上運(yùn)行應(yīng)用程序。

因此,我們首先需要?jiǎng)?chuàng)建一個(gè)包含所有命令的Dockerfile文本文件,以便用戶通過調(diào)用命令行的方式,掛載某個(gè)鏡像。

Dockerfile

  1. FROM openjdk:13-jdk-alpine    
  2.     COPY ./target/url-shortener-api-0.0.1-SNAPSHOT.jar /usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jar     
  3.     EXPOSE 8080     
  4. ENTRYPOINT ["java","-jar","/usr/src/app/url-shortener-api-0.0.1-SNAPSHOT.jar"
  • FROM:表示需要構(gòu)建的基礎(chǔ)鏡像。我使用的是Java免費(fèi)開源版--OpenJDK v13。您也可以在共享的Docker鏡像平臺(tái)--Docker hub(https://hub.docker.com/)上,找到其他類型base鏡像。
  • COPY:此命令會(huì)將文件從本地文件系統(tǒng),復(fù)制到指定路徑的容器文件系統(tǒng)中。在此,我將目標(biāo)文件夾中的JAR文件,復(fù)制到容器中的/usr/src/app文件夾中(稍后我將解釋如何創(chuàng)建JAR文件)。
  • EXPOSE:負(fù)責(zé)通知Docker容器在運(yùn)行時(shí),偵聽指定網(wǎng)絡(luò)端口的指令。其默認(rèn)協(xié)議為TCP,您也可以使用UDP。
  • ENTRYPOINT:負(fù)責(zé)配置可執(zhí)行的容器。在此,我通過命令為“java -jar .jar”,指定Docker將如何運(yùn)行一個(gè).jar文件類型的應(yīng)用程序。

為了在項(xiàng)目中創(chuàng)建.jar文件,以便Dockerfile中的COPY命令能夠正常工作,我使用Maven來創(chuàng)建可執(zhí)行的.jar。如果您的pom.xml缺少M(fèi)aven,請用如下方式進(jìn)行添加:

XML

  1. <build>      
  2.     <plugins>      
  3.         <plugin>      
  4.             <groupId>org.springframework.boot</groupId>      
  5.             <artifactId>spring-boot-maven-plugin</artifactId>      
  6.         </plugin>      
  7.     </plugins>      
  8. </build>  

隨后,我運(yùn)行命令:mvn clean package,以構(gòu)建出一個(gè)Docker鏡像。接著,在Dockerfile文件夾中,我運(yùn)行了命令:docker build -t url-shortener:latest。其中,-t可用于標(biāo)記一個(gè)鏡像,并實(shí)現(xiàn)版本控制。在此,即為最新的存儲(chǔ)庫URL-shortener。我們可以使用命令“docker images”來創(chuàng)建鏡像。屏幕上的顯示結(jié)果為:

最后,我還需要在docker容器中構(gòu)建MySQL服務(wù)器鏡像,以方便數(shù)據(jù)庫容器與應(yīng)用容器相隔離。為此,我在Docker容器中運(yùn)行了如下命令:

  1. $ docker run --name shortener -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 3306:3306 mysql:8 

您可以在Docker hub上查看到相關(guān)文檔。

為了在容器內(nèi)運(yùn)行數(shù)據(jù)庫,我通過配置,將現(xiàn)有的應(yīng)用程序連接上該MySQL服務(wù)器。即:在application.properties中設(shè)置spring.datasource.url,以連接到shortener容器。

然后,我使用以下命令來運(yùn)行已構(gòu)建好的Docker 鏡像容器:

  1. docker run -d –-name url-shortener-api -p 8080:8080 --link shortener url-shortener 
  • -d表示Docker容器在終端的后臺(tái)運(yùn)行。
  • --name可設(shè)置容器的名稱。
  • -p host-port:docker-port:是將本地端口映射到容器內(nèi)的端口上。在本例中,我在容器內(nèi)公開了端口8080,并映射到了本地的8080上。
  • --link:用于鏈接應(yīng)用容器與數(shù)據(jù)庫容器,以實(shí)現(xiàn)容器間的相互發(fā)現(xiàn)和安全傳輸。
  • url-shortener:則指明了待運(yùn)行的Docker鏡像名稱。

至此,我們便可以在瀏覽器中訪問http://localhost:8080/swagger-ui.html了。通過將鏡像發(fā)布到Docker Hub上,任何計(jì)算機(jī)和服務(wù)器都可以輕松地運(yùn)行該應(yīng)用。

當(dāng)然,為了改善該Docker的使用體驗(yàn),我們需要注意多階段構(gòu)建,以及docker-compose兩個(gè)方面。

多階段構(gòu)建

使用多階段構(gòu)建,您將可以在Dockerfile中使用多個(gè)FROM語句。每個(gè)FROM指令都可以使用不同的base,并且每個(gè)指令都能夠開啟構(gòu)建的新階段。您可以有選擇性地將各個(gè)工件(artifacts)從一個(gè)階段復(fù)制到另一個(gè)階段,并在最終鏡像中去掉不想要的內(nèi)容。

多階段構(gòu)建有利于我們避免每次對代碼進(jìn)行更改后,都必須手動(dòng)重建.jar文件。據(jù)此,我們可以定義一個(gè)構(gòu)建階段,來執(zhí)行Maven包命令。而另一個(gè)階段會(huì)將來自第一次構(gòu)建的結(jié)果,直接復(fù)制到Docker容器的文件系統(tǒng)中。您可以通過鏈接--https://github.com/AnteMarin/UrlShortener-API/blob/develop/Dockerfile,查看完整的Dockerfile。

Docker-compose

Compose是一個(gè)用于定義和運(yùn)行多容器Docker應(yīng)用的工具。借助Compose,您可以使用YAML文件,來配置應(yīng)用程序的服務(wù),然后使用單個(gè)命令,從配置中創(chuàng)建并啟動(dòng)所有的服務(wù)。

使用docker-compose,我們能夠?qū)?yīng)用程序和數(shù)據(jù)庫打包到一個(gè)配置文件中,以便立即運(yùn)行所有的內(nèi)容。據(jù)此,我們避免了每次去運(yùn)行MySQL容器,將其鏈接到應(yīng)用容器的繁瑣。

Docker-compose.yml文件的具體配置內(nèi)容可知:首先,我們通過設(shè)置鏡像mysql v8.0和MySQL服務(wù)器的憑據(jù),來配置MySQL容器。接著,我們通過設(shè)置構(gòu)建參數(shù),來配置應(yīng)用容器,畢竟我們需要的是鏡像,而非使用MySQL進(jìn)行拉取。此外,我們還需要通過設(shè)置,讓應(yīng)用容器依賴于MySQL容器。最終,我們可以使用命令“docker-compose up”,來運(yùn)行整個(gè)項(xiàng)目。

MySQL計(jì)劃事件(Scheduled Event)

說到短鏈接的到期設(shè)置,我們既可以讓用戶自定義,又可以保持默認(rèn)值。為此,我們可以在數(shù)據(jù)庫中設(shè)置一個(gè)計(jì)劃事件。通過每x分鐘運(yùn)行一次該事件,到期時(shí)間只要小于當(dāng)前時(shí)間,數(shù)據(jù)庫就會(huì)自動(dòng)刪除某一行,就這么簡單。這非常適用于保持?jǐn)?shù)據(jù)庫中的少量數(shù)據(jù)。不過,該方法有兩個(gè)問題值得注意:

  • 首先,該事件只會(huì)從數(shù)據(jù)庫中刪除記錄,而不會(huì)從緩存中刪除數(shù)據(jù)。如前所述,如果緩存可以找到匹配的數(shù)據(jù)的話,就不會(huì)去查看數(shù)據(jù)庫。因此,某條短鏈接即便已經(jīng)在數(shù)據(jù)庫中被刪除了,我們?nèi)匀豢梢詮木彺嬷蝎@取它。
  • 其次,在示例腳本中,我設(shè)置該事件為每隔2分鐘運(yùn)行一次。如果數(shù)據(jù)庫的記錄變動(dòng)較大,則可能出現(xiàn)前一個(gè)事件尚未在其預(yù)定的間隔周期內(nèi)執(zhí)行完畢,后一個(gè)事件已被觸發(fā),進(jìn)而出現(xiàn)多個(gè)事件實(shí)例同時(shí)在執(zhí)行的混亂局面。

小結(jié)

通過上述示例和討論,我向您展示了如何使用Java和Spring Boot,來創(chuàng)建URL短鏈接生成器的API。這是一個(gè)十分常見的面試問題,您既可以據(jù)此創(chuàng)建自己的改進(jìn)版本,又可以從上述GitHub處克隆項(xiàng)目的存儲(chǔ)庫,并創(chuàng)建自己的前端。

原文標(biāo)題:URL Shortener Complete Tutorial,作者:Ante Marin

【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】

 

責(zé)任編輯:華軒 來源: 51CTO
相關(guān)推薦

2022-12-15 08:49:58

ReactQR生成器

2015-06-24 10:10:38

C#短鏈接生成

2022-10-17 18:29:55

2024-12-23 08:00:00

2017-11-22 10:53:22

2025-11-11 09:54:38

2024-11-01 15:51:06

2021-12-10 09:45:19

生成器配置代碼

2023-03-01 00:07:32

JavaScript迭代器生成器

2010-09-07 16:31:17

SQL語句insert

2011-12-23 13:42:05

JavaScript

2021-04-22 21:15:38

Generator函數(shù)生成器

2017-07-01 16:02:39

分布式ID生成器

2015-08-25 11:07:58

2025-01-23 08:36:27

CSS開發(fā)工具

2022-02-22 08:20:04

React工具PDF 文件

2023-05-17 16:02:00

CSS工具代碼生成器

2017-09-06 09:26:03

Python生成器協(xié)程

2021-07-23 11:24:54

Create Inc開源G代碼生成器

2024-08-19 00:00:00

表單生成器開發(fā)開源
點(diǎn)贊
收藏

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

欧美日韩一区二区三区四区不卡| 囯产精品一品二区三区| 国产精品一在线观看| 欧美无乱码久久久免费午夜一区| 一区二区精品国产| jlzzjlzzjlzz亚洲人| 99精品福利视频| 这里精品视频免费| 欧美美女18p| 视频一区不卡| 99草在线视频| 老司机一区二区三区| 中文字幕视频一区二区在线有码| 两女双腿交缠激烈磨豆腐| 欧亚av在线| 亚洲狠狠丁香婷婷综合久久久| 国产在线一区二| 91亚洲视频在线观看| 国产乱码精品| 欧美激情一二区| 麻豆视频免费在线播放| 狼人精品一区二区三区在线| 91精品国产色综合久久ai换脸 | 91精品国产调教在线观看| 亚洲国产成人av在线| 天天干天天av| 香蕉成人影院| 精品日韩中文字幕| 在线观看日韩羞羞视频| 国产视频福利在线| av资源网一区| 99久久综合狠狠综合久久止| 中文字幕乱码中文字幕| 久久av最新网址| 欧美精品第一页在线播放| 我要看黄色一级片| 一级做a爱片久久毛片| 韩国精品视频在线观看 | 大陆极品少妇内射aaaaaa| 撸视在线观看免费视频| 99久久99久久精品国产片果冻 | 97色伦亚洲国产| 国产三级国产精品国产国在线观看| 国产欧美一区二区精品久久久| 精品国产伦理网| 中文写幕一区二区三区免费观成熟| 国产精品亚洲d| 91久久精品一区二区三区| 18岁网站在线观看| 国产不卡人人| 五月天中文字幕一区二区| 激情五月婷婷六月| 99在线视频观看| 亚洲精品国久久99热| 大桥未久一区二区三区| 秋霞午夜理伦电影在线观看| 国产精品国产三级国产有无不卡| 日韩欧美亚洲v片| 四虎精品在线| 久久亚洲私人国产精品va媚药| 精品国产乱码一区二区三区四区| 亚洲伦理在线观看| 成人sese在线| 韩国成人av| 日韩电影网址| 国产欧美综合色| 五月天久久狠狠| 男女啪啪在线观看| 亚洲欧美日韩综合aⅴ视频| 懂色av粉嫩av蜜臀av| 18videosex性欧美麻豆| 一二三区精品福利视频| 男女超爽视频免费播放| 久草在线中文最新视频| 日本高清视频一区二区| 浓精h攵女乱爱av| 少妇高潮毛片色欲ava片| 加勒比一区二区三区在线| 国产午夜精品一区二区三区视频 | 国产精品秘入口| 国产精品五月天| 国产大尺度在线观看| 黄色成人在线网| 狠狠久久亚洲欧美专区| 不卡av免费在线| 99久久久成人国产精品| 精品精品国产高清一毛片一天堂| 国产精品无码网站| 欧美日韩精品一区二区视频| 久久综合伊人77777| 日本一区二区免费在线观看| 日韩精品久久久久久| 成人激情综合网| 神马一区二区三区| 国产日本一区二区| 97久久国产亚洲精品超碰热| 涩涩视频网站在线观看| 欧美高清视频一二三区 | 又骚又黄的视频| 国产很黄免费观看久久| 欧美一级片免费观看| 国内精品久久久久国产| 欧美日韩免费在线观看| 五月天视频在线观看| 久久精品国产亚洲5555| 中文字幕欧美日韩在线| 精品在线视频观看| 另类成人小视频在线| 精品久久久久久亚洲| 免费a级在线播放| 欧美日韩亚洲精品内裤| 亚洲图片 自拍偷拍| 天堂资源在线亚洲| 不卡毛片在线看| 亚洲中文一区二区| 成人免费av资源| 一本一道久久a久久精品综合| 羞羞视频在线观看免费| 成人香蕉视频| 在线一区二区观看| 欧亚乱熟女一区二区在线| 91综合久久一区二区| 1769国产精品| 秋霞网一区二区| 综合色中文字幕| 九色porny91| 狼人精品一区二区三区在线 | 久久久久久久有限公司| av免费看在线| 欧美精品日韩一本| 欧美 日韩 国产 成人 在线观看 | 亚洲精品午夜在线观看| 亚洲动漫在线观看| 久久露脸国产精品| 99在线小视频| 日韩理论片在线| 日韩欧美国产片| 清纯唯美综合亚洲| 国产精品99久久久久久久久久久久| 色网站免费观看| 亚洲最大色网站| 在线中文字日产幕| 欧美激情1区| 91在线播放国产| 成人在线观看亚洲| 91精品国产综合久久小美女| 精品一区二区在线观看视频| 免费在线观看视频一区| 欧美亚洲爱爱另类综合| 深夜在线视频| 精品无人区乱码1区2区3区在线 | 在线观看国产精品一区| 久久久夜夜夜| 日韩久久久久久久| 福利一区二区免费视频| 色婷婷久久av| 一区二区三区黄| 国产精品久久久久久久久免费丝袜| 少妇黄色一级片| 久久综合99| 91在线视频导航| 精品亚洲视频在线| 亚洲一区二区三区四区电影| 久久中文久久字幕| 99视频在线观看免费| 亚洲精品va在线观看| www.欧美com| 在线观看一区| 精品一区久久久久久| 一区二区三区四区日本视频| 亚洲性av网站| 91在线你懂的| 夜色激情一区二区| 亚洲av片不卡无码久久| 视频精品一区二区| 一级二级三级欧美| 亚洲国产高清在线观看| 韩国19禁主播vip福利视频| 欧美 日韩 综合| 色偷偷成人一区二区三区91| 国产不卡在线观看视频| 国产精品一级在线| 玩弄中年熟妇正在播放| 国产欧美久久一区二区三区| 成人在线中文字幕| aa级大片免费在线观看| 亚洲图片在线综合| 99久久精品免费看国产交换| 性久久久久久久久| 国产一二三四区在线| 亚洲爱情岛论坛永久| 国产亚洲精品aa| 日韩不卡的av| 午夜亚洲精品| 国产又大又长又粗又黄| 国偷自产av一区二区三区| 青草热久免费精品视频| 嫩草在线视频| 日韩电影在线看| 欧美精三区欧美精三区| www.涩涩爱| 成人小视频在线| 在线观看国产中文字幕| 欧美三区不卡| 日韩欧美第二区在线观看| 欧美日韩卡一| 97精品一区二区视频在线观看| 97电影在线观看| 亚洲成人黄色在线| 一级黄色录像大片| 欧美日韩国产中字| 亚洲精品电影院| 久久女同精品一区二区| 国偷自产av一区二区三区麻豆| 亚洲一区在线观| 久久精品一区二区三区不卡牛牛 | 亚洲天堂免费观看| 国产suv精品一区二区69| 色综合久久久久综合99| 久久久久成人网站| 亚洲欧洲色图综合| 免费在线观看污| 成人av电影在线| 亚洲丝袜在线观看| 久久99久久99精品免视看婷婷| 俄罗斯av网站| 日韩午夜高潮| 热久久最新地址| 久久久人成影片免费观看| 少妇免费毛片久久久久久久久| 极品束缚调教一区二区网站| 99re热精品| 日韩区一区二| 成人亚洲欧美一区二区三区| 日韩高清在线| 97在线视频免费| 国产一二区在线| 日韩中文娱乐网| av男人的天堂在线| 一区二区三区亚洲| 激情小视频在线观看| 精品视频久久久久久久| 婷婷伊人综合中文字幕| 精品久久国产老人久久综合| 亚洲AV无码精品国产| 91精品国产黑色紧身裤美女| 国产一区二区三区四区视频| 欧美日韩一区二区三区四区五区| 国产一级片一区二区| 91福利视频久久久久| 一级一片免费看| 色综合天天狠狠| 潘金莲一级淫片aaaaaa播放| 91黄视频在线| 高潮无码精品色欲av午夜福利| 欧美在线综合视频| 性高潮视频在线观看| 欧美中文字幕一二三区视频| 影音先锋黄色网址| 欧美日韩国产片| 亚洲午夜激情视频| 777午夜精品视频在线播放| 无码日韩精品一区二区| 欧美专区在线观看一区| 国产尤物视频在线观看| 91精品国产aⅴ一区二区| 性一交一乱一伧老太| 亚洲国产成人精品一区二区| 嫩草精品影院| 老司机午夜精品| 精品国产网站在线观看| 国产99999| 亚洲大胆美女视频| 男人天堂资源在线| 日韩在线视频中文字幕| 欧美14一18处毛片| 97国产suv精品一区二区62| 自拍偷拍欧美视频| 国产精品免费在线免费| 97精品资源在线观看| 国产精成人品localhost| 亚洲免费福利一区| 在线成人性视频| 欧美视频四区| 午夜视频在线瓜伦| 国产乱码精品一区二区三区忘忧草| 久久久久久久久久久久国产精品| 91啪九色porn原创视频在线观看| 中文字幕第20页| 亚洲激情成人在线| 亚洲av无码精品一区二区| 91精品综合久久久久久| 天堂av在线播放| 久久天天躁狠狠躁夜夜躁| 91超碰在线免费| 国产在线精品播放| 日本在线中文字幕一区| 在线观看日韩片| 午夜综合激情| 成人三级做爰av| 欧美国产日产图区| 日本亚洲色大成网站www久久| 欧美喷水一区二区| 日韩资源在线| 欧美激情精品久久久久久蜜臀 | 96精品久久久久中文字幕| 日韩av网站在线免费观看| 亚洲精品偷拍视频| 老司机久久99久久精品播放免费| 国产无套精品一区二区三区| 国产欧美精品一区aⅴ影院| 91porny在线| 日韩欧美国产一区二区三区| 97视频精彩视频在线观看| 韩国一区二区电影| 日韩视频在线直播| 亚洲自拍偷拍二区| 天堂一区二区在线免费观看| 亚洲激情 欧美| 一区二区日韩av| 国产精品久久免费| 在线不卡国产精品| 在线成人av观看| 国产一区二区三区四区五区加勒比 | 免费成人在线影院| 免费人成又黄又爽又色| 精品女厕一区二区三区| 欧美一级一区二区三区| 久久国产精品久久国产精品| 日韩色性视频| 亚洲欧美日本国产有色| 福利一区二区| 亚洲人成电影网| 国产探花在线观看| 91中文在线视频| 91成人影院| 不用播放器的免费av| 中文字幕av一区二区三区高| 亚洲 欧美 中文字幕| 日韩av在线不卡| 黄色污网站在线观看| 电影午夜精品一区二区三区| 伊人久久大香线蕉综合四虎小说| www.se五月| 最新国产の精品合集bt伙计| 亚洲午夜激情视频| 日韩在线观看网站| 日本久久久久| 一区二区三区四区五区精品| 免费久久精品视频| 国产精品69久久久久孕妇欧美| 欧美三级一区二区| 免费在线看黄色| 91沈先生作品| 欧美精品成人| 亚洲精品国产成人av在线| 午夜一区二区三区视频| 涩爱av在线播放一区二区| 浅井舞香一区二区| 欧美色网址大全| 黄色片视频在线| 亚洲青青青在线视频| 亚洲av综合色区无码一区爱av| 久久久在线观看| 你懂的一区二区三区| 亚洲欧美自偷自拍另类| 国产精品国产三级国产专播品爱网| 国产精品无码天天爽视频| 欧美精品在线播放| 国偷自产av一区二区三区| 男女av免费观看| 中文成人av在线| 国产视频在线观看视频| 久久久久久久久久国产| 最近国产精品视频| 在线免费视频一区| 亚洲欧美日韩国产成人精品影院 | 国产在线拍揄自揄拍无码| 国产精品1024| 日本韩国欧美中文字幕| 最好看的2019年中文视频| 久久wwww| 欧美成人免费高清视频| ...av二区三区久久精品| 高h放荡受浪受bl| 国产精品成人品| 午夜国产一区| 中文字幕第4页| 欧美xxxx老人做受| 羞羞影院欧美| 蜜桃视频一区二区在线观看| 91热门视频在线观看| 国产特级黄色片| 欧洲亚洲免费视频| 亚洲综合婷婷| 免费网站在线高清观看| 欧美刺激午夜性久久久久久久| 666av成人影院在线观看| 一二三在线视频| 国产三级久久久|