mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs: Migrate HttpClient guide and its code examples (/http) to standalone (#51400)
**Unit Testing Code Does Not Compile** The compilation error is: ``` The compilation error is ./src/testing/http-client.spec.ts - Error: Module build failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js): Error: /home/projects/obk3vc--run/src/testing/http-client.spec.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property. ``` I’m not sure what to say about unit testing HTTP in a full Standalone app. Is it different? _This is the only known remaining defect in this conversion of HTTP to Standalone._ **Edited content of `http-request-data-from-server.md`** The current version of this page is confusing. In particular * It tells readers they **should always unsubscribe** from the HttpClient method calls. This is *not true* and this example doesn't even do it. I replaced this instruction with more nuanced advice and an explanation of why it is OK to not unsubscribe to HttpClient methods. * There is a "helpful" note about using the RxJS `map` operator to transform the response. This is *not "helpfulf"*. It is *confusing* because the sample doesn't use `map` anywhere. It was unnecessary here, even if it might be helpful elsewhere. I removed this note. * The "Requesting a typed response" section seemed unclear to me, particularly because the guide begins with a `get` request that already has the `Config` return type specification. My revision attempts to make this more clear. * The bold "callout" about the `observe` and `responseType` options appears out of nowhere after "Requesting a typed response". It's disconcerting at best. I moved it to the bottom of the page and linked to it from the `options` discussion at the top. I made a few other revisions that I hope improve the readability of this page. **Corrected `http-make-jsonp-request.md`** The JSONP example, handwritten in the guide page, would not have compiled. I added one that does to `heroes.service.ts` and displayed it on this page. **Corrected `http-handle-request-errors.md` This page ended with a section called "Sending data to a server" that introduces PUT, POST, and DELETE. These features have nothing to do with error handling and the verbiage here duplicates the opening paragraphs of the next topic which does: "Send data to a server". So I deleted this section from the error handling guide page. **Archived http-setup-server-communication.md** `http-setup-server-communication.md` appears to be the original long document that has since been divided over the other pages in this folder. It shouldn’t be in the reader’s flow. I did update it for Standalone. But I also removed it from left-nav and marked as archived. PR Close #51400
This commit is contained in:
parent
49415e8221
commit
8ef5cc680d
27 changed files with 352 additions and 288 deletions
|
|
@ -1,8 +1,27 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { ConfigComponent } from './config/config.component';
|
||||
import { DownloaderComponent } from './downloader/downloader.component';
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
import { MessagesComponent } from './messages/messages.component';
|
||||
import { PackageSearchComponent } from './package-search/package-search.component';
|
||||
import { UploaderComponent } from './uploader/uploader.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
ConfigComponent,
|
||||
DownloaderComponent,
|
||||
HeroesComponent,
|
||||
MessagesComponent,
|
||||
PackageSearchComponent,
|
||||
UploaderComponent
|
||||
],
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
// #docplaster
|
||||
// #docregion sketch
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
// #enddocregion sketch
|
||||
import { FormsModule } from '@angular/forms';
|
||||
// #docregion sketch
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
// #enddocregion sketch
|
||||
import { HttpClientXsrfModule } from '@angular/common/http';
|
||||
|
||||
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { InMemoryDataService } from './in-memory-data.service';
|
||||
|
||||
import { RequestCache, RequestCacheWithMap } from './request-cache.service';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AuthService } from './auth.service';
|
||||
import { ConfigComponent } from './config/config.component';
|
||||
import { DownloaderComponent } from './downloader/downloader.component';
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
import { HttpErrorHandler } from './http-error-handler.service';
|
||||
import { MessageService } from './message.service';
|
||||
import { MessagesComponent } from './messages/messages.component';
|
||||
import { PackageSearchComponent } from './package-search/package-search.component';
|
||||
import { UploaderComponent } from './uploader/uploader.component';
|
||||
|
||||
import { httpInterceptorProviders } from './http-interceptors/index';
|
||||
// #docregion sketch
|
||||
|
||||
@NgModule({
|
||||
// #docregion xsrf
|
||||
imports: [
|
||||
// #enddocregion xsrf
|
||||
BrowserModule,
|
||||
// #enddocregion sketch
|
||||
FormsModule,
|
||||
// #docregion sketch
|
||||
// import HttpClientModule after BrowserModule.
|
||||
// #docregion xsrf
|
||||
HttpClientModule,
|
||||
// #enddocregion sketch
|
||||
HttpClientXsrfModule.withOptions({
|
||||
cookieName: 'My-Xsrf-Cookie',
|
||||
headerName: 'My-Xsrf-Header',
|
||||
}),
|
||||
// #enddocregion xsrf
|
||||
|
||||
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
|
||||
// and returns simulated server responses.
|
||||
// Remove it when a real server is ready to receive requests.
|
||||
HttpClientInMemoryWebApiModule.forRoot(
|
||||
InMemoryDataService, {
|
||||
dataEncapsulation: false,
|
||||
passThruUnknownUrl: true,
|
||||
put204: false // return entity after PUT/update
|
||||
}
|
||||
)
|
||||
// #docregion sketch, xsrf
|
||||
],
|
||||
// #enddocregion xsrf
|
||||
declarations: [
|
||||
AppComponent,
|
||||
// #enddocregion sketch
|
||||
ConfigComponent,
|
||||
DownloaderComponent,
|
||||
HeroesComponent,
|
||||
MessagesComponent,
|
||||
UploaderComponent,
|
||||
PackageSearchComponent,
|
||||
// #docregion sketch
|
||||
],
|
||||
// #enddocregion sketch
|
||||
// #docregion interceptor-providers
|
||||
providers: [
|
||||
// #enddocregion interceptor-providers
|
||||
AuthService,
|
||||
HttpErrorHandler,
|
||||
MessageService,
|
||||
{ provide: RequestCache, useClass: RequestCacheWithMap },
|
||||
// #docregion interceptor-providers
|
||||
httpInterceptorProviders
|
||||
],
|
||||
// #enddocregion interceptor-providers
|
||||
// #docregion sketch
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion sketch
|
||||
|
|
@ -1,55 +1,72 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Config, ConfigService } from './config.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-config',
|
||||
templateUrl: './config.component.html',
|
||||
imports: [ CommonModule ],
|
||||
providers: [ ConfigService ],
|
||||
styles: ['.error { color: #b30000; }']
|
||||
})
|
||||
export class ConfigComponent {
|
||||
error: any;
|
||||
headers: string[] = [];
|
||||
// #docregion v2
|
||||
// #docregion typed_response, v2
|
||||
config: Config | undefined;
|
||||
|
||||
// #enddocregion v2
|
||||
// #enddocregion typed_response, v2
|
||||
// #docregion v1
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
// #enddocregion v1
|
||||
clear() {
|
||||
this.config = undefined;
|
||||
this.error = undefined;
|
||||
this.headers = [];
|
||||
}
|
||||
|
||||
// #docregion v1, v2
|
||||
// #docregion v1, v2, typed_response, untyped_response
|
||||
showConfig() {
|
||||
this.configService.getConfig()
|
||||
// #enddocregion v1, v2
|
||||
// #enddocregion v1, v2, typed_response, untyped_response
|
||||
.subscribe({
|
||||
next: (data: Config) => this.config = { ...data }, // success path
|
||||
next: data => this.config = { ...data }, // success path
|
||||
error: error => this.error = error, // error path
|
||||
});
|
||||
}
|
||||
|
||||
showConfig_v1() {
|
||||
this.configService.getConfig_1()
|
||||
// #docregion v1
|
||||
.subscribe((data: Config) => this.config = {
|
||||
// #docregion typed_response, v1
|
||||
.subscribe(data => this.config = {
|
||||
heroesUrl: data.heroesUrl,
|
||||
textfile: data.textfile,
|
||||
date: data.date,
|
||||
});
|
||||
}
|
||||
// #enddocregion v1
|
||||
// #enddocregion typed_response, v1
|
||||
|
||||
showConfig_untyped_response() {
|
||||
this.configService.getConfig_untyped_response()
|
||||
// #docregion untyped_response
|
||||
.subscribe(data => this.config = {
|
||||
heroesUrl: (data as any).heroesUrl,
|
||||
textfile: (data as any).textfile,
|
||||
date: (data as any).date,
|
||||
});
|
||||
}
|
||||
// #enddocregion untyped_response
|
||||
|
||||
|
||||
|
||||
showConfig_v2() {
|
||||
this.configService.getConfig()
|
||||
// #docregion v2
|
||||
// clone the data object, using its known Config shape
|
||||
.subscribe((data: Config) => this.config = { ...data });
|
||||
.subscribe(data => this.config = { ...data });
|
||||
}
|
||||
// #enddocregion v2
|
||||
|
||||
|
|
@ -76,4 +93,4 @@ export class ConfigComponent {
|
|||
return val instanceof Date ? 'date' : Array.isArray(val) ? 'array' : typeof val;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ import { HttpClient } from '@angular/common/http';
|
|||
// #enddocregion proto
|
||||
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
|
||||
|
||||
// #docregion rxjs-imports
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError, retry } from 'rxjs/operators';
|
||||
// #enddocregion rxjs-imports
|
||||
import { Observable, catchError, retry, throwError } from 'rxjs';
|
||||
|
||||
// #docregion config-interface
|
||||
export interface Config {
|
||||
|
|
@ -63,6 +60,13 @@ export class ConfigService {
|
|||
}
|
||||
// #enddocregion getConfig_3
|
||||
|
||||
|
||||
getConfig_untyped_response() {
|
||||
// #docregion untyped_response
|
||||
return this.http.get(this.configUrl);
|
||||
// #enddocregion untyped_response
|
||||
}
|
||||
|
||||
// #docregion getConfigResponse
|
||||
getConfigResponse(): Observable<HttpResponse<Config>> {
|
||||
return this.http.get<Config>(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DownloaderService } from './downloader.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-downloader',
|
||||
templateUrl: './downloader.component.html',
|
||||
imports: [ CommonModule ],
|
||||
providers: [ DownloaderService ]
|
||||
})
|
||||
export class DownloaderComponent {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Hero } from './hero';
|
||||
import { HeroesService } from './heroes.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-heroes',
|
||||
templateUrl: './heroes.component.html',
|
||||
imports: [ CommonModule, FormsModule ],
|
||||
providers: [HeroesService],
|
||||
styleUrls: ['./heroes.component.css']
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import { HttpHeaders } from '@angular/common/http';
|
|||
|
||||
// #enddocregion http-options
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { Observable, catchError, map } from 'rxjs';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
|
||||
|
|
@ -40,9 +39,10 @@ export class HeroesService {
|
|||
);
|
||||
}
|
||||
|
||||
// #docregion searchHeroes
|
||||
// #docregion searchHeroes, searchHeroesJsonp
|
||||
/* GET heroes whose name contains search term */
|
||||
searchHeroes(term: string): Observable<Hero[]> {
|
||||
// #enddocregion searchHeroesJsonp
|
||||
term = term.trim();
|
||||
|
||||
// Add safe, URL encoded search parameter if there is a search term
|
||||
|
|
@ -56,6 +56,27 @@ export class HeroesService {
|
|||
}
|
||||
// #enddocregion searchHeroes
|
||||
|
||||
// This JSONP example doesn't run. It is for the JSONP documentation only.
|
||||
/** Imaginary API in a different domain that supports JSONP. */
|
||||
heroesSearchUrl = 'https://heroes.com/search';
|
||||
|
||||
/** Does whatever is necessary to convert the result from API to Heroes */
|
||||
jsonpResultToHeroes(result: any) { return result as Hero[]; }
|
||||
|
||||
/* GET heroes (using JSONP) whose name contains search term */
|
||||
searchHeroesJsonp(term: string): Observable<Hero[]> {
|
||||
// #docregion searchHeroesJsonp
|
||||
term = term.trim();
|
||||
|
||||
const heroesUrl = `${this.heroesSearchUrl}?${term}`;
|
||||
return this.http.jsonp(heroesUrl, 'callback')
|
||||
.pipe(
|
||||
map(result => this.jsonpResultToHeroes(result)),
|
||||
catchError(this.handleError('searchHeroes', []))
|
||||
);
|
||||
}
|
||||
// #enddocregion searchHeroesJsonp
|
||||
|
||||
//////// Save methods //////////
|
||||
|
||||
// #docregion addHero
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// #docplaster
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
|
||||
HttpInterceptor, HttpHandler, HttpRequest
|
||||
} from '@angular/common/http';
|
||||
|
||||
// #docregion
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// #docplaster
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpEvent, HttpHeaders, HttpRequest, HttpResponse,
|
||||
HttpInterceptor, HttpHandler
|
||||
HttpEvent, HttpRequest, HttpResponse, HttpInterceptor, HttpHandler
|
||||
} from '@angular/common/http';
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { startWith, tap } from 'rxjs/operators';
|
||||
import { Observable, of, startWith, tap } from 'rxjs';
|
||||
|
||||
import { RequestCache } from '../request-cache.service';
|
||||
import { searchUrl } from '../package-search/package-search.service';
|
||||
|
|
|
|||
|
|
@ -1,39 +1,33 @@
|
|||
// #docplaster
|
||||
// #docregion interceptor-providers
|
||||
/* "Barrel" of Http Interceptors */
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
// #enddocregion interceptor-providers
|
||||
import { CustomJsonInterceptor , CustomJsonParser, JsonParser} from './custom-json-interceptor';
|
||||
|
||||
// #docregion interceptor-providers
|
||||
import { AuthInterceptor } from './auth-interceptor';
|
||||
import { CachingInterceptor } from './caching-interceptor';
|
||||
import { CustomJsonInterceptor , CustomJsonParser, JsonParser} from './custom-json-interceptor';
|
||||
import { EnsureHttpsInterceptor } from './ensure-https-interceptor';
|
||||
import { LoggingInterceptor } from './logging-interceptor';
|
||||
// #docregion interceptor-providers
|
||||
import { NoopInterceptor } from './noop-interceptor';
|
||||
// #enddocregion interceptor-providers
|
||||
import { TrimNameInterceptor } from './trim-name-interceptor';
|
||||
import { UploadInterceptor } from './upload-interceptor';
|
||||
import { RetryInterceptor } from './retry-interceptor';
|
||||
// #docregion interceptor-providers
|
||||
|
||||
/** Http interceptor providers in outside-in order */
|
||||
/** Array of Http interceptor providers in outside-in order */
|
||||
export const httpInterceptorProviders = [
|
||||
// #docregion noop-provider
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
|
||||
// #enddocregion noop-provider, interceptor-providers
|
||||
// #enddocregion interceptor-providers
|
||||
// #docregion custom-json-interceptor
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: CustomJsonInterceptor, multi: true },
|
||||
{ provide: JsonParser, useClass: CustomJsonParser },
|
||||
// #enddocregion custom-json-interceptor
|
||||
|
||||
// #docregion interceptor-providers
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: UploadInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true },
|
||||
|
||||
// #docregion interceptor-providers
|
||||
];
|
||||
// #enddocregion interceptor-providers
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpEvent, HttpInterceptor, HttpHandler,
|
||||
HttpRequest, HttpResponse
|
||||
HttpInterceptor, HttpHandler, HttpRequest, HttpResponse
|
||||
} from '@angular/common/http';
|
||||
|
||||
// #docregion excerpt
|
||||
import { finalize, tap } from 'rxjs/operators';
|
||||
import { finalize, tap } from 'rxjs';
|
||||
import { MessageService } from '../message.service';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -23,7 +22,7 @@ export class LoggingInterceptor implements HttpInterceptor {
|
|||
// Succeeds when there is a response; ignore other events
|
||||
next: (event) => (ok = event instanceof HttpResponse ? 'succeeded' : ''),
|
||||
// Operation failed; error is an HttpErrorResponse
|
||||
error: (error) => (ok = 'failed')
|
||||
error: (_error) => (ok = 'failed')
|
||||
}),
|
||||
// Log when response observable either completes or errors
|
||||
finalize(() => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// #docregion noop
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
|
||||
|
|
@ -8,9 +9,20 @@ import { Observable } from 'rxjs';
|
|||
/** Pass untouched request through to the next request handler. */
|
||||
@Injectable()
|
||||
export class NoopInterceptor implements HttpInterceptor {
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler):
|
||||
Observable<HttpEvent<any>> {
|
||||
return next.handle(req);
|
||||
}
|
||||
}
|
||||
// #enddocregion noop
|
||||
|
||||
// #docregion noop-provider
|
||||
import { Provider } from '@angular/core';
|
||||
|
||||
// Injection token for the Http Interceptors multi-provider
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
/** Provider for the Noop Interceptor. */
|
||||
export const noopInterceptorProvider: Provider =
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true };
|
||||
// #enddocregion noop-provider
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MessageService } from '../message.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
imports: [ CommonModule ],
|
||||
styleUrls: ['./messages.component.css']
|
||||
})
|
||||
export class MessagesComponent {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
||||
|
|
@ -6,8 +7,10 @@ import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
|||
import { NpmPackageInfo, PackageSearchService } from './package-search.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-package-search',
|
||||
templateUrl: './package-search.component.html',
|
||||
imports: [ CommonModule ],
|
||||
styles: ['input { margin-bottom: .5rem; }'],
|
||||
providers: [ PackageSearchService ]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { UploaderService } from './uploader.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-uploader',
|
||||
templateUrl: './uploader.component.html',
|
||||
imports: [ CommonModule, FormsModule ],
|
||||
styles: ['input[type=file] { font-size: 1.2rem; margin-top: 1rem; display: block; }'],
|
||||
providers: [ UploaderService ]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,74 @@
|
|||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
// #docplaster
|
||||
// #docregion sketch
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
// #enddocregion sketch
|
||||
import { provideProtractorTestingSupport } from '@angular/platform-browser';
|
||||
// #docregion sketch
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { importProvidersFrom } from '@angular/core';
|
||||
// #enddocregion sketch
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { HttpClientJsonpModule } from '@angular/common/http';
|
||||
import { HttpClientXsrfModule } from '@angular/common/http';
|
||||
import { httpInterceptorProviders } from './app/http-interceptors/index';
|
||||
import { noopInterceptorProvider } from './app/http-interceptors/noop-interceptor';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
// #region example helper services; not shown in docs
|
||||
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { InMemoryDataService } from './app/in-memory-data.service';
|
||||
|
||||
import { AuthService } from './app/auth.service';
|
||||
import { HttpErrorHandler } from './app/http-error-handler.service';
|
||||
import { MessageService } from './app/message.service';
|
||||
import { RequestCache, RequestCacheWithMap } from './app/request-cache.service';
|
||||
// #endregion example helper services; not shown in docs
|
||||
|
||||
// #docregion sketch
|
||||
|
||||
import {AppComponent} from './app/app.component';
|
||||
|
||||
// #docregion interceptor-providers, jsonp, noop-provider, xsrf
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
importProvidersFrom(HttpClientModule),
|
||||
// #enddocregion interceptor-providers, jsonp, noop-provider, sketch, xsrf
|
||||
// #docregion jsonp
|
||||
importProvidersFrom(HttpClientJsonpModule),
|
||||
// #enddocregion jsonp
|
||||
// #docregion noop-provider
|
||||
noopInterceptorProvider,
|
||||
// #enddocregion noop-provider
|
||||
// #docregion interceptor-providers
|
||||
httpInterceptorProviders,
|
||||
// #enddocregion interceptor-providers
|
||||
// #docregion xsrf
|
||||
importProvidersFrom(
|
||||
HttpClientXsrfModule.withOptions({
|
||||
cookieName: 'My-Xsrf-Cookie',
|
||||
headerName: 'My-Xsrf-Header',
|
||||
})
|
||||
),
|
||||
// #enddocregion xsrf
|
||||
|
||||
AuthService,
|
||||
HttpErrorHandler,
|
||||
MessageService,
|
||||
{ provide: RequestCache, useClass: RequestCacheWithMap },
|
||||
|
||||
importProvidersFrom(
|
||||
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
|
||||
// and returns simulated server responses.
|
||||
// Remove it when a real server is ready to receive requests.
|
||||
HttpClientInMemoryWebApiModule.forRoot(
|
||||
InMemoryDataService, {
|
||||
dataEncapsulation: false,
|
||||
passThruUnknownUrl: true,
|
||||
put204: false // return entity after PUT/update
|
||||
}
|
||||
)
|
||||
),
|
||||
provideProtractorTestingSupport(), // essential for e2e testing
|
||||
// #docregion interceptor-providers, jsonp, noop-provider, sketch, xsrf
|
||||
]
|
||||
});
|
||||
// #enddocregion interceptor-providers, jsonp, noop-provider, sketch, xsrf
|
||||
|
|
|
|||
|
|
@ -50,11 +50,4 @@ The following example shows how to pipe a failed request to the `retry()` operat
|
|||
|
||||
<code-example header="app/config/config.service.ts (getConfig with retry)" path="http/src/app/config/config.service.ts" region="getConfig"></code-example>
|
||||
|
||||
## Sending data to a server
|
||||
|
||||
In addition to fetching data from a server, `HttpClient` supports other HTTP methods such as PUT, POST, and DELETE, which you can use to modify the remote data.
|
||||
|
||||
The sample app for this guide includes an abridged version of the "Tour of Heroes" example that fetches heroes and enables users to add, delete, and update them.
|
||||
The following sections show examples of the data-update methods from the sample's `HeroesService`.
|
||||
|
||||
@reviewed 2023-08-14
|
||||
@reviewed 2023-08-29
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ To implement an interceptor, declare a class that implements the `intercept()` m
|
|||
|
||||
Here is a do-nothing `noop` interceptor that passes the request through without touching it:
|
||||
|
||||
<code-example header="app/http-interceptors/noop-interceptor.ts" path="http/src/app/http-interceptors/noop-interceptor.ts"></code-example>
|
||||
<code-example header="app/http-interceptors/noop-interceptor.ts" path="http/src/app/http-interceptors/noop-interceptor.ts" region="noop"></code-example>
|
||||
|
||||
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
|
||||
In this sense, each interceptor is fully capable of handling the request entirely by itself.
|
||||
|
|
@ -46,42 +46,49 @@ This is a common middleware pattern found in frameworks such as Express.js.
|
|||
|
||||
## Provide the interceptor
|
||||
|
||||
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
|
||||
Like other services, you must provide the interceptor class before the app can use it.
|
||||
The `NoopInterceptor` is like a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
|
||||
As with other services, you must provide the interceptor class before the app can use it.
|
||||
|
||||
Because interceptors are optional dependencies of the `HttpClient` service, you must provide them in the same injector or a parent of the injector that provides `HttpClient`.
|
||||
Interceptors provided *after* DI creates the `HttpClient` are ignored.
|
||||
|
||||
This app provides `HttpClient` in the app's root injector, as a side-effect of importing the `HttpClientModule` in `AppModule`.
|
||||
You should provide interceptors in `AppModule` as well.
|
||||
|
||||
After importing the `HTTP_INTERCEPTORS` injection token from `@angular/common/http`, write the `NoopInterceptor` provider like this:
|
||||
|
||||
<code-example path="http/src/app/http-interceptors/index.ts" region="noop-provider"></code-example>
|
||||
Write a provider for it like this one:
|
||||
<code-example path="http/src/app/http-interceptors/noop-interceptor.ts" region="noop-provider"></code-example>
|
||||
|
||||
Notice the `multi: true` option.
|
||||
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a *multiprovider* that injects an array of values, rather than a single value.
|
||||
|
||||
You *could* add this provider directly to the providers array of the `AppModule`.
|
||||
However, it's rather verbose and there's a good chance that you'll create more interceptors and provide them in the same way.
|
||||
Because interceptors are optional dependencies of the `HttpClient` service, you must provide them in the same injector or a parent of the injector that provides `HttpClient`.
|
||||
Interceptors provided *after* DI creates the `HttpClient` are ignored.
|
||||
|
||||
This app provides `HttpClient` in the app's root injector by adding the `HttpClientModule` to the `providers` array of the `boostrapApplication()` in `main.ts`.
|
||||
You should provide interceptors there as well.
|
||||
|
||||
<code-example path="http/src/main.ts" region="noop-provider"></code-example>
|
||||
|
||||
## Providing many interceptors
|
||||
|
||||
There's a good chance that you'll create more interceptors.
|
||||
|
||||
You *could* add each provider to the `providers` array of the `boostrapApplication()` as you did for the `NoopInterceptor`.
|
||||
|
||||
That's rather verbose and there's a good chance that you'll make a bookkeeping mistake trying to remember to add each one.
|
||||
|
||||
You must also pay [close attention to the order](#interceptor-order) in which you provide these interceptors.
|
||||
|
||||
Consider creating a "barrel" file that gathers all the interceptor providers into an `httpInterceptorProviders` array, starting with this first one, the `NoopInterceptor`.
|
||||
Consider creating a "barrel" file that gathers _all the interceptor providers_ into a single `httpInterceptorProviders` array.
|
||||
|
||||
<code-example header="app/http-interceptors/index.ts" path="http/src/app/http-interceptors/index.ts" region="interceptor-providers"></code-example>
|
||||
|
||||
Then import and add it to the `AppModule` `providers array` like this:
|
||||
|
||||
<code-example header="app/app.module.ts (interceptor providers)" path="http/src/app/app.module.ts" region="interceptor-providers"></code-example>
|
||||
|
||||
As you create new interceptors, add them to the `httpInterceptorProviders` array and you won't have to revisit the `AppModule`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
There are many more interceptors in the complete sample code.
|
||||
These interceptors are defined in the complete sample code.
|
||||
|
||||
</div>
|
||||
|
||||
Then import this array and add it to the `bootstrapApplication()` `providers` in `main.ts` like this:
|
||||
|
||||
<code-example header="main.ts (interceptor providers)" path="http/src/main.ts" region="interceptor-providers"></code-example>
|
||||
|
||||
As you create new interceptors, add them to the `httpInterceptorProviders` array and you won't have to revisit `main.ts`.
|
||||
|
||||
## Interceptor order
|
||||
|
||||
Angular applies interceptors in the order that you provide them.
|
||||
|
|
@ -186,4 +193,4 @@ newReq = req.clone({ body: null }); // clear the body
|
|||
|
||||
</code-example>
|
||||
|
||||
@reviewed 2023-03-16
|
||||
@reviewed 2023-08-16
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ Here is a custom JsonParser that has a special date reviver.
|
|||
|
||||
<code-example header="app/http-interceptors/custom-json-interceptor.ts" path="http/src/app/http-interceptors/custom-json-interceptor.ts" region="custom-json-parser"></code-example>
|
||||
|
||||
You provide the `CustomParser` along with the `CustomJsonInterceptor`.
|
||||
Finally, provide the `CustomParser` along with the `CustomJsonInterceptor` in that same `httpInterceptorProviders` array.
|
||||
|
||||
<code-example header="app/http-interceptors/index.ts" path="http/src/app/http-interceptors/index.ts" region="custom-json-interceptor"></code-example>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,25 +7,19 @@ Apps can use the `HttpClient` to make [JSONP](https://en.wikipedia.org/wiki/JSON
|
|||
Angular JSONP requests return an `Observable`.
|
||||
Follow the pattern for subscribing to observables and use the RxJS `map` operator to transform the response before using the [async pipe](api/common/AsyncPipe) to manage the results.
|
||||
|
||||
In Angular, use JSONP by including `HttpClientJsonpModule` in the `NgModule` imports.
|
||||
In the following example, the `searchHeroes()` method uses a JSONP request to query for heroes whose names contain the search term.
|
||||
Enable JSONP by providing the `HttpClientJsonpModule` in the `bootstrapApplication` providers array in `main.ts` like this:
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
<code-example path="http/src/main.ts" region="jsonp"></code-example>
|
||||
|
||||
/* GET heroes whose name contains search term */
|
||||
searchHeroes(term: string): Observable {
|
||||
term = term.trim();
|
||||
|
||||
const heroesURL = `${this.heroesURL}?${term}`;
|
||||
return this.http.jsonp(heroesUrl, 'callback').pipe(
|
||||
catchError(this.handleError('searchHeroes', [])) // then handle the error
|
||||
);
|
||||
}
|
||||
In the following example, the `searchHeroesJsonp()` method uses a JSONP request to query for heroes whose names contain the search term acquired from the user.
|
||||
|
||||
<code-example path="http/src/app/heroes/heroes.service.ts" region="searchHeroesJsonp">
|
||||
</code-example>
|
||||
|
||||
This request passes the `heroesURL` as the first parameter and the callback function name as the second parameter.
|
||||
The response is wrapped in the callback function, which takes the observables returned by the JSONP method and pipes them through to the error handler.
|
||||
This request passes the `heroesUrl` with the search term as the first parameter and the standard callback function name, `callback`, as the second parameter.
|
||||
|
||||
You may have to `map` the Observable response from the `http.jsonp` method to the intended data type
|
||||
as this example does with `jsonpResultToHeroes`.
|
||||
|
||||
## Request non-JSON data
|
||||
|
||||
|
|
@ -44,4 +38,4 @@ A `download()` method in the `DownloaderComponent` initiates the request by subs
|
|||
|
||||
<a id="error-handling"></a>
|
||||
|
||||
@reviewed 2022-11-03
|
||||
@reviewed 2023-08-17
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# HTTP: Request data from a server
|
||||
|
||||
Use the [`HttpClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
|
||||
The asynchronous method sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
|
||||
The return type varies based on the `observe` and `responseType` values that you pass to the call.
|
||||
|
||||
The `get()` method takes two arguments; the endpoint URL from which to fetch, and an *options* object that is used to configure the request.
|
||||
This asynchronous method sends an HTTP request, and returns an [Observable](guide/observables-in-angular) that emits the requested data when the response is received.
|
||||
|
||||
The `get(url, options)` method takes two arguments; the string endpoint URL from which to fetch, and an *optional options* object to configure the request.
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
|
|
@ -22,7 +22,9 @@ options: {
|
|||
Important options include the *observe* and *responseType* properties.
|
||||
|
||||
* The *observe* option specifies how much of the response to return
|
||||
* The *responseType* option specifies the format in which to return data
|
||||
* The *responseType* option specifies the desired format of the returned data
|
||||
|
||||
To better understand the `observe` and `responseType` option types, [see below](#string-union-types).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
|
@ -39,26 +41,40 @@ In the `ConfigService` example, the app needs a configuration file on the server
|
|||
<code-example header="assets/config.json" path="http/src/assets/config.json"></code-example>
|
||||
|
||||
To fetch this kind of data, the `get()` call needs the following options: `{observe: 'body', responseType: 'json'}`.
|
||||
These are the default values for those options, so the following examples do not pass the options object.
|
||||
*These are the **default values** for those options*, so most `get()` calls - and most of the following examples - do not pass the options object.
|
||||
Later sections show some of the additional option possibilities.
|
||||
|
||||
<a id="config-service"></a>
|
||||
|
||||
The example conforms to the best practices for creating scalable solutions by defining a re-usable [injectable service](guide/glossary#service "service definition") to perform the data-handling functionality.
|
||||
In addition to fetching data, the service can post-process the data, add error handling, and add retry logic.
|
||||
### Handle data access in a service class
|
||||
|
||||
The `ConfigService` fetches this file using the `HttpClient.get()` method.
|
||||
The example conforms to the best practice for maintainable solutions by isolating the data-access functionality in a re-usable [injectable service](guide/glossary#service "service definition") separate from the component.
|
||||
|
||||
The `ConfigService` fetches the JSON file using the `HttpClient.get()` method.
|
||||
|
||||
<code-example header="app/config/config.service.ts (getConfig v.1)" path="http/src/app/config/config.service.ts" region="getConfig_1"></code-example>
|
||||
|
||||
The `ConfigComponent` injects the `ConfigService` and calls the `getConfig` service method.
|
||||
Notice that `get` was called
|
||||
* without an *options* value because the server endpoint returns JSON and JSON is the default data format.
|
||||
* with a generic, `Config`, that indicates the data return type; you'll [learn why shortly](#typed-response).
|
||||
|
||||
Because the service method returns an `Observable` of configuration data, the component *subscribes* to the method's return value.
|
||||
The subscription callback performs minimal post-processing.
|
||||
It copies the data fields into the component's `config` object, which is data-bound in the component template for display.
|
||||
In addition to fetching data, the service can post-process the data,
|
||||
[add error handling](guide/http-handle-request-errors),
|
||||
and add retry logic.
|
||||
|
||||
### Present the data in the component
|
||||
|
||||
The `ConfigComponent` injects the `ConfigService` in its constructor and offers a `showConfig` method, which calls the service's `getConfig` method.
|
||||
|
||||
<code-example header="app/config/config.component.ts (showConfig v.1)" path="http/src/app/config/config.component.ts" region="v1"></code-example>
|
||||
|
||||
Because the service's `getConfig` method returns an `Observable` of configuration data, the component *subscribes* to the method's return value.
|
||||
|
||||
If you didn't subscribe, the service would not have issued an HTTP request and there would be no config data to display. You will [understand why shortly](#always-subscribe).
|
||||
|
||||
This example subscription callback performs minimal post-processing.
|
||||
It copies the data fields into the component's `config` object, which is data-bound in the component template for display.
|
||||
|
||||
<a id="always-subscribe"></a>
|
||||
|
||||
## Starting the request
|
||||
|
|
@ -69,7 +85,9 @@ This is true for *all* `HttpClient` *methods*.
|
|||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You should always unsubscribe from an observable when a component is destroyed.
|
||||
In general, you should unsubscribe from an observable when a component is destroyed.
|
||||
|
||||
You don't have to unsubscribe from `HttpClient` observables because they unsubscribe automatically after the server request responds or times out. Most developers choose not to unsubscribe. None of this guide's examples unsubscribe.
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -113,45 +131,60 @@ It is up to the server to ensure that the type specified by the server API is re
|
|||
|
||||
</div>
|
||||
|
||||
To specify the response object type, first define an interface with the required properties.
|
||||
Suppose you made the `get` call without specifying the return type like this:
|
||||
<code-example header="Config Service - get without result type (not so good)" path="http/src/app/config/config.service.ts" region="untyped_response">
|
||||
</code-example>
|
||||
|
||||
The return type would be `Object`, To access its properties you would have to explicitly convert them with `as any` like this:
|
||||
|
||||
<code-example header="Config Component - without result type (not so good)" path="http/src/app/config/config.component.ts" region="untyped_response">
|
||||
</code-example>
|
||||
|
||||
It's safer and less clumsy if the returned object has the desired type.
|
||||
|
||||
Begin by defining an interface with the required properties.
|
||||
Use an interface rather than a class, because the response is a plain object that cannot be automatically converted to an instance of a class.
|
||||
|
||||
<code-example path="http/src/app/config/config.service.ts" region="config-interface"></code-example>
|
||||
|
||||
Next, specify that interface as the `HttpClient.get()` call's type parameter in the service.
|
||||
Now, specify that interface as the `HttpClient.get()` call's type parameter in the service.
|
||||
|
||||
<code-example header="app/config/config.service.ts (getConfig v.2)" path="http/src/app/config/config.service.ts" region="getConfig_2"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When you pass an interface as a type parameter to the `HttpClient.get()` method, use the [RxJS `map` operator](guide/rx-library#operators) to transform the response data as needed by the UI.
|
||||
You can then pass the transformed data to the [async pipe](api/common/AsyncPipe).
|
||||
|
||||
</div>
|
||||
<code-example header="Config Service - get with result type (better)" path="http/src/app/config/config.service.ts" region="getConfig_2"></code-example>
|
||||
|
||||
The callback in the updated component method receives a typed data object, which is easier and safer to consume:
|
||||
|
||||
<code-example header="app/config/config.component.ts (showConfig v.2)" path="http/src/app/config/config.component.ts" region="v2"></code-example>
|
||||
|
||||
To access properties that are defined in an interface, you must explicitly convert the plain object you get from the JSON to the required response type.
|
||||
For example, the following `subscribe` callback receives `data` as an Object, and then type-casts it in order to access the properties.
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
.subscribe(data => this.config = {
|
||||
heroesUrl: (data as any).heroesUrl,
|
||||
textfile: (data as any).textfile,
|
||||
});
|
||||
|
||||
<code-example header="Config Component - with typed response" path="http/src/app/config/config.component.ts" region="typed_response">
|
||||
</code-example>
|
||||
|
||||
You can go a step further and clone the result directly into the component's `config` property with [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#description).
|
||||
|
||||
<code-example header="Config Component - with destructured assignment" path="http/src/app/config/config.component.ts" region="v2"></code-example>
|
||||
|
||||
|
||||
## Reading the full response
|
||||
|
||||
In the previous example, the call to `HttpClient.get()` did not specify any options.
|
||||
By default, it returned the JSON data contained in the response body.
|
||||
|
||||
You might need more information about the transaction than is contained in the response body.
|
||||
Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
|
||||
|
||||
Tell `HttpClient` that you want the full response with the `observe` option of the `get()` method:
|
||||
|
||||
<code-example path="http/src/app/config/config.service.ts" region="getConfigResponse"></code-example>
|
||||
|
||||
Now `HttpClient.get()` returns an `Observable` of type `HttpResponse` rather than just the JSON data contained in the body.
|
||||
|
||||
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
|
||||
|
||||
<code-example header="app/config/config.component.ts (showConfigResponse)" path="http/src/app/config/config.component.ts" region="showConfigResponse"></code-example>
|
||||
|
||||
As you can see, the response object has a `body` property of the correct type.
|
||||
|
||||
<a id="string-union-types"></a>
|
||||
## The `observe` and `responseType` options
|
||||
|
||||
<div class="callout is-important">
|
||||
|
||||
<header><code>observe</code> and <code>response</code> types</header>
|
||||
|
||||
The types of the `observe` and `response` options are *string unions*, rather than plain strings.
|
||||
The types of the `observe` and `responseType` options are *string unions*, rather than plain strings.
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
|
|
@ -198,24 +231,4 @@ client.get('/foo', options);
|
|||
|
||||
</div>
|
||||
|
||||
## Reading the full response
|
||||
|
||||
In the previous example, the call to `HttpClient.get()` did not specify any options.
|
||||
By default, it returned the JSON data contained in the response body.
|
||||
|
||||
You might need more information about the transaction than is contained in the response body.
|
||||
Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
|
||||
|
||||
Tell `HttpClient` that you want the full response with the `observe` option of the `get()` method:
|
||||
|
||||
<code-example path="http/src/app/config/config.service.ts" region="getConfigResponse"></code-example>
|
||||
|
||||
Now `HttpClient.get()` returns an `Observable` of type `HttpResponse` rather than just the JSON data contained in the body.
|
||||
|
||||
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
|
||||
|
||||
<code-example header="app/config/config.component.ts (showConfigResponse)" path="http/src/app/config/config.component.ts" region="showConfigResponse"></code-example>
|
||||
|
||||
As you can see, the response object has a `body` property of the correct type.
|
||||
|
||||
@reviewed 2023-02-27
|
||||
@reviewed 2023-08-18
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ Failing to do so renders Angular's default protection ineffective.
|
|||
|
||||
If your backend service uses different names for the XSRF token cookie or header, use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
|
||||
<code-example path="http/src/app/app.module.ts" region="xsrf"></code-example>
|
||||
Add it to the `bootstrapApplication()` `providers` array in `main.ts` as follows:
|
||||
|
||||
<code-example path="http/src/main.ts" region="xsrf"></code-example>
|
||||
|
||||
<a id="testing-requests"></a>
|
||||
|
||||
@reviewed 2022-11-14
|
||||
@reviewed 2023-08-16
|
||||
|
|
|
|||
|
|
@ -47,9 +47,8 @@ Calling the `subscribe()` method *executes* the observable, which is what initia
|
|||
You must call `subscribe()` or nothing happens.
|
||||
Just calling `HeroesService.deleteHero()` does not initiate the DELETE request.
|
||||
|
||||
</div>
|
||||
|
||||
<code-example path="http/src/app/heroes/heroes.component.ts" region="delete-hero-no-subscribe"></code-example>
|
||||
</div>
|
||||
|
||||
|
||||
## Make a PUT request
|
||||
|
|
|
|||
|
|
@ -1,36 +1,43 @@
|
|||
# HTTP Server communication
|
||||
|
||||
<div class="callout is-critical">
|
||||
|
||||
<header>Marked for archiving</header>
|
||||
|
||||
To ensure that you have the best experience possible, this topic is marked for archiving until we determine that it clearly conveys the most accurate information possible.
|
||||
|
||||
In the meantime, this topic might be helpful: [Understanding HTTP](guide/understanding-communicating-with-http).
|
||||
|
||||
If you think this content should not be archived, please file a [GitHub issue](https://github.com/angular/angular/issues/new?template=3-docs-bug.md).
|
||||
|
||||
</div>
|
||||
|
||||
Most front-end applications need to communicate with a server over the HTTP protocol, to download or upload data and access other back-end services.
|
||||
|
||||
## Setup for server communication
|
||||
|
||||
Before you can use `HttpClient`, you need to import the Angular `HttpClientModule`.
|
||||
Most apps do so in the root `AppModule`.
|
||||
|
||||
<code-example header="app/app.module.ts (excerpt)" path="http/src/app/app.module.ts" region="sketch"></code-example>
|
||||
|
||||
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
|
||||
|
||||
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
|
||||
|
||||
The `HttpClient` service makes use of [observables](guide/glossary#observable "Observable definition") for all transactions.
|
||||
You must import the RxJS observable and operator symbols that appear in the example snippets.
|
||||
These `ConfigService` imports are typical.
|
||||
|
||||
<code-example header="app/config/config.service.ts (RxJS imports)" path="http/src/app/config/config.service.ts" region="rxjs-imports"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can run the <live-example></live-example> that accompanies this guide.
|
||||
You can run the <live-example name="http"></live-example> that accompanies this guide.
|
||||
|
||||
The sample app does not require a data server.
|
||||
It relies on the [Angular *in-memory-web-api*](https://github.com/angular/angular/tree/main/packages/misc/angular-in-memory-web-api), which replaces the *HttpClient* module's `HttpBackend`.
|
||||
The replacement service simulates the behavior of a REST-like backend.
|
||||
|
||||
Look at the `AppModule` *imports* to see how it is configured.
|
||||
Look at the `bootstrapApplication()` method in `main.ts` to see how it is configured.
|
||||
|
||||
</div>
|
||||
|
||||
## Setup for server communication
|
||||
|
||||
Before you can use `HttpClient`, you need to provide the Angular `HttpClientModule` so that it is available for [dependency injection](guide/dependency-injection) into the classes that need it.
|
||||
|
||||
Most developers provide the `HttpClientModule` when initializing the app with `bootstrapApplication` in `main.ts` as shown in this example:
|
||||
|
||||
<code-example header="main.ts (excerpt)" path="http/src/main.ts" region="sketch"></code-example>
|
||||
|
||||
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
|
||||
|
||||
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
|
||||
|
||||
## Requesting data from a server
|
||||
|
||||
Use the [`HttpClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
|
||||
|
|
@ -251,4 +258,4 @@ The component's `showConfigResponse()` method displays the response headers as w
|
|||
|
||||
As you can see, the response object has a `body` property of the correct type.
|
||||
|
||||
@reviewed 2023-02-27
|
||||
@reviewed 2023-08-16
|
||||
|
|
|
|||
|
|
@ -1,30 +1,25 @@
|
|||
# HTTP: Setup for server communication
|
||||
|
||||
Before you can use `HttpClient`, you need to import the Angular `HttpClientModule`.
|
||||
Most apps do so in the root `AppModule`.
|
||||
Before you can use `HttpClient`, you must add it to the application's [root dependency injector](guide/dependency-injection).
|
||||
|
||||
<code-example header="app/app.module.ts (excerpt)" path="http/src/app/app.module.ts" region="sketch"></code-example>
|
||||
Most apps do so in the `providers` array of `bootstrapApplication()` in `main.ts`.
|
||||
|
||||
<code-example header="main.ts (excerpt)" path="http/src/main.ts" region="sketch"></code-example>
|
||||
|
||||
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
|
||||
|
||||
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
|
||||
|
||||
The `HttpClient` service makes use of [observables](guide/glossary#observable "Observable definition") for all transactions.
|
||||
You must import the RxJS observable and operator symbols that appear in the example snippets.
|
||||
These `ConfigService` imports are typical.
|
||||
|
||||
<code-example header="app/config/config.service.ts (RxJS imports)" path="http/src/app/config/config.service.ts" region="rxjs-imports"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can run the <live-example></live-example> that accompanies this guide.
|
||||
You can run the <live-example name="http"></live-example> that accompanies this guide.
|
||||
|
||||
The sample app does not require a data server.
|
||||
It relies on the [Angular *in-memory-web-api*](https://github.com/angular/angular/tree/main/packages/misc/angular-in-memory-web-api), which replaces the *HttpClient* module's `HttpBackend`.
|
||||
The replacement service simulates the behavior of a REST-like backend.
|
||||
|
||||
Look at the `AppModule` *imports* to see how it is configured.
|
||||
Look at the `bootstrapApplication()` method in `main.ts` to see how it is configured.
|
||||
|
||||
</div>
|
||||
|
||||
@reviewed 2022-11-03
|
||||
@reviewed 2023-08-16
|
||||
|
|
|
|||
|
|
@ -23,6 +23,6 @@ The HTTP client service offers the following major features.
|
|||
|
||||
## What's next
|
||||
|
||||
* [Setup for server communication](guide/http-server-communication)
|
||||
* [Setup for server communication](guide/http-setup-server-communication)
|
||||
|
||||
@reviewed 2023-03-14
|
||||
@reviewed 2023-08-16
|
||||
|
|
|
|||
|
|
@ -449,11 +449,6 @@
|
|||
"title": "Setup for server communication",
|
||||
"tooltip": "Setup a server communication for HTTP"
|
||||
},
|
||||
{
|
||||
"url": "guide/http-server-communication",
|
||||
"title": "Communicating with backend services",
|
||||
"tooltip": "Understanding communicating with backend services using HTTP"
|
||||
},
|
||||
{
|
||||
"url": "guide/http-request-data-from-server",
|
||||
"title": "Request data from a server",
|
||||
|
|
|
|||
Loading…
Reference in a new issue