mirror of https://github.com/abpframework/abp.git
committed by
GitHub
3 changed files with 365 additions and 3 deletions
@ -1,3 +1,179 @@ |
|||
## HTTP请求 |
|||
|
|||
TODO... |
|||
## 关于 HttpClient |
|||
|
|||
Angular具有很棒的 `HttpClient` 与后端服务进行通信. 它位于顶层,是[XMLHttpRequest Web API](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)的封装. 同时也是Angular建议用于任何HTTP请求的代理,在你的ABP项目中使用 `HttpClient` 是最佳做法. |
|||
|
|||
但是 `HttpClient` 将错误处理留给调用方,换句话说HTTP错误是通过手动处理的,通过挂接到返回的 `Observable` 的观察者中来处理. |
|||
|
|||
```js |
|||
getConfig() { |
|||
this.http.get(this.configUrl).subscribe( |
|||
config => this.updateConfig(config), |
|||
error => { |
|||
// Handle error here |
|||
}, |
|||
); |
|||
} |
|||
``` |
|||
|
|||
上面的代码尽管清晰灵活,但即使将错误处理委派给Store或任何其他注入. 以这种方式处理错误也是重复性的工作. |
|||
|
|||
`HttpInterceptor` 能够捕获 `HttpErrorResponse` 并可用于集中的错误处理. 然而,在必须放置错误处理程序(也就是拦截器)的情况下,需要额外的工作以及对Angular内部机制的理解. 检查[这个issue](https://github.com/angular/angular/issues/20203)了解详情. |
|||
|
|||
## RestService |
|||
|
|||
ABP核心模块有用于HTTP请求的实用程序服务: `RestService`. 除非另有明确配置,否则它将捕获HTTP错误并调度 `RestOccurError` 操作, 然后由 `ThemeSharedModule` 引入的 `ErrorHandler` 捕获此操作. 你应该已经在应用程序中导入了此模块,在使用 `RestService` 时,默认情况下将自动处理所有HTTP错误. |
|||
|
|||
### RestService 入门 |
|||
|
|||
为了使用 `RestService`, 你必须将它注入到你的类中. |
|||
|
|||
```js |
|||
import { RestService } from '@abp/ng.core'; |
|||
|
|||
@Injectable({ |
|||
/* class metadata here */ |
|||
}) |
|||
class DemoService { |
|||
constructor(private rest: RestService) {} |
|||
} |
|||
``` |
|||
|
|||
你不必在模块或组件/指令级别提供 `estService`,因为它已经在**根中**中提供了. |
|||
|
|||
### 如何使用RestService发出请求 |
|||
|
|||
你可以使用 `RestService` 的 `request` 方法来处理HTTP请求. 示例: |
|||
|
|||
```js |
|||
getFoo(id: number) { |
|||
const request: Rest.Request<null> = { |
|||
method: 'GET', |
|||
url: '/api/some/path/to/foo/' + id, |
|||
}; |
|||
|
|||
return this.rest.request<null, FooResponse>(request); |
|||
} |
|||
``` |
|||
|
|||
`request` 方法始终返回 `Observable<T>`. 无论何时使用 `getFoo` 方法,都可以执行以下操作: |
|||
|
|||
```js |
|||
doSomethingWithFoo(id: number) { |
|||
this.demoService.getFoo(id).subscribe( |
|||
foo => { |
|||
// Do something with foo. |
|||
} |
|||
) |
|||
} |
|||
``` |
|||
|
|||
**你不必担心关于取消订阅**. `RestService` 在内部使用 `HttpClient`,因此它返回的每个可观察对象都是有限的可观察对象,成功或出错后将自动关闭订阅. |
|||
|
|||
如你所见,`request` 方法获取一个具有 `Rest.Reques<T>` 类型的请求选项对象. 此泛型类型需要请求主体的接口. 当没有正文时,例如在 `GET` 或 `DELETE` 请求中,你可以传递 `null`. 示例: |
|||
|
|||
```js |
|||
postFoo(body: Foo) { |
|||
const request: Rest.Request<Foo> = { |
|||
method: 'POST', |
|||
url: '/api/some/path/to/foo', |
|||
body |
|||
}; |
|||
|
|||
return this.rest.request<Foo, FooResponse>(request); |
|||
} |
|||
``` |
|||
|
|||
你可以在[此处检查](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23)完整的 `Rest.Request<T>` 类型,与Angular中的[HttpRequest](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L23)类相比只有很少的改动. |
|||
|
|||
### 如何禁用RestService的默认错误处理程序 |
|||
|
|||
默认 `request` 方法始终处理错误. 让我们看看如何改变这种行为并由自己处理错误: |
|||
|
|||
```js |
|||
deleteFoo(id: number) { |
|||
const request: Rest.Request<null> = { |
|||
method: 'DELETE', |
|||
url: '/api/some/path/to/foo/' + id, |
|||
}; |
|||
|
|||
return this.rest.request<null, void>(request, { skipHandleError: true }); |
|||
} |
|||
``` |
|||
|
|||
`skipHandleError` 配置选项设置为 `true` 时,禁用错误处理程序,并返回 `observable` 引发错误,你可以在订阅中捕获该错误. |
|||
|
|||
```js |
|||
removeFooFromList(id: number) { |
|||
this.demoService.deleteFoo(id).subscribe( |
|||
foo => { |
|||
// Do something with foo. |
|||
}, |
|||
error => { |
|||
// Do something with error. |
|||
} |
|||
) |
|||
} |
|||
``` |
|||
|
|||
### 如何从应用程序配置获取特定的API端点 |
|||
|
|||
`request` 方法接收到的另一个配置选项是 `apiName` (在v2.4中可用),它用于从应用程序配置获取特定的模块端点. |
|||
|
|||
```js |
|||
putFoo(body: Foo, id: string) { |
|||
const request: Rest.Request<Foo> = { |
|||
method: 'PUT', |
|||
url: '/' + id, |
|||
body |
|||
}; |
|||
|
|||
return this.rest.request<Foo, void>(request, {apiName: 'foo'}); |
|||
} |
|||
``` |
|||
|
|||
上面的putFoo将请求 `https://localhost:44305/api/some/path/to/foo/{id}` 当环境变量如下: |
|||
|
|||
```js |
|||
// environment.ts |
|||
|
|||
export const environment = { |
|||
apis: { |
|||
default: { |
|||
url: 'https://localhost:44305', |
|||
}, |
|||
foo: { |
|||
url: 'https://localhost:44305/api/some/path/to/foo', |
|||
}, |
|||
}, |
|||
|
|||
/* rest of the environment variables here */ |
|||
} |
|||
``` |
|||
|
|||
### 如何观察响应对象或HTTP事件而不是正文 |
|||
|
|||
`RestService` 假定你通常对响应的正文感兴趣,默认情况下将 `observe` 属性设置为 `body`. 但是有时你可能对其他内容(例如自定义标头)非常感兴趣. 为此, `request` 方法在 `config` 对象中接收 `watch` 属性. |
|||
|
|||
```js |
|||
getSomeCustomHeaderValue() { |
|||
const request: Rest.Request<null> = { |
|||
method: 'GET', |
|||
url: '/api/some/path/that/sends/some-custom-header', |
|||
}; |
|||
|
|||
return this.rest.request<null, HttpResponse<any>>( |
|||
request, |
|||
{observe: Rest.Observe.Response}, |
|||
).pipe( |
|||
map(response => response.headers.get('Some-Custom-Header')) |
|||
); |
|||
} |
|||
``` |
|||
|
|||
你可以在[此处](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/rest.ts#L10)找到 `Rest.Observe` 枚举. |
|||
|
|||
## 下一步是什么? |
|||
|
|||
* [本地化](./Localization.md) |
|||
@ -1,3 +1,189 @@ |
|||
# 如何懒加载 Scripts 与 Styles |
|||
|
|||
TODO... |
|||
你可以使用@abp/ng.core包中的 `LazyLoadService` 以简单明了的方式延迟加载脚本和样式. |
|||
|
|||
## 入门 |
|||
|
|||
你不必在模块或组件/指令级别提供 `LazyLoadService`,因为它已经在**根中**中提供了. 你可以在组件,指令或服务中注入并使用它. |
|||
|
|||
```js |
|||
import { LazyLoadService } from '@abp/ng.core'; |
|||
|
|||
@Component({ |
|||
/* class metadata here */ |
|||
}) |
|||
class DemoComponent { |
|||
constructor(private lazyLoadService: LazyLoadService) {} |
|||
} |
|||
``` |
|||
|
|||
## 用法 |
|||
|
|||
你可以使用 `LazyLoadService` 的 `load` 方法在DOM中的所需位置创建 `<script>` 或 `<link>` 元素并强制浏览器下载目标资源. |
|||
|
|||
### 如何加载 Scripts |
|||
|
|||
`load` 方法的第一个参数需要一个 `LoadingStrategy`. 如果传递 `ScriptLoadingStrategy` 实例,`LazyLoadService` 将使用给定的 `src` 创建一个 `<script>` 元素并放置在指定的DOM位置. |
|||
|
|||
```js |
|||
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core'; |
|||
|
|||
@Component({ |
|||
template: ` |
|||
<some-component *ngIf="libraryLoaded$ | async"></some-component> |
|||
` |
|||
}) |
|||
class DemoComponent { |
|||
libraryLoaded$ = this.lazyLoad.load( |
|||
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/some-library.js'), |
|||
); |
|||
|
|||
constructor(private lazyLoadService: LazyLoadService) {} |
|||
} |
|||
``` |
|||
|
|||
`load` 方法返回一个 `observable`,你可以在组件中或通过 `AsyncPipe` 订阅它. 在上面的示例中**仅当脚本成功加载或之前已经加载脚本时**, `NgIf` 指令才会呈现 `<some-component>`. |
|||
|
|||
> 你可以使用 `async` 管道在模板中多次订阅,脚本将仅加载一次 |
|||
|
|||
请参阅[LoadingStrategy](./Loading-Strategy.md)查看所有可用的加载策略以及如何构建自己的加载策略. |
|||
|
|||
### 如何加载 Styles |
|||
|
|||
如果传递给 `load` 方法第一个参数为 `StyleLoadingStrategy` 实例,`LazyLoadService` 将使用给定的 `href` 创建一个 `<link>` 元素并放置在指定的DOM位置. |
|||
|
|||
```js |
|||
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core'; |
|||
|
|||
@Component({ |
|||
template: ` |
|||
<some-component *ngIf="stylesLoaded$ | async"></some-component> |
|||
` |
|||
}) |
|||
class DemoComponent { |
|||
stylesLoaded$ = this.lazyLoad.load( |
|||
LOADING_STRATEGY.AppendAnonymousStyleToHead('/assets/some-styles.css'), |
|||
); |
|||
|
|||
constructor(private lazyLoadService: LazyLoadService) {} |
|||
} |
|||
``` |
|||
|
|||
`load` 方法返回一个 `observable`,你可以在组件中或通过 `AsyncPipe` 订阅它. 在上面的示例中**仅当样式成功加载或之前已经加载样式时**, `NgIf` 指令才会呈现 `<some-component>`. |
|||
|
|||
> 你可以使用 `async` 管道在模板中多次订阅,样式将仅加载一次 |
|||
|
|||
请参阅[LoadingStrategy](./Loading-Strategy.md)查看所有可用的加载策略以及如何构建自己的加载策略. |
|||
|
|||
### 高级用法 |
|||
|
|||
你有**很大的自由度来定义延迟加载的工作方式**. 示例: |
|||
|
|||
```js |
|||
const domStrategy = DOM_STRATEGY.PrependToHead(); |
|||
|
|||
const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.Anonymous( |
|||
'sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh', |
|||
); |
|||
|
|||
const loadingStrategy = new StyleLoadingStrategy( |
|||
'https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css', |
|||
domStrategy, |
|||
crossOriginStrategy, |
|||
); |
|||
|
|||
this.lazyLoad.load(loadingStrategy, 1, 2000); |
|||
``` |
|||
|
|||
此代码将创建具有给定URL和完整性哈希的 `<link>` 元素,将其插入到 `<head>` 元素的顶部,如果第一次尝试失败,则在2秒后重试一次. |
|||
|
|||
一个常见的用例是在**使用功能之前加载多个脚本/样式**: |
|||
|
|||
```js |
|||
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core'; |
|||
import { frokJoin } from 'rxjs'; |
|||
|
|||
@Component({ |
|||
template: ` |
|||
<some-component *ngIf="scriptsAndStylesLoaded$ | async"></some-component> |
|||
` |
|||
}) |
|||
class DemoComponent { |
|||
private stylesLoaded$ = forkJoin( |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.PrependAnonymousStyleToHead('/assets/library-dark-theme.css'), |
|||
), |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.PrependAnonymousStyleToHead('/assets/library.css'), |
|||
), |
|||
); |
|||
|
|||
private scriptsLoaded$ = forkJoin( |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/library.js'), |
|||
), |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/other-library.css'), |
|||
), |
|||
); |
|||
|
|||
scriptsAndStylesLoaded$ = forkJoin(this.scriptsLoaded$, this.stylesLoaded$); |
|||
|
|||
constructor(private lazyLoadService: LazyLoadService) {} |
|||
} |
|||
``` |
|||
|
|||
RxJS `forkJoin` 并行加载所有脚本和样式,并仅在加载所有脚本和样式时才放行. 因此放置 `<some-component>` 时,所有必需的依赖项都可用的. |
|||
|
|||
> 注意到我们在文档头上添加了样式吗? 有时这是必需的因为你的应用程序样式可能会覆盖某些库样式. 在这种情况下你必须注意前置样式的顺序. 它们将一一放置,**并且在放置前,最后一个放置在最上面**. |
|||
|
|||
另一个常见的用例是**按顺序加载依赖脚本**: |
|||
|
|||
```js |
|||
import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core'; |
|||
import { concat } from 'rxjs'; |
|||
|
|||
@Component({ |
|||
template: ` |
|||
<some-component *ngIf="scriptsLoaded$ | async"></some-component> |
|||
` |
|||
}) |
|||
class DemoComponent { |
|||
scriptsLoaded$ = concat( |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.PrependAnonymousScriptToHead('/assets/library.js'), |
|||
), |
|||
this.lazyLoad.load( |
|||
LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/script-that-requires-library.js'), |
|||
), |
|||
); |
|||
|
|||
constructor(private lazyLoadService: LazyLoadService) {} |
|||
} |
|||
``` |
|||
|
|||
在此示例中,第二个文件需要预先加载第一个文件, RxJS `concat` 函数将允许你以给定的顺序一个接一个地加载所有脚本,并且仅在加载所有脚本时放行. |
|||
|
|||
## API |
|||
|
|||
### loaded |
|||
|
|||
```js |
|||
loaded: Set<string> |
|||
``` |
|||
|
|||
所有以前加载的路径都可以通过此属性访问. 它是一个简单的[JavaScript集] |
|||
|
|||
### load |
|||
|
|||
```js |
|||
load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable<Event> |
|||
``` |
|||
|
|||
- `strategy` 是主要参数,上面已经介绍过. |
|||
- `retryTimes` 定义加载失败前再次尝试多少次(默认值:2). |
|||
- `retryDelay` 定义重试之间的延迟(默认为1000). |
|||
|
|||
## 下一步是什么? |
|||
|
|||
- [DomInsertionService](./Dom-Insertion-Service.md) |
|||
Loading…
Reference in new issue