ActivatedRoute 和 Router,以及記錄後臺遇到的問題

weiewiyi發表於2022-05-10

ActivatedRoute 和 Router的區別

前臺angular使用這兩個來進行路由的操作,但是好像一直不大清楚區別。這裡簡單記錄一下。

區別

constructor(private route: ActivatedRoute,
            private router: Router) {
} 
  • ActivatedRoute是當前元件的路由物件,包含了當前的路由資訊。
  • router是全域性路由器物件。可以在各個路由之間跳轉

所以最大的區別我認為就是作用域不同,一個是在獲取當前路由的資訊,另一個則是對全域性路由操作跳轉。

ActivatedRoute:

constructor(private route: ActivatedRoute){
} 
ngOnInit() {
 console.log(this.route);
}

將ActivatedRoute在ngOnit中列印之後,可以看到有如下屬性。
component, date , fragment,params,queryParams,snapshot,url,_futurnSnapshot
_routerState等。

Angular 文件將 ActivatedRoute 定義為。提供給每個路由元件的服務,其中包含路由特定資訊,例如路由引數、靜態資料、解析資料、全域性查詢引數和全域性片段。每個都Route將一個 URL 對映path到一個元件。

image.png


這裡講一下常看到或者用到的幾個屬性:

component

我們可以看到對應的是IndexComponet, 也就是當前路由物件對應著的是IndexCompoent.

snapshot

在Snapshot 中,我們在元件和路由中的值是不同步的。如果使用 snapshot 並且在路由定義中有一個引數,例如 product/:id,那麼如果頁面更改了id,那麼將不會獲得任何新的 id。快照意味著它是在 ngOnInit 執行時,這是它在那個時間點的狀態。

我們可以通過如下的方式獲取snapshot快照, 並可以獲取它的params路由引數。

constructor(private route: ActivatedRoute){
} 
ngOnInit() {
 const id = this.route.snapshot.params.id;
}

這裡params和queryParams用的比較多,所以重點講一下。

params 路由引數

params生成的URL採用matrix法(;key=value;kye1=value1)

Angular 路由器使用它來確定路由。它們是路由定義的一部分。我們需要 product/:id 這種形式來跳轉到正確的路由。

我們常使用這樣的形式,當如下定義路由時,id就是params引數

{ path: 'product/:id', component: ProductDetailComponent }

獲取id就是params引數:

constructor(private route:ActivatedRoute){
}
 
this.route.params.subscribe(param => {
      const id = +param.id;
    });

queryParams 查詢引數

queryParams生成的URL採用傳統表示法(?key=value&key1=value1)

queryParams查詢引數是可選的,通常會以 /product?page=10 的形式傳遞。

傳遞查詢引數:
第一種是通過[queryParams]指令新增。

<a [routerLink]="['product']" [queryParams]="{ page:10 }">Page 10</a>

第二種使用navigate方式導航新增。

  this.router.navigate(['/product'], { queryParams: { page: 10 } }); 

讀取查詢引數:

constructor(private route:ActivatedRoute){
}
 
this.route.queryParams.subscribe(param => {
      const page = +params['page'];
});

Params 和 queryParams總結

兩種都有不同的用法:

當我們希望根據productID 識別產品,在這種情況下,可以使用Params引數。

獲取/product/{id}

再舉一個例子,您想根據指定過濾產品,在這種情況下,可以使用queryParams引數。

GET /product?colour=red

專案中採取了這樣的方式:

訂閱了params引數。

public subscribeParams(): void {
    this.route.params.subscribe((params: {page?: string, size?: string}) => {
      this.params = params;
    });
  }

在路由跳轉的時候,將引數轉換為路由引數。


onSubmit(queryForm: FormGroup) {
    this.params = {...this.params, ...queryForm.value}
    this.reload(this.params);
}

reload(params: Params): void {
    // 將引數轉換為路由引數
    const queryParams = CommonService.convertToRouteParams(params);
    this.router.navigate(['./'],
      {
        relativeTo: this.route,
        queryParams: queryParams,
      }).then();
  }

這樣使得路由顯示的是matrix法,比較美觀。也統一了引數獲取方式。
image.png


Router

它載入與所請求路由相關聯的元件,以及獲取特定路由的相關資料。這允許我們通過控制不同的路由,獲取不同的資料,從而渲染不同的頁面.

簡單來說就是跳轉頁面。

它根據我們提供的路由來跳轉,如下。我們需要把這個TaskRoutingModule引入所處的元件,當前元件跳轉到時候,就能根據提供的路徑來跳轉到相應的元件


const routes: Routes = [
  {
    path: '',
    component: IndexComponent,
  },
  {
    path: 'view/:id',
    component: ViewComponent,
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TaskRoutingModule { }

跳轉方式:

第一種方式可以使用routerLink指令跳轉。

  <a routerLink="view/{{id}}">view頁面</a>

第二種方式可以使用navigate方法進行相對路徑跳轉

this.router.navigate(['./'],
      {
        relativeTo: this.route,
        queryParams: queryParams,
      }).then();

也可以使用navigateByUrl方法進行絕對路徑跳轉

 this.router.navigateByUrl('volunteer').then();

總結:當我們需要獲取路由的資訊的時候,可以使用activatedRoute, 當需要使用路由跳轉,使用Router。

後臺遇到的問題

另外說一下之前遇到的問題。

當前臺向後臺傳值的時候,後臺沒有接收到如圖的name。

image.png

後面排查原因的時候,才發現前臺傳的是欄位名是content,而不是name。並沒有與後臺的欄位名對應。

image.png

@RequestParam

@RequestParam註解,等價於request.getParam,可以解決前臺引數名稱與後臺接收引數變數名稱不一致的問題。

RequestParam: 主要用在Controller層,用於獲取URL中“?”後攜帶的引數的值,如:
http://localhost:8080/request...中id引數的值

  • 相關屬性:
  • 1、name/value:url中指定引數的名稱
  • 2、required: 為true時,這個引數必選填寫,預設是true,為false時:引數可選是否填寫
  • 3、defaultValue:引數不填寫時的預設值

所以當前後臺引數不一致的時候我們可以使用

RequestParam(value="前端傳值的欄位") <T> 後端要求的欄位名)

雖然這種方式可以處理,但是前後臺引數一致比較規範,所以我改了前臺傳遞的的欄位名。

獲取認證為null

@Override
  public Optional<AuthUserDetails> getAuthUserDetailWithoutTransaction() {
    logger.debug("根據認證獲取當前登入使用者名稱,並獲取該使用者");
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
}

列印了下SecurityContextHolder.getContext().getAuthentication()結果為null。

查閱了Baeldunghttps://www.baeldung.com/spri...

得知
預設情況下,Spring Security Authentication 繫結到ThreadLocal(也就是執行緒本地. )因此,當執行流在帶有 @Async 的新執行緒中執行時,它不會是經過身份驗證的上下文,會丟失掉上下文資訊。

解決方法

1.可以傳遞上下文資訊:

    // 1. 在主執行緒中獲取安全上下文。
    SecurityContext securityContext = SecurityContextHolder.getContext();
    threadTaskExecutor.execute(() -> {
        try {
            // 2. 將主執行緒中的安全上下文設定到子執行緒中的ThreadLocal中。
            SecurityContextHolder.setContext(securityContext);
            // 業務程式碼
        } catch (Exception e) {
            // 異常資訊捕獲
        } finally {
            // 清除操作
            // 3. 將呼叫者中的安全上下文設定到當前業務子執行緒中的ThreadLocal中。
            SecurityContextHolder.clearContext();
        }
    });

詳細:https://blog.csdn.net/qq_3725...

2.在主執行緒中獲取好需要的資訊再作為引數傳遞到非同步方法中

這個方法也是最簡單的方法,在非同步方法的上一層根據上下文獲取好想要的資訊之後,比如id等,再作為引數傳遞到非同步方法。

當然還有很多方法,可以谷歌搜尋關鍵字 非同步安全上下文配置

相關文章