Skip to content

Commit 036d9a5

Browse files
author
Jason Watmore
committed
Updated to new HttpClient
1 parent 099db3d commit 036d9a5

File tree

9 files changed

+335
-237
lines changed

9 files changed

+335
-237
lines changed

app/_helpers/fake-backend.ts

Lines changed: 61 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,87 @@
1-
import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod, XHRBackend, RequestOptions } from '@angular/http';
2-
import { MockBackend, MockConnection } from '@angular/http/testing';
1+
import { Injectable } from '@angular/core';
2+
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
3+
import { Observable } from 'rxjs/Observable';
4+
import 'rxjs/add/observable/of';
5+
import 'rxjs/add/observable/throw';
6+
import 'rxjs/add/operator/delay';
7+
import 'rxjs/add/operator/mergeMap';
8+
import 'rxjs/add/operator/materialize';
9+
import 'rxjs/add/operator/dematerialize';
310

4-
export function fakeBackendFactory(backend: MockBackend, options: BaseRequestOptions, realBackend: XHRBackend) {
5-
// array in local storage for registered users
6-
let users: any[] = JSON.parse(localStorage.getItem('users')) || [];
11+
@Injectable()
12+
export class FakeBackendInterceptor implements HttpInterceptor {
713

8-
// configure fake backend
9-
backend.connections.subscribe((connection: MockConnection) => {
10-
// wrap in timeout to simulate server api call
11-
setTimeout(() => {
14+
constructor() { }
1215

13-
// authenticate
14-
if (connection.request.url.endsWith('/api/authenticate') && connection.request.method === RequestMethod.Post) {
15-
// get parameters from post request
16-
let params = JSON.parse(connection.request.getBody());
16+
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
17+
// array in local storage for registered users
18+
let users: any[] = JSON.parse(localStorage.getItem('users')) || [];
19+
20+
// wrap in delayed observable to simulate server api call
21+
return Observable.of(null).mergeMap(() => {
1722

23+
// authenticate
24+
if (request.url.endsWith('/api/authenticate') && request.method === 'POST') {
1825
// find if any user matches login credentials
1926
let filteredUsers = users.filter(user => {
20-
return user.username === params.username && user.password === params.password;
27+
return user.username === request.body.username && user.password === request.body.password;
2128
});
2229

2330
if (filteredUsers.length) {
2431
// if login details are valid return 200 OK with user details and fake jwt token
2532
let user = filteredUsers[0];
26-
connection.mockRespond(new Response(new ResponseOptions({
27-
status: 200,
28-
body: {
29-
id: user.id,
30-
username: user.username,
31-
firstName: user.firstName,
32-
lastName: user.lastName,
33-
token: 'fake-jwt-token'
34-
}
35-
})));
33+
let body = {
34+
id: user.id,
35+
username: user.username,
36+
firstName: user.firstName,
37+
lastName: user.lastName,
38+
token: 'fake-jwt-token'
39+
};
40+
41+
return Observable.of(new HttpResponse({ status: 200, body: body }));
3642
} else {
3743
// else return 400 bad request
38-
connection.mockError(new Error('Username or password is incorrect'));
44+
return Observable.throw('Username or password is incorrect');
3945
}
40-
41-
return;
4246
}
4347

4448
// get users
45-
if (connection.request.url.endsWith('/api/users') && connection.request.method === RequestMethod.Get) {
49+
if (request.url.endsWith('/api/users') && request.method === 'GET') {
4650
// check for fake auth token in header and return users if valid, this security is implemented server side in a real application
47-
if (connection.request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
48-
connection.mockRespond(new Response(new ResponseOptions({ status: 200, body: users })));
51+
if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
52+
return Observable.of(new HttpResponse({ status: 200, body: users }));
4953
} else {
5054
// return 401 not authorised if token is null or invalid
51-
connection.mockRespond(new Response(new ResponseOptions({ status: 401 })));
55+
return Observable.throw('Unauthorised');
5256
}
53-
54-
return;
5557
}
5658

5759
// get user by id
58-
if (connection.request.url.match(/\/api\/users\/\d+$/) && connection.request.method === RequestMethod.Get) {
60+
if (request.url.match(/\/api\/users\/\d+$/) && request.method === 'GET') {
5961
// check for fake auth token in header and return user if valid, this security is implemented server side in a real application
60-
if (connection.request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
62+
if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
6163
// find user by id in users array
62-
let urlParts = connection.request.url.split('/');
64+
let urlParts = request.url.split('/');
6365
let id = parseInt(urlParts[urlParts.length - 1]);
6466
let matchedUsers = users.filter(user => { return user.id === id; });
6567
let user = matchedUsers.length ? matchedUsers[0] : null;
6668

67-
// respond 200 OK with user
68-
connection.mockRespond(new Response(new ResponseOptions({ status: 200, body: user })));
69+
return Observable.of(new HttpResponse({ status: 200, body: user }));
6970
} else {
7071
// return 401 not authorised if token is null or invalid
71-
connection.mockRespond(new Response(new ResponseOptions({ status: 401 })));
72+
return Observable.throw('Unauthorised');
7273
}
73-
74-
return;
7574
}
7675

7776
// create user
78-
if (connection.request.url.endsWith('/api/users') && connection.request.method === RequestMethod.Post) {
77+
if (request.url.endsWith('/api/users') && request.method === 'POST') {
7978
// get new user object from post body
80-
let newUser = JSON.parse(connection.request.getBody());
79+
let newUser = request.body;
8180

8281
// validation
8382
let duplicateUser = users.filter(user => { return user.username === newUser.username; }).length;
8483
if (duplicateUser) {
85-
return connection.mockError(new Error('Username "' + newUser.username + '" is already taken'));
84+
return Observable.throw('Username "' + newUser.username + '" is already taken');
8685
}
8786

8887
// save new user
@@ -91,17 +90,15 @@ export function fakeBackendFactory(backend: MockBackend, options: BaseRequestOpt
9190
localStorage.setItem('users', JSON.stringify(users));
9291

9392
// respond 200 OK
94-
connection.mockRespond(new Response(new ResponseOptions({ status: 200 })));
95-
96-
return;
93+
return Observable.of(new HttpResponse({ status: 200 }));
9794
}
9895

9996
// delete user
100-
if (connection.request.url.match(/\/api\/users\/\d+$/) && connection.request.method === RequestMethod.Delete) {
97+
if (request.url.match(/\/api\/users\/\d+$/) && request.method === 'DELETE') {
10198
// check for fake auth token in header and return user if valid, this security is implemented server side in a real application
102-
if (connection.request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
99+
if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
103100
// find user by id in users array
104-
let urlParts = connection.request.url.split('/');
101+
let urlParts = request.url.split('/');
105102
let id = parseInt(urlParts[urlParts.length - 1]);
106103
for (let i = 0; i < users.length; i++) {
107104
let user = users[i];
@@ -114,43 +111,28 @@ export function fakeBackendFactory(backend: MockBackend, options: BaseRequestOpt
114111
}
115112

116113
// respond 200 OK
117-
connection.mockRespond(new Response(new ResponseOptions({ status: 200 })));
114+
return Observable.of(new HttpResponse({ status: 200 }));
118115
} else {
119116
// return 401 not authorised if token is null or invalid
120-
connection.mockRespond(new Response(new ResponseOptions({ status: 401 })));
117+
return Observable.throw('Unauthorised');
121118
}
122-
123-
return;
124119
}
125120

126121
// pass through any requests not handled above
127-
let realHttp = new Http(realBackend, options);
128-
let requestOptions = new RequestOptions({
129-
method: connection.request.method,
130-
headers: connection.request.headers,
131-
body: connection.request.getBody(),
132-
url: connection.request.url,
133-
withCredentials: connection.request.withCredentials,
134-
responseType: connection.request.responseType
135-
});
136-
realHttp.request(connection.request.url, requestOptions)
137-
.subscribe((response: Response) => {
138-
connection.mockRespond(response);
139-
},
140-
(error: any) => {
141-
connection.mockError(error);
142-
});
143-
144-
}, 500);
145-
146-
});
122+
return next.handle(request);
123+
124+
})
147125

148-
return new Http(backend, options);
149-
};
126+
// call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
127+
.materialize()
128+
.delay(500)
129+
.dematerialize();
130+
}
131+
}
150132

151133
export let fakeBackendProvider = {
152134
// use fake backend in place of Http service for backend-less development
153-
provide: Http,
154-
useFactory: fakeBackendFactory,
155-
deps: [MockBackend, BaseRequestOptions, XHRBackend]
135+
provide: HTTP_INTERCEPTORS,
136+
useClass: FakeBackendInterceptor,
137+
multi: true
156138
};

app/_helpers/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './fake-backend';
1+
export * from './jwt.interceptor';
2+
export * from './fake-backend';

app/_helpers/jwt.interceptor.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
3+
import { Observable } from 'rxjs/Observable';
4+
5+
@Injectable()
6+
export class JwtInterceptor implements HttpInterceptor {
7+
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
8+
// add authorization header with jwt token if available
9+
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
10+
if (currentUser && currentUser.token) {
11+
request = request.clone({
12+
setHeaders: {
13+
Authorization: `Bearer ${currentUser.token}`
14+
}
15+
});
16+
}
17+
18+
return next.handle(request);
19+
}
20+
}

app/_services/authentication.service.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { Injectable } from '@angular/core';
2-
import { Http, Headers, Response } from '@angular/http';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
33
import { Observable } from 'rxjs/Observable';
44
import 'rxjs/add/operator/map'
55

66
@Injectable()
77
export class AuthenticationService {
8-
constructor(private http: Http) { }
8+
constructor(private http: HttpClient) { }
99

1010
login(username: string, password: string) {
11-
return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password }))
12-
.map((response: Response) => {
11+
return this.http.post<any>('/api/authenticate', { username: username, password: password })
12+
.map(user => {
1313
// login successful if there's a jwt token in the response
14-
let user = response.json();
1514
if (user && user.token) {
1615
// store user details and jwt token in local storage to keep user logged in between page refreshes
1716
localStorage.setItem('currentUser', JSON.stringify(user));

app/_services/user.service.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,29 @@
11
import { Injectable } from '@angular/core';
2-
import { Http, Headers, RequestOptions, Response } from '@angular/http';
2+
import { HttpClient } from '@angular/common/http';
33

44
import { User } from '../_models/index';
55

66
@Injectable()
77
export class UserService {
8-
constructor(private http: Http) { }
8+
constructor(private http: HttpClient) { }
99

1010
getAll() {
11-
return this.http.get('/api/users', this.jwt()).map((response: Response) => response.json());
11+
return this.http.get<User[]>('/api/users');
1212
}
1313

1414
getById(id: number) {
15-
return this.http.get('/api/users/' + id, this.jwt()).map((response: Response) => response.json());
15+
return this.http.get('/api/users/' + id);
1616
}
1717

1818
create(user: User) {
19-
return this.http.post('/api/users', user, this.jwt()).map((response: Response) => response.json());
19+
return this.http.post('/api/users', user);
2020
}
2121

2222
update(user: User) {
23-
return this.http.put('/api/users/' + user.id, user, this.jwt()).map((response: Response) => response.json());
23+
return this.http.put('/api/users/' + user.id, user);
2424
}
2525

2626
delete(id: number) {
27-
return this.http.delete('/api/users/' + id, this.jwt()).map((response: Response) => response.json());
28-
}
29-
30-
// private helper methods
31-
32-
private jwt() {
33-
// create authorization header with jwt token
34-
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
35-
if (currentUser && currentUser.token) {
36-
let headers = new Headers({ 'Authorization': 'Bearer ' + currentUser.token });
37-
return new RequestOptions({ headers: headers });
38-
}
27+
return this.http.delete('/api/users/' + id);
3928
}
4029
}

app/app.module.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import { NgModule } from '@angular/core';
22
import { BrowserModule } from '@angular/platform-browser';
33
import { FormsModule } from '@angular/forms';
4-
import { HttpModule } from '@angular/http';
4+
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
55

66
// used to create fake backend
77
import { fakeBackendProvider } from './_helpers/index';
8-
import { MockBackend, MockConnection } from '@angular/http/testing';
9-
import { BaseRequestOptions } from '@angular/http';
108

119
import { AppComponent } from './app.component';
1210
import { routing } from './app.routing';
1311

1412
import { AlertComponent } from './_directives/index';
1513
import { AuthGuard } from './_guards/index';
14+
import { JwtInterceptor } from './_helpers/index';
1615
import { AlertService, AuthenticationService, UserService } from './_services/index';
1716
import { HomeComponent } from './home/index';
1817
import { LoginComponent } from './login/index';
@@ -22,7 +21,7 @@ import { RegisterComponent } from './register/index';
2221
imports: [
2322
BrowserModule,
2423
FormsModule,
25-
HttpModule,
24+
HttpClientModule,
2625
routing
2726
],
2827
declarations: [
@@ -37,11 +36,14 @@ import { RegisterComponent } from './register/index';
3736
AlertService,
3837
AuthenticationService,
3938
UserService,
39+
{
40+
provide: HTTP_INTERCEPTORS,
41+
useClass: JwtInterceptor,
42+
multi: true
43+
},
4044

41-
// providers used to create fake backend
42-
fakeBackendProvider,
43-
MockBackend,
44-
BaseRequestOptions
45+
// provider used to create fake backend
46+
fakeBackendProvider
4547
],
4648
bootstrap: [AppComponent]
4749
})

0 commit comments

Comments
 (0)