6

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等,再作为参数传递到异步方法。

当然还有很多方法,可以谷歌搜索关键字 异步安全上下文配置


weiweiyi
1k 声望123 粉丝