-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement HTTP interceptors for request handling, authentication, and…
… error management
- Loading branch information
Showing
27 changed files
with
608 additions
and
275 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,4 +43,4 @@ | |
"karma-jasmine-html-reporter": "~2.1.0", | ||
"typescript": "~5.5.2" | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
:host { | ||
display: flex; | ||
text-rendering: optimizeLegibility; | ||
-webkit-font-smoothing: antialiased; | ||
-moz-osx-font-smoothing: grayscale; | ||
} | ||
|
||
.app-layout { | ||
height: 100vh; | ||
} | ||
|
||
.menu-sidebar { | ||
position: relative; | ||
z-index: 10; | ||
min-height: 100vh; | ||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35); | ||
} | ||
|
||
.header-trigger { | ||
height: 64px; | ||
padding: 20px 24px; | ||
font-size: 20px; | ||
cursor: pointer; | ||
transition: all .3s, padding 0s; | ||
} | ||
|
||
.trigger:hover { | ||
color: #1890ff; | ||
} | ||
|
||
.sidebar-logo { | ||
position: relative; | ||
height: 64px; | ||
padding-left: 24px; | ||
overflow: hidden; | ||
line-height: 64px; | ||
background: #001529; | ||
transition: all .3s; | ||
} | ||
|
||
.sidebar-logo img { | ||
display: inline-block; | ||
height: 32px; | ||
width: 32px; | ||
vertical-align: middle; | ||
} | ||
|
||
.sidebar-logo h1 { | ||
display: inline-block; | ||
margin: 0 0 0 20px; | ||
color: #fff; | ||
font-weight: 600; | ||
font-size: 14px; | ||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; | ||
vertical-align: middle; | ||
} | ||
|
||
nz-header { | ||
padding: 0; | ||
width: 100%; | ||
z-index: 2; | ||
} | ||
|
||
.app-header { | ||
position: relative; | ||
height: 64px; | ||
padding: 0; | ||
background: #fff; | ||
box-shadow: 0 1px 4px rgba(0, 21, 41, .08); | ||
} | ||
|
||
nz-content { | ||
margin: 24px; | ||
} | ||
|
||
.inner-content { | ||
padding: 24px; | ||
background: #fff; | ||
height: 100%; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,17 @@ | ||
import { DatePipe } from '@angular/common'; | ||
import { Component } from '@angular/core'; | ||
import { RouterOutlet } from '@angular/router'; | ||
import { CommonModule } from '@angular/common'; | ||
import { RouterLink, RouterOutlet } from '@angular/router'; | ||
import { NzIconModule } from 'ng-zorro-antd/icon'; | ||
import { NzLayoutModule } from 'ng-zorro-antd/layout'; | ||
import { NzMenuModule } from 'ng-zorro-antd/menu'; | ||
|
||
@Component({ | ||
selector: 'app-root', | ||
standalone: true, | ||
imports: [RouterOutlet, DatePipe], | ||
imports: [CommonModule, RouterLink, RouterOutlet, NzIconModule, NzLayoutModule, NzMenuModule], | ||
templateUrl: './app.component.html', | ||
styleUrl: './app.component.scss', | ||
styleUrls: ['./app.component.scss'], | ||
}) | ||
export class AppComponent { | ||
title = 'ng-ui'; | ||
timeNow = new Date(); | ||
isCollapsed = false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,40 @@ | ||
import { ApplicationConfig, LOCALE_ID, provideZoneChangeDetection } from '@angular/core'; | ||
import { provideRouter } from '@angular/router'; | ||
import { ApplicationConfig, importProvidersFrom, LOCALE_ID, provideZoneChangeDetection } from '@angular/core'; | ||
import { provideRouter, withComponentInputBinding } from '@angular/router'; | ||
|
||
import { provideClientHydration } from '@angular/platform-browser'; | ||
import { routes } from './app.routes'; | ||
import { icons } from './icons-provider'; | ||
import { provideNzIcons } from 'ng-zorro-antd/icon'; | ||
import { provideNzI18n, zh_CN } from 'ng-zorro-antd/i18n'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; | ||
import { | ||
provideHttpClient, | ||
withFetch, | ||
withInterceptors, | ||
withInterceptorsFromDi, | ||
withXsrfConfiguration, | ||
} from '@angular/common/http'; | ||
import { indexInterceptor } from './core'; | ||
|
||
export const appConfig: ApplicationConfig = { | ||
providers: [ | ||
{ provide: LOCALE_ID, useValue: 'zh_CN' }, | ||
provideZoneChangeDetection({ eventCoalescing: true }), | ||
provideRouter(routes), | ||
provideRouter(routes, withComponentInputBinding()), | ||
provideClientHydration(), | ||
provideNzIcons(icons), | ||
provideNzI18n(zh_CN), | ||
importProvidersFrom(FormsModule), | ||
provideAnimationsAsync(), | ||
provideHttpClient( | ||
withFetch(), | ||
withInterceptorsFromDi(), | ||
withInterceptors(indexInterceptor), | ||
withXsrfConfiguration({ | ||
cookieName: 'XSRF-TOKEN', | ||
headerName: 'X-XSRF-TOKEN', | ||
}), | ||
), | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
import { Routes } from '@angular/router'; | ||
|
||
export const routes: Routes = []; | ||
export const routes: Routes = [ | ||
{ path: '', pathMatch: 'full', redirectTo: '/welcome' }, | ||
{ path: 'welcome', loadChildren: () => import('./pages/welcome/welcome.routes').then(m => m.WELCOME_ROUTES) }, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { | ||
ClearOutline, | ||
CloseOutline, | ||
DashboardOutline, | ||
FormOutline, | ||
GroupOutline, | ||
HomeOutline, | ||
MenuFoldOutline, | ||
MenuOutline, | ||
MenuUnfoldOutline, | ||
SearchOutline, | ||
SettingOutline, | ||
UserOutline, | ||
} from '@ant-design/icons-angular/icons'; | ||
|
||
export const icons = [ | ||
MenuFoldOutline, | ||
MenuUnfoldOutline, | ||
DashboardOutline, | ||
FormOutline, | ||
HomeOutline, | ||
SettingOutline, | ||
UserOutline, | ||
GroupOutline, | ||
MenuOutline, | ||
SearchOutline, | ||
ClearOutline, | ||
CloseOutline, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { indexInterceptor } from './net/http.Interceptor'; | ||
export { PageTitleStrategy } from './services/page-title-strategy'; | ||
export { AuthService } from './services/auth.service'; | ||
export { SessionStorageService } from './storage/session-storage'; | ||
export { BrowserStorage } from './storage/browser-storage'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { inject } from '@angular/core'; | ||
import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http'; | ||
import { catchError, finalize, Observable, throwError, timeout } from 'rxjs'; | ||
import { Router } from '@angular/router'; | ||
import { environment } from '../../../environments/environment'; | ||
import { ProgressBar } from '../services/progress-bar'; | ||
import { NzMessageService } from 'ng-zorro-antd/message'; | ||
import { AuthService } from '../services/auth.service'; | ||
|
||
/** | ||
* 默认的HTTP拦截器,用于处理所有HTTP请求。 | ||
* 在请求发送前显示加载进度条,并在请求完成后隐藏。 | ||
* 如果请求的URL包含'assets/',则直接传递请求,不做任何修改。 | ||
* 对于其他请求,会添加'X-Requested-With'头部,并根据环境配置拼接完整的URL。 | ||
* 设置请求超时时间为5秒,如果请求在5秒内未完成,则触发超时处理。 | ||
* 使用finalize操作符确保加载进度条在请求完成后隐藏。 | ||
* @param req - HTTP请求对象 | ||
* @param next - 下一个HTTP处理函数 | ||
* @returns Observable<HttpEvent<unknown>> - HTTP响应事件的Observable对象 | ||
*/ | ||
function defaultInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> { | ||
const _loading = inject(ProgressBar); | ||
_loading.show(); | ||
if (req.url.indexOf('assets/') > -1) { | ||
return next(req); | ||
} | ||
const originalUrl = req.url.indexOf('http') > -1 ? req.url : environment.host + req.url; | ||
const xRequestedReq = req.clone({ | ||
headers: req.headers.append('X-Requested-With', 'XMLHttpRequest'), | ||
url: originalUrl, | ||
}); | ||
return next(xRequestedReq).pipe( | ||
timeout({ first: 5_000, each: 10_000 }), | ||
finalize(() => _loading.hide()), | ||
); | ||
} | ||
|
||
/** | ||
* 错误处理拦截器,用于处理 HTTP 请求过程中可能出现的错误。 | ||
* @param req - HTTP请求对象。 | ||
* @param next - 下一个HTTP处理函数。 | ||
* @returns 返回一个Observable,包含处理后的HTTP响应或错误信息。 | ||
* 错误处理包括: | ||
* - 网络错误(状态码为0):提示用户检查网络连接。 | ||
* - 认证失败(状态码为401):登出用户并重定向到登录页面。 | ||
* - 其他服务器错误:提示用户稍后重试,并显示错误码和详细信息。 | ||
*/ | ||
function handleErrorInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn) { | ||
const _message = inject(NzMessageService); | ||
const _auth = inject(AuthService); | ||
const _route = inject(Router); | ||
return next(req).pipe( | ||
catchError(error => { | ||
if (error.status === 0) { | ||
// A client-side or network error occurred. Handle it accordingly. | ||
console.error('An error occurred:', error.error); | ||
_message.error(`网络错误,请检查网络连接后重试!`); | ||
} else { | ||
if (error.status === 401) { | ||
if (error.error.path === '/oauth2/login') { | ||
return throwError(() => error.error); | ||
} | ||
_auth.logout(); | ||
_route.navigate([_auth.loginUrl]).then(); | ||
_message.error(`访问认证失败,错误码: ${error.status},详细信息: ${error.error.message}`); | ||
return throwError(() => error.error); | ||
} else { | ||
_message.error(`服务器访问错误,请稍后重试. 错误码: ${error.status},详细信息: ${error.error}`); | ||
} | ||
} | ||
console.error(`Backend returned code ${error.status}, body was: `, error.error); | ||
return throwError(() => error); | ||
}), | ||
); | ||
} | ||
|
||
/** | ||
* 认证令牌拦截器 | ||
* 在发送 HTTP 请求前,检查用户是否已登录。如果已登录,则在请求头中添加认证令牌。 | ||
* @param req - 原始 HTTP 请求 | ||
* @param next - 下一个处理程序函数 | ||
* @returns 返回一个 Observable,包含修改后的 HTTP 事件 | ||
*/ | ||
function authTokenInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> { | ||
const _auth = inject(AuthService); | ||
if (!_auth.isLogged()) { | ||
return next(req); | ||
} | ||
const newReq = req.clone({ | ||
headers: req.headers.set('Authorization', `Bearer ${_auth.authToken()}`), | ||
}); | ||
return next(newReq); | ||
} | ||
|
||
export const indexInterceptor = [defaultInterceptor, handleErrorInterceptor, authTokenInterceptor]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { type CanActivateFn, Router } from '@angular/router'; | ||
import { AuthService } from '.'; | ||
import { inject } from '@angular/core'; | ||
|
||
// 定义一个函数,用于判断用户是否已登录 | ||
export const authGuard: CanActivateFn = (route, state) => { | ||
const auth = inject(AuthService); | ||
const router = inject(Router); | ||
if (auth.isLogged()) { | ||
return true; | ||
} | ||
auth.redirectUrl = state.url; | ||
return router.parseUrl(auth.loginUrl); | ||
}; |
Oops, something went wrong.