当我一直使用这种方式时,IP和国家/地区在后端传递为空
我需要传递活动日志的值
我使用 Java Spring boot 的后端
并为此使用 AOP
我用这种方法解决了下面的问题,但没有
地理服务
导出类 GeoService { 私人 geoApiUrl = 'https://ipinfo.io/json'; // IPinfo.io API
constructor(private http: HttpClient) {}
getGeoInfo(): Observable<any> {
return this.http.get<GeoInfoResponse>(this.geoApiUrl).pipe(
catchError(error => {
console.error('Error fetching geo information:', error);
return throwError(() => new Error('Failed to fetch geo information'));
})
);
}
}
export interface GeoInfoResponse {
ip: string;
country: string;
city: string;
region: string;
loc: string; // Latitude and longitude
org: string; // Organization
postal: string; // Postal code
timezone: string;
}
地理资讯服务
export class GeoInfoService {
private ipSubject = new ReplaySubject<string | null>(1);
private countrySubject = new ReplaySubject<string | null>(1);
constructor(private geoService: GeoService) {
this.loadGeoInfo();
}
private loadGeoInfo(): void {
this.geoService.getGeoInfo().subscribe({
next: (data: GeoInfoResponse) => {
this.ipSubject.next(data.ip);
this.countrySubject.next(data.country);
},
error: (error) => {
console.error('Error loading geo info:', error);
this.ipSubject.next(null);
this.countrySubject.next(null);
},
});
}
getIp(): Observable<string | null> {
return this.ipSubject.asObservable();
}
getCountry(): Observable<string | null> {
return this.countrySubject.asObservable();
}
}
地理拦截器
export class GeoInterceptor implements HttpInterceptor {
constructor(private geoInfoService: GeoInfoService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.geoInfoService.getIp().pipe(
switchMap(ip =>
this.geoInfoService.getCountry().pipe(
map(country => {
let headers = req.headers;
if (ip) {
headers = headers.append('Client-IP', ip);
}
if (country) {
headers = headers.append('Client-Country', country);
}
const modifiedReq = req.clone({ headers });
return modifiedReq;
}),
switchMap(modifiedReq => next.handle(modifiedReq))
)
)
);
}
}
验证拦截器
export class AuthInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
let authToken = this.authStorageService.getToken();
if (authToken) {
const authReq = req.clone({
setHeaders: { Authorization: 'Bearer ' + authToken },
});
return next.handle(authReq);
} else {
return next.handle(req);
}
}
}
应用模块
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: GeoInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
],
})
export class AppModule {}
应用程序路由模块
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegistrationComponent },
{
path: 'home',
component: DefaultLayoutComponent,
canActivate: [AuthenticationGuard],
children: [
{ path: 'authentication',loadChildren: () =>import('./modules/authentication/authentication.module').then((m) => m.AuthenticationModule),},
{ path: 'test', loadChildren: () => import('./modules/test/test.module').then((m) => m.TestModule),},
],
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
exports: [RouterModule],
})
export class AppRoutingModule {}
在任何拦截器运行之前,您需要获取国家/地区和 IP。
首先确保
GeoInfoService
和 GeoService
在装饰器上设置了 providedIn: 'root'
。只有这样它才能被 APP_INITIALIZER 访问。
@Injectable({
providedIn: 'root'
})
export class GeoService {
...
为此,您可以使用
APP_INITIALIZER
,应用程序在设置完成后加载。
需要注意的重要一点是,我使用的是
HttpBackend
,其请求不会被我们的拦截器拦截,您也可以尝试使用 httpClient
do 来完成我推荐 httpbackend 的早期问题。
// try out this commented code also.
// const appInitializer = (genInfoService: GeoInfoService) => {
// return () => {
// return genInfoService.loadGeoInfo();
// };
// };
function appInitializer(http: HttpBackend, geoInfoService: GeoInfoService) {
return () => {
return http.handle(new HttpRequest('GET', 'https://ipinfo.io/json')).pipe(
tap({
next: (data: any) => {
geoInfoService.ipSubject.next(data.ip);
geoInfoService.countrySubject.next(data.country);
},
error: (error) => {
console.error('Error loading geo info:', error);
geoInfoService.ipSubject.next(null);
geoInfoService.countrySubject.next(null);
},
}),
);
};
}
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: GeoInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{
provide: APP_INITIALIZER,
useFactory: appInitializer,
multi: true,
deps: [HttpBackend, GeoInfoService]
},
],
})
export class AppModule {}
如果您要使用注释代码,则修改服务以返回一个可观察的值,该可观察值将由 APP_initializer 调用,该值将在加载应用程序之前设置值。
private loadGeoInfo(): void {
return this.geoService.getGeoInfo().pipe(
tap(() => ({
next: (data: GeoInfoResponse) => {
this.ipSubject.next(data.ip);
this.countrySubject.next(data.country);
},
error: (error) => {
console.error('Error loading geo info:', error);
this.ipSubject.next(null);
this.countrySubject.next(null);
},
})
);
}