一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - Spring Boot(五)之跨域、自定義查詢及分頁

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

2020-09-18 14:35Java之家 JAVA教程

這篇文章主要介紹了Spring Boot(五)之跨域、自定義查詢及分頁的的相關資料,需要的朋友可以參考下

跨域

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

跨域請求

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

例如最常見的我們很多的 css 樣式文件是會鏈接到某個公共 cdn 服務器上,而不是在本身的服務器上,這其實就是典型的一個跨域請求。但瀏覽器由于安全原因限制了在腳本( script )中發起的跨域 http 請求。也就是說 xmlhttprequest 和 fetch 等是遵循“同源規則”的,即只能訪問自己服務器的指定端口的資源(同一服務器不同端口也會視為跨域)。但這種限制在今天,我們的應用需要訪問多種外部 api 或 資源的時候就不能滿足開發者的需求了,因此就產生了若干對于跨域的解決方案,jsonp 是其中一種,但在今天來看主流的更徹底的解決方案是 cors ( cross-origin resource sharing )。

跨域資源共享 ( cors )

這種機制將跨域的訪問控制權交給服務器,這樣可以保證安全的跨域數據傳輸。現代瀏覽器一般會將 cors 的支持封裝在 http api 之中( 比如 xmlhttprequest 和 fetch ),這樣可以有效控制使用跨域請求的風險,因為你繞不過去,總得要使用 api 吧。

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

簡單請求僅允許三個 http 方法:get,post 以及 head,另外只能支持若干 header 參數: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發起到http://another.domain/data/blablabla的資源請求

?
1
2
3
4
5
get /data/blablabla/ http/1.1
// 請求的域名
host: another.domain
...//省略其它部分,重點是下面這句,說明了發起請求者的來源
origin: http://me.domain

應用了 cors 的對方服務器返回的響應應該像下面這個樣子,當然這里 access-control-allow-origin: * 中的 * 表示任何網站都可以訪問該資源,如果要限制只能從 me.domain 訪問,那么需要改成 access-control-allow-origin:http://me.domain

?
1
2
3
4
5
http/1.1 200 ok
...//省略其它部分
access-control-allow-origin: *
...//省略其它部分
content-type: application/json

那么對于復雜請求怎么辦呢?這需要一次預檢請求和一次實際的請求,也就是說需要兩次和對方服務器的請求/響應。預檢請求是以 option 方法進行的,因為 option 方法不會改變任何資源,所以這個預檢請求是安全的,它的職責在于發送實際請求將會使用的 http 方法以及將要發送的 header 中將攜帶哪些內容,這樣對方服務器可以根據預檢請求的信息決定是否接受。

?
1
2
3
4
5
6
7
// 預檢請求
options /resources/post/ http/1.1
host: another.domain
...// 省略其它部分
origin: http://me.domain
access-control-request-method: post
access-control-request-headers: content-type

服務器對預檢請求的響應如下:

?
1
2
3
4
5
6
7
8
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 中簡單到不忍直視,添加一個配置類即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
  // 設置你要允許的網站域名,如果全允許則設為 *
  config.addallowedorigin("http://localhost:4200");
  // 如果要限制 header 或 method 請自行更改
  config.addallowedheader("*");
  config.addallowedmethod("*");
  source.registercorsconfiguration("/**", config);
  filterregistrationbean bean = new filterregistrationbean(new corsfilter(source));
  // 這個順序很重要哦,為避免麻煩請設置在最前
  bean.setorder(0);
  return bean;
 }
}

如果我們使用 postman 訪問一下 api,會發現得到一個 invalid cors request 的響應,因為我們的 api 只授權給了 localhost:4200

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

用 postman 無法得到請求結果

當然,如果我們使用 curl 的話是可以訪問的,這是因為 curl 不是瀏覽器。

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

自定義查詢

我們回過頭來再來看看數據查詢,大部分情況下 spring data 提供的按方法名進行查詢的方式足夠簡單也足夠強大,但總歸還是有很多局限。為了說明這個問題,也順便為我的新 angular 項目打造一個 api,我們把 api 的需求改一下。現在我們要做的不是一個簡單的 todo 了,而是類似 teambition 或 worktile 那樣的企業協作平臺,當然我們不會做的那么復雜,是個簡化版本。那么這時我們的對象模型是這樣的:

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

主要的領域對象

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

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

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

?
1
set<project> findbymemberscontaining(user user)

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

?
1
set<project> findbymemberscontainingandenabledandarchived(user user, boolean enabled, boolean archived)

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

?
1
2
@query("{'owner.$id': ?#{[0]}, 'enabled': ?#{[1]}, 'archived': ?#{[2]}}")
set<project> findrelated(user user, boolean enabled, boolean archived)

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

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

?
1
2
3
4
5
6
db.project.find(
 {
  "owner.$id": objectid("58f5a904edc76ab0e033cfc3"), 
  "enabled": true,
  "archived": false
 })

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

在mongodb的console查詢

數據的分頁

很多時候我們希望 api 返回的數據是可以分頁的,這個分頁問題在 spring boot 有怎樣便捷的方法呢?我們是否需要再定義一堆什么 pagesize,pagecount,start, off 的參數呢?答案是完全沒必要,分頁這個事情對于 spring boot 來說很簡單,只需改變各層級原有方法中返回的 list 或 set 對象為 page 對象,傳入參數多一個 pageable 類型的參數即可。

?
1
2
3
4
5
6
7
8
9
10
11
12
// 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);

現在呢,我們就可以這樣使用了 get http://localhost:8090/projects/?page=0&size=3 表示取每頁三個數據取第一頁。

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

原文鏈接:https://juejin.im/post/58f9abdf570c350058cb3856

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 人性本色 | 青青草久| 久久夜色噜噜噜亚洲AV0000 | 91制片厂 果冻传媒 天美传媒 | 麻豆天美精东果冻传媒在线 | 亚洲精品第一国产综合高清 | 国产精品视频第一页 | 海派甜心完整版在线观看 | 俺去俺来也www色官网免费的 | 美女林柏欣21p人体之仓之梦 | 国产91区 | 亚洲激情偷拍 | 国产一区二区三区福利 | 欧美一级片免费在线观看 | 强行扒开美女大腿挺进 | 黑人破中国女人处 | 红色一片在线影视 | 亚洲大逼 | 丝袜护士强制脚足取精 | 国产成人精品一区二三区2022 | 久久国产精品人妻中文 | 欧美伊人久久久久久久久影院 | 四虎地址 | 99re5精品视频在线观看 | 免费视频精品一区二区三区 | 国产综合成人亚洲区 | 久久综合狠狠综合久久综合88 | 99精品国产自在现线观看 | 青青操在线| 射逼网 | 国模一区二区三区视频一 | 搡60一70岁的老女人小说 | www.日本在线播放 | 午夜视频一区二区 | 亚洲 色 欧美 爱 视频 日韩 | 国产大神91一区二区三区 | 爆操| 免费观看一级特黄三大片视频 | 女人又色又爽又黄 | 特色特色大片在线 | 精品视频在线免费观看 |