6

The difference between ActivatedRoute and Router

The front-end angular uses these two for routing operations, but it seems that the difference has never been clear. Just record it here.

the difference

 constructor(private route: ActivatedRoute,
            private router: Router) {
}
  • ActivatedRoute is the routing object of the current component, which contains the current routing information.
  • router is the global router object. Can jump between routes

So I think the biggest difference is that the scope is different. One is to obtain the information of the current route, and the other is to jump to the global routing operation.

ActivatedRoute:

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

After printing ActivatedRoute in ngOnit, you can see the following properties.
component, date , fragment, params, queryParams, snapshot, url, _futurnSnapshot
_routerState, etc.

The Angular documentation defines ActivatedRoute as . A service provided to each routing component that contains routing-specific information such as routing parameters, static data, parsed data, global query parameters, and global fragments. Each Route maps a URL path to a component.

image.png


Here are a few properties that are often seen or used:

component

We can see that it corresponds to IndexComponent, that is, the current routing object corresponds to IndexComponent.

snapshot

In Snapshot, our values in components and routes are out of sync. If you use snapshot and have a parameter in your route definition, such as product/:id, then if the page changes the id, you won't get any new id. A snapshot means it was when ngOnInit was running, which is its state at that point in time.

We can get the snapshot snapshot in the following way, and we can get its params route parameter.

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

Here params and queryParams are used more, so let's focus on them.

params route parameters

The URL generated by params adopts the matrix method (;key=value;kye1=value1)

Angular router uses this to determine routing. They are part of the route definition. We need the form product/:id to jump to the correct route.

We often use this form, when the route is defined as follows, the id is the params parameter

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

Getting the id is the params parameter:

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

queryParams query parameters

The URL generated by queryParams is in traditional notation (?key=value&key1=value1)

queryParams query parameters are optional and are usually passed as /product?page=10 .

Pass query parameters:
The first is added via the [queryParams] directive.

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

The second is to use the navigate method to navigate to add.

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

Read query parameters:

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

Params and queryParams summary

Both have different usages:

When we want to identify a product based on productID, in this case, the Params parameter can be used.

get /product/{id}

As another example, you want to filter products based on a specification, in which case you can use the queryParams parameter.

GET /product?colour=red

The project takes this approach:

The params parameter is subscribed.

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

When the route jumps, the parameter is converted into a route parameter.

 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();
  }

This makes the route display the matrix method, which is more beautiful. The parameter acquisition method is also unified.
image.png


Router

It loads components associated with the requested route, as well as fetches relevant data for a specific route. This allows us to render different pages by controlling different routes and getting different data.

Simply put, it is a page jump.

It jumps according to the route we provide, as follows. We need to introduce this TaskRoutingModule into the component where it is located. When the current component jumps to it, it can jump to the corresponding component according to the provided path.

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

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

Jump method:

The first way can use the routerLink instruction to jump.

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

The second way can use the navigate method for relative path jumping

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

You can also use the navigateByUrl method for absolute path jumping

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

Summary: When we need to obtain routing information, we can use activatedRoute, and when we need to use routing jumps, use Router.

Problems encountered in the background

Also, let's talk about previous problems.

When the foreground passes a value to the background, the background does not receive the name as shown in the figure.

image.png

When I checked the reason later, I found that the field name passed by the front desk was content, not name. It does not correspond to the field name in the background.

image.png

@RequestParam

The @RequestParam annotation, equivalent to request.getParam, can solve the problem of inconsistency between the front-end parameter name and the back-end receiving parameter variable name.

RequestParam: Mainly used in the Controller layer to obtain the value of the parameter carried after "?" in the URL, such as:
The value of the id parameter in http://localhost:8080/requestParam/user?id=1

  • Related properties:
  • 1. name/value: the name of the specified parameter in the url
  • 2. required: when it is true, this parameter must be filled in, the default is true, when it is false: whether the parameter is optional
  • 3. defaultValue: the default value when the parameter is not filled in

So when the current background parameters are inconsistent, we can use

 RequestParam(value="前端传值的字段") <T> 后端要求的字段名)

Although this method can be handled, the front-end and back-end parameters are consistent and standardized, so I changed the field name passed by the front-end.

get authentication is null

 @Override
  public Optional<AuthUserDetails> getAuthUserDetailWithoutTransaction() {
    logger.debug("根据认证获取当前登录用户名,并获取该用户");
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
}

After printing SecurityContextHolder.getContext().getAuthentication(), the result is null.

Checked out Baeldung https://www.baeldung.com/spring-security-async-principal-propagation

Know that by default Spring Security Authentication is bound to ThreadLocal (aka thread local.) So when the execution flow runs in a new thread with @Async it won't be the authenticated context, it will be lost contextual information.

Solution

1. Contextual information can be passed:

 // 1. 在主线程中获取安全上下文。
    SecurityContext securityContext = SecurityContextHolder.getContext();
    threadTaskExecutor.execute(() -> {
        try {
            // 2. 将主线程中的安全上下文设置到子线程中的ThreadLocal中。
            SecurityContextHolder.setContext(securityContext);
            // 业务代码
        } catch (Exception e) {
            // 异常信息捕获
        } finally {
            // 清除操作
            // 3. 将调用者中的安全上下文设置到当前业务子线程中的ThreadLocal中。
            SecurityContextHolder.clearContext();
        }
    });

Details: https://blog.csdn.net/qq_37253891/article/details/118608126 .

2. Obtain the required information in the main thread and pass it as a parameter to the asynchronous method

This method is also the simplest method. After the upper layer of the asynchronous method obtains the desired information according to the context, such as id, etc., it is passed to the asynchronous method as a parameter.

Of course there are many ways, you can google the keyword asynchronous security context configuration


weiweiyi
1k 声望123 粉丝