欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot(五)之跨域、自定義查詢及分頁

 更新時間:2017年04月25日 16:18:15   投稿:mrr  
這篇文章主要介紹了Spring Boot(五)之跨域、自定義查詢及分頁的的相關(guān)資料,需要的朋友可以參考下

跨域

前面我們初步做出了一個可以實現(xiàn)受保護的 REST API,但是我們沒有涉及一個前端領(lǐng)域很重要的問題,那就是跨域請求( cross-origin HTTP request )。先來回顧一些背景知識:

跨域請求

定義:當(dāng)我們從本身站點請求不同域名或端口的服務(wù)所提供的資源時,就會發(fā)起跨域請求。

例如最常見的我們很多的 css 樣式文件是會鏈接到某個公共 CDN 服務(wù)器上,而不是在本身的服務(wù)器上,這其實就是典型的一個跨域請求。但瀏覽器由于安全原因限制了在腳本( script )中發(fā)起的跨域 HTTP 請求。也就是說 XMLHttpRequest 和 Fetch 等是遵循“同源規(guī)則”的,即只能訪問自己服務(wù)器的指定端口的資源(同一服務(wù)器不同端口也會視為跨域)。但這種限制在今天,我們的應(yīng)用需要訪問多種外部 API 或 資源的時候就不能滿足開發(fā)者的需求了,因此就產(chǎn)生了若干對于跨域的解決方案,JSONP 是其中一種,但在今天來看主流的更徹底的解決方案是 CORS ( Cross-Origin Resource Sharing )。

跨域資源共享 ( CORS )

這種機制將跨域的訪問控制權(quán)交給服務(wù)器,這樣可以保證安全的跨域數(shù)據(jù)傳輸。現(xiàn)代瀏覽器一般會將 CORS 的支持封裝在 HTTP API 之中( 比如 XMLHttpRequest 和 Fetch ),這樣可以有效控制使用跨域請求的風(fēng)險,因為你繞不過去,總得要使用 API 吧。

概括來說,這個機制是增加一系列的 HTTP 頭來讓服務(wù)器可以描述哪些源是允許使用瀏覽器來訪問資源的。而且對于簡單的請求和復(fù)雜請求,處理機制是不一樣的。

簡單請求僅允許三個 HTTP 方法:GET,POST 以及 HEAD,另外只能支持若干 header 參數(shù):Accept , Accept-Language , Content-Language , Content-Type (值只能是 application/x-www-form-urlencoded、multipart/form-data 和 text/plain), DPR , Downlink , Save-Data , Viewport-Width 和 Width。

對于簡單請求來說,比如下面這樣一個簡單的GET請求:從 http://me.domain 發(fā)起到 http://another.domain/data/blablabla 的資源請求

GET /data/blablabla/ HTTP/1.1
// 請求的域名
Host: another.domain
...//省略其它部分,重點是下面這句,說明了發(fā)起請求者的來源
Origin: http://me.domain

應(yīng)用了 CORS 的對方服務(wù)器返回的響應(yīng)應(yīng)該像下面這個樣子,當(dāng)然這里 Access-Control-Allow-Origin: * 中的 * 表示任何網(wǎng)站都可以訪問該資源,如果要限制只能從 me.domain 訪問,那么需要改成 Access-Control-Allow-Origin: http://me.domain

HTTP/1.1 200 OK
...//省略其它部分
Access-Control-Allow-Origin: *
...//省略其它部分
Content-Type: application/json

那么對于復(fù)雜請求怎么辦呢?這需要一次預(yù)檢請求和一次實際的請求,也就是說需要兩次和對方服務(wù)器的請求/響應(yīng)。預(yù)檢請求是以 OPTION 方法進行的,因為 OPTION 方法不會改變?nèi)魏钨Y源,所以這個預(yù)檢請求是安全的,它的職責(zé)在于發(fā)送實際請求將會使用的 HTTP 方法以及將要發(fā)送的 HEADER 中將攜帶哪些內(nèi)容,這樣對方服務(wù)器可以根據(jù)預(yù)檢請求的信息決定是否接受。

// 預(yù)檢請求
OPTIONS /resources/post/ HTTP/1.1
Host: another.domain
...// 省略其它部分
Origin: http://me.domain
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

服務(wù)器對預(yù)檢請求的響應(yīng)如下:

HTTP/1.1 200 OK
// 省略其它部分
Access-Control-Allow-Origin: http://me.domain
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
// 省略其它部分
Content-Type: text/plain

接下來的正式請求就和上面的簡單請求差不多了,就不贅述了。

在 Spring Boot 中如何啟用 CORS

啰嗦了這么多,終于進入正題,但我一直覺得不能光知其然而不知其所以然,所以各位就忍了吧。加入 CORS 的支持在 Spring Boot 中簡單到不忍直視,添加一個配置類即可:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
 @Bean
 public FilterRegistrationBean corsFilter() {
  UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  CorsConfiguration config = new CorsConfiguration();
  config.setAllowCredentials(true);
  // 設(shè)置你要允許的網(wǎng)站域名,如果全允許則設(shè)為 *
  config.addAllowedOrigin("http://localhost:4200");
  // 如果要限制 HEADER 或 METHOD 請自行更改
  config.addAllowedHeader("*");
  config.addAllowedMethod("*");
  source.registerCorsConfiguration("/**", config);
  FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
  // 這個順序很重要哦,為避免麻煩請設(shè)置在最前
  bean.setOrder(0);
  return bean;
 }
}

如果我們使用 POSTMAN 訪問一下 API,會發(fā)現(xiàn)得到一個 Invalid CORS request 的響應(yīng),因為我們的 API 只授權(quán)給了 localhost:4200

用 POSTMAN 無法得到請求結(jié)果

當(dāng)然,如果我們使用 CURL 的話是可以訪問的,這是因為 CURL 不是瀏覽器。

嗯嗯,這樣就結(jié)束了,這節(jié)好水,但就是這么簡單啊。

自定義查詢

我們回過頭來再來看看數(shù)據(jù)查詢,大部分情況下 Spring Data 提供的按方法名進行查詢的方式足夠簡單也足夠強大,但總歸還是有很多局限。為了說明這個問題,也順便為我的新 Angular 項目打造一個 API,我們把 API 的需求改一下。現(xiàn)在我們要做的不是一個簡單的 Todo 了,而是類似 Teambition 或 Worktile 那樣的企業(yè)協(xié)作平臺,當(dāng)然我們不會做的那么復(fù)雜,是個簡化版本。那么這時我們的對象模型是這樣的:

主要的領(lǐng)域?qū)ο?/p>

我們首先看一下 Project 這個對象,我們來構(gòu)建它的 API,增刪改沒啥可講。但是查詢上會有點不一樣,首先我們并不希望把所有的 Project 都查出來,而是該用戶參與的項目要提供一個 API 給客戶端。

Project 和 User 按關(guān)系型數(shù)據(jù)庫的看法是個多對多的關(guān)系,在MongoDB中這其實有多種做法,可以在 User 對象中設(shè)置一個 Project 的集合屬性,也可以在 Project 中設(shè)置 User 的集合屬性 (在我們的例子里是 memebers ),還可以兩者結(jié)合,就是在 User 和 Projet 中互相有對方的集合屬性。具體采用哪種需要看業(yè)務(wù)場景和性能需求,比如如果任何一個項目的成員數(shù)如果不會很大,那么在 Project 中嵌入 User 集合就比較劃算;如果項目的成員較多,但一個成員歸屬的項目不會很多的情況下,就可以把 Project 的集合嵌入到 User 中。我們這里采用了第一種做法。

那么接下來我們來寫該用戶參與的項目的查詢。當(dāng)然我們可以按照 Spring Data 強大的按方法名稱來生成對應(yīng)查詢的方式來做:尋找 members 屬性中包含該用戶的集合

Set<Project> findByMembersContaining(User user)

看起來還可以,挺簡單,但是如果我們說再加兩個條件要篩選 project.enabled == true (我們不會物理刪除項目,而是設(shè)置其標志位 enabled,所以這就是篩選未刪除的項目) 和 project.archived == false (項目完結(jié)后需要歸檔,這就是篩選未歸檔的)。這兩個條件一加上,好家伙,我們的方法名變成了下面這個樣子,不忍直視?。?/p>

Set<Project> findByMembersContainingAndEnabledAndArchived(User user, boolean enabled, boolean archived)

當(dāng)然好用還是好用了,但是這個方法名也太長了,好在 Spring Data 中提供很多種方式自定義查詢,我們介紹一種相對簡單的:利用 @Query 注解來進行查詢,方法名字就沒有那么雷人了:

@Query("{'owner.$id': ?#{[0]}, 'enabled': ?#{[1]}, 'archived': ?#{[2]}}")
Set<Project> findRelated(User user, boolean enabled, boolean archived)

這個注解中的內(nèi)容是一個 JSON 對象,就和我們在 MongoDB 的控制臺查詢的find()中的內(nèi)容是一樣的,只不過將雙引號換成單引號,將需要變量用 [0]、[1] 和 [2] 的形式表示第一、第二和第三個參數(shù)。那么 ?#{} 是表示里面的內(nèi)容是個 SpEL ( Spring 的表達式語言) 表達式。

所以實踐中,我們可以在 MongoDB 的控制臺去實驗語句是否好用,然后在 Spring 中編寫表達式。

db.project.find(
 {
  "owner.$id": ObjectId("58f5a904edc76ab0e033cfc3"),  
  "enabled": true, 
  "archived": false
 })

在MongoDB的console查詢

數(shù)據(jù)的分頁

很多時候我們希望 API 返回的數(shù)據(jù)是可以分頁的,這個分頁問題在 Spring Boot 有怎樣便捷的方法呢?我們是否需要再定義一堆什么 pageSize,pageCount,start, off 的參數(shù)呢?答案是完全沒必要,分頁這個事情對于 Spring Boot 來說很簡單,只需改變各層級原有方法中返回的 List 或 Set 對象為 Page 對象,傳入?yún)?shù)多一個 Pageable 類型的參數(shù)即可。

// Controller
@RequestMapping(method = RequestMethod.GET)
public Page<Project> findRelated(
  @RequestHeader(value = "userId") String userId,
  @RequestParam(value = "enabled", defaultValue = "true", required = false) boolean enabled,
  @RequestParam(value = "archived", defaultValue = "false", required = false) boolean archived,
  Pageable pageable) {
 return service.findRelated(userId, enabled, archived, pageable);
}
// Repository
@Query("{'owner.$id': ?#{[0]}, 'enabled': ?#{[1]}, 'archived': ?#{[2]}}")
Page<Project> findRelated(ObjectId userId, boolean enabled, boolean archived, Pageable pageable);

現(xiàn)在呢,我們就可以這樣使用了 GET http://localhost:8090/projects/?page=0&size=3 表示取每頁三個數(shù)據(jù)取第一頁。

本章代碼:https://github.com/wpcfan/spring-boot-tut/tree/chap05

相關(guān)文章

  • SpringBoot如何讀取war包jar包和Resource資源

    SpringBoot如何讀取war包jar包和Resource資源

    這篇文章主要介紹了SpringBoot如何讀取war包jar包和Resource資源,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • 如何開發(fā)一個簡單的Akka Java應(yīng)用

    如何開發(fā)一個簡單的Akka Java應(yīng)用

    這篇文章主要介紹了如何開發(fā)一個簡單的Akka Java應(yīng)用 ,幫助大家使用Java創(chuàng)建Akka項目并將其打包,感興趣的朋友可以了解下
    2020-10-10
  • 淺談JVM垃圾回收之哪些對象可以被回收

    淺談JVM垃圾回收之哪些對象可以被回收

    這篇文章主要介紹了JVM垃圾回收之哪些對象可以被回收,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 新版IDEA使用Spring Initializr創(chuàng)建工程的兩種方法

    新版IDEA使用Spring Initializr創(chuàng)建工程的兩種方法

    這篇文章主要介紹了新版IDEA使用Spring Initializr創(chuàng)建工程(兩種方法,官方工具和IDEA),文中通過代碼示例和圖文結(jié)合的方式給大家講解的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-10-10
  • mybatis-plus如何使用mapper的xml

    mybatis-plus如何使用mapper的xml

    這篇文章主要介紹了mybatis-plus如何使用mapper的xml問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 淺談Java中String的常用方法

    淺談Java中String的常用方法

    今天帶大家來復(fù)習(xí)一下Java中String的常用方法,文中有非常詳細的介紹,對正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java讀取properties文件內(nèi)容的幾種方式詳解

    Java讀取properties文件內(nèi)容的幾種方式詳解

    這篇文章主要介紹了Java讀取properties文件內(nèi)容的幾種方式詳解,讀取properties配置文件在實際的開發(fā)中使用的很多,本文來介紹常用的幾種實現(xiàn)方式,需要的朋友可以參考下
    2023-11-11
  • 深入了解java內(nèi)存分配和回收策略

    深入了解java內(nèi)存分配和回收策略

    下面小編就為大家?guī)硪黄钊肓私鈐ava內(nèi)存分配和回收策略。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Java實現(xiàn)文件切割拼接的實現(xiàn)代碼

    Java實現(xiàn)文件切割拼接的實現(xiàn)代碼

    這篇文章主要介紹了Java實現(xiàn)文件切割拼接的實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • JAVA程序員不得不留意的編碼規(guī)范

    JAVA程序員不得不留意的編碼規(guī)范

    這篇文章主要介紹了JAVA程序員不得不留意的編碼規(guī)范,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07

最新評論