范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Angular中的身份验证DI中的循环依赖问题突然冒出来

  从我们离开的地方开始,我们的 Auth 服务的第一个用例是标头令牌。 添加标头令牌的最佳方法是通过 Http 拦截器。 开始吧。
  由于我们无论如何都要注入 AuthService,并使用 AppModule,因此使用 HttpInterceptorFunction 而不是 goold ol" HttpClientModule 并没有太大区别。 最终会更明显地表明它确实是一个更好的选择。
  您可以阅读有关 Angular 15 独立 HTTPClient 提供程序的信息。
  在我们的 App Module provider 数组中,我们为拦截器添加了另一个条目: // app.module @NgModule({     // ...       providers: [         {         provide: HTTP_INTERCEPTORS,         multi: true,         useClass: AppInterceptor,       },         // ...     ] }) export class AppModule {}
  拦截器立即注入 AuthService 以使用它。 让我在第一行添加一个控制台日志。 // services/http HttpInterceptor @Injectable() export class AppInterceptor implements HttpInterceptor {   constructor(private authState: AuthState) {     console.log("interceptor injected");   }   intercept(req: HttpRequest, next: HttpHandler): Observable {     // prefixing the api with proper value, mostly from config     // remote config url are expected to be filtered out, it would not make sense     const url = "https://saphire.sekrab.com/api" + req.url;      const adjustedReq = req.clone({       url: url,       setHeaders: this.getHeaders(),     });      return next.handle(adjustedReq);   }   private getHeaders(): any {     // TODO authorization here     let headers: any = {};      return headers;   } }
  在我们添加我们的头之前,让我们记住我们的事件顺序:假设我们使用 Http 调用来获取远程配置,它通常有正确的 API URL,很明显我们需要过滤掉配置 URL。 在这个例子中,我没有为配置调用远程 URL,但很高兴知道拦截器应该检查 req.url 并过滤掉它不想处理的那些。 // simple check to exclude local data or config url if (req.url.indexOf("config") > -1) {     // pass through     return next(req); } DI 问题中的循环依赖
  错误:NG0200:为 InjectionToken HTTP_INTERCEPTORS 检测到 DI 中的循环依赖。
  你见过这个吗? 当您在另一个服务中注入一个服务时,它会发生,该服务将其注入自身。 在我们的例子中,AuthService 和 HttpClient 相互注入。
  除了这两个服务之外,AuthService中还注入了使用Http的配置服务。 怎么看都是乱七八糟的。
  但在你步入中年之前,这件事会杀死你。 由于我们没有在 AuthService 构造函数中使用 HttpClient,因此这个肿瘤是良性的。 然而,如果我们确实在构造函数中发起了一个 Http 调用,那就是它在我们面前爆炸的时候。
  有很多修复,其中大部分都是围绕延迟 Http 调用来确保 AuthService 已构建。 就像等待远程配置准备就绪一样。 但这不是一个干净的解决方案。
  那么,作为一般规则,那就解决了:避免在服务构造函数中进行 Http 请求。 特别是那些早期注射的。
  如果您确实需要注入一个在其构造函数中调用 Http 的服务(下周我们将有一个用例),请将您的服务分开,并将它们分散在您的应用程序中。AuthState 服务
  为了在我们前进的过程中进行清理并变得更加系统化,让我们将所有与 Http 无关的方法移至它们自己的服务中。 AuthState 是将保存 Observable 状态的服务,并且不包含对 HttpClient 的引用。 构造函数负责读取LocalStorage信息,它有GetToken新方法返回token。// services/auth.state  @Injectable({ providedIn: "root" }) export class AuthState {   // create an internal subject and an observable to keep track   private stateItem: BehaviorSubject = new BehaviorSubject(     null   );   stateItem$: Observable = this.stateItem.asObservable();    constructor() {     // simpler to initiate state here     // check item validity     console.log("authState in");     const _localuser: IAuthInfo = JSON.parse(localStorage.getItem("user"));     if (this.CheckAuth(_localuser)) {       this.SetState(_localuser);     } else {       this.Logout();     }   } // also move here: SetState  RemoveState CheckAuth  Logout }
  现在AuthService就简单多了,只有Login,用AuthState存入localStorage。 我们稍后会在使用适当的 localStorage 包装器时增强它。
  所以现在我们需要创建一个 GetToken 方法来检索访问令牌,然后在 HttpInterceptor 中使用它 // services/auth.state // add this new method GetToken() {   const _auth = this.stateItem.getValue();   // check if auth is still valid first before you return   return this.CheckAuth(_auth) ? _auth.accessToken : null;; }
  稍后我们将添加检查令牌的逻辑。 然后我们在拦截器中使用它 // update http file to fill out get headers private getHeaders(): any {   //  authorization here   let headers: any = {};     const _auth = this.authState.GetToken();     if (_auth && _auth !== "") {       headers["authorization"] = `Bearer ${_auth}`;     }     return headers; }
  在全模块解决方案中,一切都是我们类的私有成员。 在独立的情况下,一切都是松散变量。 你更喜欢哪种方式? 401刷新
  当我们收到 401 时会发生什么? 我们可以将用户显示出来,或者使用我们的刷新令牌来获取新的访问令牌。 在 Angular 中,这可能是让我伤痕累累的任务之一。 这是事件的顺序:
  赶上 401(哪个 401)
  使用刷新令牌创建新的 Http 调用,并请求新的访问令牌
  等待回应
  更新本地存储
  重新提交原始请求(重试)
  回去好好生活
  否则注销
  捕获另一个并发 401,排队等待
  所以我们先修改Http函数来捕获401,并为其调用一个函数。 // services/http  intercept(req: HttpRequest, next: HttpHandler): Observable {     // ...      return next.handle(adjustedReq).pipe(       catchError(error => {          // if this is really an http error          if (error instanceof HttpErrorResponse             // and of 401 status             && error.status === 401           ){                     // handle 401 error, return an observable to continue the pipe           return this.handle401Error();          }          // rethrow error, to be caught elsewhere          return throwError(() => error);       })     );   }    private handle401Error(): Observable {     // let"s first try to submit a refresh access token request         // return authService.RefreshToken()     // switchMap when done to resubmit the req passed, using next.handler     // catchError means it is not working, rethrow and logout   }
  我们现在要做的是填写 handle401Error 函数。 首先,看起来我们需要 AuthService(而不是 AuthState)中的 RefreshToken 方法。 这意味着我们也需要注入它。 请记住:AuthService 在构造函数中没有 Http 调用。 // services/auth.service // add RefreshToken method RefreshToken(): Observable {   return this.http     .post(this._refreshUrl, { token: this.authState.GetToken() })     .pipe(       map((response) => {         // this response has the new refresh token and access token        if (!response) {           // something terrible happened           throw(new Error("Oh oh"));         }          // update session         const retUser: IAuthInfo = (response).data;         // we"ll be more selective later...         localStorage.setItem("user", JSON.stringify(retUser));          this.authState.SetState(retUser);          return true;       })     ); }
  回到我们的 handle401Error 函数 // services/http // update handle401Error function, also, inject AuthService in the constructor private handle401Error(     // pass in orginalReq and handler     originalReq: HttpRequest,   next: HttpHandler ): Observable {     return this.authService.RefreshToken().pipe(       switchMap((result: boolean) => {         if (result) {           // token saved (in RefreshToken), now recall the original req after adjustment             // so we need to pass "next" handler, and "originalReq"           return next.handle(originalReq.clone({setHeaders: this.getHeaders()}));         }       }),       catchError(error => {         // else refresh token did not work, its bigger than both of us         // log out and throw error         this.authState.Logout();         return throwError(() => error);       })     ); }
  我们调整签名以传入 originalReq 和下一个处理程序: // services/http // adjust call return next.handle(adjustedReq).pipe(   catchError((error) => {         // ...       return this.handle401Error(adjustedReq, next);     }         // ...   }) );
  对此进行测试,第一个问题是 /login 点。 如果是 401,则无需重试,这仅表示凭据错误。 所以处理程序必须过滤掉 /login 点 // services/http filter out login from handler401Error return next.handle(adjustedReq).pipe(   catchError((error) => {     // if this is really an http error     if (       error instanceof HttpErrorResponse &&       // and of 401 status       error.status === 401 &&       // filter out login calls       req.url.indexOf("login") < 0     ) {       return this.handle401Error(adjustedReq, next);     }     // rethrow error     return throwError(() => error);   })
  通过在某个页面中进行调用并在我的测试服务器上硬编码一些东西来测试它,这就是我注销序列的结果:
  因此,您可以看到使用正确的访问令牌撤回了原始请求。
  你想生产类似的彩色原木吗? 阅读驯服控制台 锁定和解锁
  我们还没有完成。 让我们创建一个示例用法来查看由此产生的问题。 我们将同时发出两个请求。 这意味着当第一个请求试图刷新令牌时,第二个请求进来了,它也可能请求一个新令牌,从而搞砸了原始令牌。 这是虚拟日志,它不会破坏系统,因为它很笨:
  请注意以下事项:
  抛出两个 401 错误,这是预期的
  两次调用刷新令牌,使用相同的访问令牌,一个应该工作,另一个不应该
  响应带有新令牌,在我的示例中它是相同的,因为它很笨。 在现实生活中会有两种不同的访问令牌,一种必须失败(如果它还没有失败的话)
  要解决这个问题,我们需要锁定、排队,然后解锁。
  使用私有布尔成员可以直接锁定和解锁: // services/http // add lock boolean @Injectable() export class AppInterceptor implements HttpInterceptor {   // if refreshing token, it is busy, lock   isBusy: boolean;   private handle401Error(     originalReq: HttpRequest,     next: HttpHandler   ): Observable {      if (!this.isBusy) {             // lock       this.isBusy = true;        return this.authService.RefreshToken().pipe(       // ...                 finalize(() => {                     // unlock           this.isBusy = false;        })       );     } else {       // return unadjusted, for now       return next.handle(originalReq);     }   } }
  现在有了这个,一个调用会重试,而所有其他调用都会失败。 在我们调整和召回所有其他令牌之前,我们需要稍等片刻,直到令牌准备就绪。 为此,我们可以有一个私有成员来跟踪成功的令牌。 准备好后,冲洗干净。
  对此最广泛接受的解决方案是使用 Boolean 的 Subject 并在其上使用管道。 它在锁定时和令牌准备就绪时更新。 // services/http update to allow subject queuing @Injectable() export class AppInterceptor implements HttpInterceptor {      // create a subject to queue outstanding refresh calls   recall: Subject = new Subject();  // ...  private handle401Error(...): Observable {    if (!this.isBusy) {     // ...     // progress subject to false     this.recall.next(false);     return this.authService.RefreshToken().pipe(       switchMap((result: boolean) => {         if (result) {           // progress subject to true           this.recall.next(true);                 // ... return next.handle         }       }),       // ...     );   } else {     // return the subject, watch when it"s ready, switch to recall original request     return this.recall.pipe(       filter(ready => ready === true),       switchMap(ready => {          // try again with adjusted header          return next.handle(originalReq.clone({ setHeaders: this.getHeaders() }));       })      );     } }
  我试图打破它,但我做不到。 如果您遇到它出现问题的情况,请告诉我。 侧点
  如果访问令牌无效(已过期),您可能会想要停止传出请求。 不。 这是一个 API 决定。 有些点不需要访问令牌(如 /login),如果令牌无效,有些点可以灵活地返回较少的数据。 提高
  我们可以添加的一项增强功能是,如果刷新令牌失败,则将用户重定向到登录页面。
  另一个增强功能是登录解析。 我们现在可以将导致重定向的 URL 保存在 auth 状态,并在登录后尝试重定向到它。 下周将出现关于用户帐户详细信息的那件事和另一件事。
  感谢您阅读到这里,您是否破坏了 401 处理程序?

胃气上逆气机不畅怎么办?胃气上逆,主要表现为胃积食了!也就是不消化了,因为你不消化,必须它就要寻找出路,比如让它往下走,从肛门排出,但是气不够推动不了,所以从下面跑不出去。它就会往上走,从口里面出来,这就出游时,你是用心地感受人文风景多,还是美美地为自己拍照多呢?谢邀!我是酷妹儿,喜欢旅行的伪旅游达人,我是更看重感受,也少不了美美哒为自己拍照,要是我老公,哪肯定是对我自拍不屑理会,自己转游去了。对于喜欢旅游的人来说,到底是感受人文风景,还是为什么有些人会害怕人口减少?羊太少,要不够吃了我国从上世纪六十年代就正式提出计划生育,八十年代把计划生育定为国策,九十年代地方政府提出,宁可家破,不可国亡,当时把计划生育当作头等大事来抓,过去搞计划生育的理由失业时没有去领取失业金,之后长时间没有就业直到退休了,那之前的失业金该怎么办?感谢邀请,更感谢楼主的提问。楼主你好,你在真正失业以后没有去领取这个失业金待遇,而且现在已经到了退休年龄了,那么既然你已经退休了,也就是享受基本养老金待遇了,所以说你再不能享受这个买了一块机械表,发现时间每天比前一天快4秒,客服说正常。请懂表的朋友帮我分析一下,怎么回事?我认为最可笑的有(对上号的别生气啊,不带搞人身攻击的。我也知道现在的人都不愿意听真话,但是我就是好说真话,)1男人戴表是成功人士的象征!然后就盲目的跟从!实际上那就是一个淘汰的计时娱乐圈哪位明星整容最成功?说起娱乐圈的美女实在太多了,多的数不胜数,在那个鱼龙混杂的环境,一张漂亮的脸蛋可以让一个人的事业顺风顺水。什么电视剧,电影,宣传片,红毯,就会莫名的而来。只要漂亮身材好不要演技只要北京坐火车到张家口过多少个山洞?感谢邀请,非常有意思的一个提问。首先想吐槽下提问者,你的脑洞有多大,哈哈哈。开玩笑啦,其实从北京到张家口的话最火车要路过多少个隧道这个我也没有精确地数过,其中有两条线能到达张家口一成都四日游,好的路线有哪些推荐?成都是一个非常适合旅游的好地方,既有美景又有美食,还有深厚的历史文化。我去过成都两次,被这座城市独特的魅力深深吸引。下面把我的见闻和感受分享出来,给大家做个参考。大部分外地人到成都天堂寨一分为二,为何在安徽的是5A景区,在湖北的就成了4A景区?谢谢邀请,天堂寨一分为二,为何在安徽是5A景区,在湖北就是4A景区。大别山山脉的天堂寨有一主峰,名字俺忘记了,这是一峰立湖北丶河南安徽三省。记得在湖北这边的一个山坡上有两个无名的红你的手机号用了多久,手机号用得越久越值钱吗?10年陪伴中国移动,然而并没有降低资费1GB国内流量66。5元,对比隔壁家中国电信1GB仅需要10元,我嘞个去,这是什么差距啊?然而为啥三大传统电信运营商中,中移动的用户始终稳居第朱大衣如果批发签名字的黄大衣T恤衫,朱粉们会疯狂抢购吗?那么朱大衣得把这几年挣的钱都赔进去,因为这个炒货根本就没有粉丝!我觉得不要看那些朱粉们平时如何高调拥护朱之文,其实是要蹭他的流量赚钱,如果朱之文真的要把他的大衣卖给粉丝们,别说花钱
南京玄奘寺住持南大历史系毕业,有6家公司,寺内供奉日本战犯近日,南京玄奘寺殿内供奉着南京大屠杀主犯田中军吉南京大屠杀的甲级战犯松井石根南京大屠杀的乙级战犯谷寿夫南京大屠杀的丙级战犯野田毅一事引起了社会各界的愤怒。供奉4个日本战犯的人名叫吴玄奘寺主持身份不简单,开公司拍电影,曾自嘲是渣男7月21日,一则消息震惊了全国乃至全世界。那就是位于南京并且距离南京大屠杀纪念馆不远的玄奘寺,居然供奉着四位日本战犯的长生牌位!长生牌顾名思义是为活人而立,往生牌是为已故的人而立。绿媒炒作9架次解放军军机昨日进入台西南空域环球网综合报道记者赵友平岛内绿媒自由时报声称,台防务部门8日晚消息,解放军空军战机8日上午越过海峡中线,进入台空域。台空军司令部8日晚间又声称,9架次解放军军机8日进入台西南空域。7。9签约交易流言汇总奥兰多魔术官方宣布,正式和球队中锋穆罕默德班巴签下一份续约合同。根据球队政策,魔术没有公布班巴的合同细节。不过此前名记称,班巴的合同是2年2100万美元。202122赛季,班巴为魔总台记者看世界难舍冰墩墩和雪容融,难忘我在瑞士看北京冬奥会的那些点点滴滴总台记者看世界!大家好,我是总台驻日内瓦记者张婧昊。位于瑞士洛桑的奥林匹克博物馆入口处的冰墩墩和雪容融(张婧昊摄)前几天看到一则消息,根据北京冬奥组委与特许生产商的特许经营协议,7人世间中残酷的三个现实这是一部有关亲情友情爱情的百姓生活史。这是一部中国五十年的变迁史。作家梁晓声的呕心力作人世间一经问世,就好评如潮。除了能引起一代人满满的回忆以外,我觉得好多人喜欢它的原因还在于,作天津卷高分作文钟南山的脸,文章层层递进,网友竖起大拇指文萌妈教育日记语文这个学科,之所以是三大主科之首,是因为它看似通俗易懂,实则博大精深,想要学好学精,并不容易。每年高考结束,各地区的高考状元,数学英语学科成绩考到高分满分并不少。但安倍晋三之死日本前首相安倍晋三遇刺身亡,事件一出就有同事兴奋不已,惊呼肯定是国人干的。我不以为然,只是把它看作一起政治事件,没有过多的想法,因为这本身与国人没有多大关系,平静地看着部分网友的各爱乐之都综艺不抢戏,专业不自卑2022年7月9日刊总第2928期7月7日下午,东方卫视打造的全国首档音乐剧全产业链文化推广节目爱乐之都专家研讨会在沪举行。上海广播电视台上海文化广播影视集团有限公司党委副书记台长结合江歌案判决以及日本法律,枪击安倍晋三的刺客会判死刑吗?日本前首相安倍晋三被枪击身亡还在持续发酵,此事件震惊世界,美国政要表示这是一个灾难!印度总理表示要为他默哀一天,而他们也纷纷表达了对暴力的谴责!关于开枪的嫌疑人,据日本媒体报道,他网剧我叫刘金凤被下架,之前因剧倭风太重被吐槽,网友活该7月9日,之前备受争议的网剧我叫刘金凤又上热搜,有媒体发现由辣目洋子李宏毅一起主演的电视剧,在平台已经搜索不到了,已被下架。我叫刘金凤这部剧在6月24日上架,之前就上过好几次热搜,