Skip to content

Commit f071ba4

Browse files
committed
Updated to new HttpClient
1 parent 71d8b15 commit f071ba4

12 files changed

+1022
-1955
lines changed

app/_guards/auth.guard.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import { Injectable } from '@angular/core';
2-
import { Router, CanActivate } from '@angular/router';
2+
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
33

44
@Injectable()
55
export class AuthGuard implements CanActivate {
66

77
constructor(private router: Router) { }
88

9-
canActivate() {
9+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
1010
if (localStorage.getItem('currentUser')) {
1111
// logged in so return true
1212
return true;
1313
}
1414

1515
// not logged in so redirect to login page
16-
this.router.navigate(['/login']);
16+
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
1717
return false;
1818
}
1919
}

app/_helpers/fake-backend.ts

+46-42
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,61 @@
1-
import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod } from '@angular/http';
2-
import { MockBackend, MockConnection } from '@angular/http/testing';
3-
4-
export function fakeBackendFactory(backend: MockBackend, options: BaseRequestOptions) {
5-
// configure fake backend
6-
backend.connections.subscribe((connection: MockConnection) => {
7-
let testUser = { username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };
8-
9-
// wrap in timeout to simulate server api call
10-
setTimeout(() => {
11-
12-
// fake authenticate api end point
13-
if (connection.request.url.endsWith('/api/authenticate') && connection.request.method === RequestMethod.Post) {
14-
// get parameters from post request
15-
let params = JSON.parse(connection.request.getBody());
16-
17-
// check user credentials and return fake jwt token if valid
18-
if (params.username === testUser.username && params.password === testUser.password) {
19-
connection.mockRespond(new Response(
20-
new ResponseOptions({ status: 200, body: { token: 'fake-jwt-token' } })
21-
));
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';
10+
11+
@Injectable()
12+
export class FakeBackendInterceptor implements HttpInterceptor {
13+
14+
constructor() { }
15+
16+
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
17+
let testUser = { id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };
18+
19+
// wrap in delayed observable to simulate server api call
20+
return Observable.of(null).mergeMap(() => {
21+
22+
// authenticate
23+
if (request.url.endsWith('/api/authenticate') && request.method === 'POST') {
24+
if (request.body.username === testUser.username && request.body.password === testUser.password) {
25+
// if login details are valid return 200 OK with a fake jwt token
26+
return Observable.of(new HttpResponse({ status: 200, body: { token: 'fake-jwt-token' } }));
2227
} else {
23-
connection.mockRespond(new Response(
24-
new ResponseOptions({ status: 200 })
25-
));
28+
// else return 400 bad request
29+
return Observable.throw('Username or password is incorrect');
2630
}
2731
}
2832

29-
// fake users api end point
30-
if (connection.request.url.endsWith('/api/users') && connection.request.method === RequestMethod.Get) {
31-
// check for fake auth token in header and return test users if valid, this security is implemented server side
32-
// in a real application
33-
if (connection.request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
34-
connection.mockRespond(new Response(
35-
new ResponseOptions({ status: 200, body: [testUser] })
36-
));
33+
// get users
34+
if (request.url.endsWith('/api/users') && request.method === 'GET') {
35+
// check for fake auth token in header and return users if valid, this security is implemented server side in a real application
36+
if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
37+
return Observable.of(new HttpResponse({ status: 200, body: [testUser] }));
3738
} else {
3839
// return 401 not authorised if token is null or invalid
39-
connection.mockRespond(new Response(
40-
new ResponseOptions({ status: 401 })
41-
));
40+
return Observable.throw('Unauthorised');
4241
}
4342
}
4443

45-
}, 500);
44+
// pass through any requests not handled above
45+
return next.handle(request);
46+
47+
})
4648

47-
});
48-
49-
return new Http(backend, options);
49+
// call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
50+
.materialize()
51+
.delay(500)
52+
.dematerialize();
53+
}
5054
}
5155

5256
export let fakeBackendProvider = {
5357
// use fake backend in place of Http service for backend-less development
54-
provide: Http,
55-
useFactory: fakeBackendFactory,
56-
deps: [MockBackend, BaseRequestOptions]
58+
provide: HTTP_INTERCEPTORS,
59+
useClass: FakeBackendInterceptor,
60+
multi: true
5761
};

app/_helpers/index.ts

+2-1
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

+20
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+
}
+13-28
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,27 @@
11
import { Injectable } from '@angular/core';
2-
import { Http, Headers, Response } from '@angular/http';
3-
import { Observable } from 'rxjs';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
import { Observable } from 'rxjs/Observable';
44
import 'rxjs/add/operator/map'
55

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

10-
constructor(private http: Http) {
11-
// set token if saved in local storage
12-
var currentUser = JSON.parse(localStorage.getItem('currentUser'));
13-
this.token = currentUser && currentUser.token;
14-
}
15-
16-
login(username: string, password: string): Observable<boolean> {
17-
return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password }))
18-
.map((response: Response) => {
10+
login(username: string, password: string) {
11+
return this.http.post<any>('/api/authenticate', { username: username, password: password })
12+
.map(user => {
1913
// login successful if there's a jwt token in the response
20-
let token = response.json() && response.json().token;
21-
if (token) {
22-
// set token property
23-
this.token = token;
24-
25-
// store username and jwt token in local storage to keep user logged in between page refreshes
26-
localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));
27-
28-
// return true to indicate successful login
29-
return true;
30-
} else {
31-
// return false to indicate failed login
32-
return false;
14+
if (user && user.token) {
15+
// store user details and jwt token in local storage to keep user logged in between page refreshes
16+
localStorage.setItem('currentUser', JSON.stringify(user));
3317
}
18+
19+
return user;
3420
});
3521
}
3622

37-
logout(): void {
38-
// clear token remove user from local storage to log user out
39-
this.token = null;
23+
logout() {
24+
// remove user from local storage to log user out
4025
localStorage.removeItem('currentUser');
4126
}
4227
}

app/_services/user.service.ts

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
11
import { Injectable } from '@angular/core';
2-
import { Http, Headers, RequestOptions, Response } from '@angular/http';
3-
import { Observable } from 'rxjs';
4-
import 'rxjs/add/operator/map'
2+
import { HttpClient } from '@angular/common/http';
53

6-
import { AuthenticationService } from './index';
74
import { User } from '../_models/index';
85

96
@Injectable()
107
export class UserService {
11-
constructor(
12-
private http: Http,
13-
private authenticationService: AuthenticationService) {
14-
}
15-
16-
getUsers(): Observable<User[]> {
17-
// add authorization header with jwt token
18-
let headers = new Headers({ 'Authorization': 'Bearer ' + this.authenticationService.token });
19-
let options = new RequestOptions({ headers: headers });
8+
constructor(private http: HttpClient) { }
209

21-
// get users from api
22-
return this.http.get('/api/users', options)
23-
.map((response: Response) => response.json());
10+
getAll() {
11+
return this.http.get<User[]>('/api/users');
2412
}
2513
}

app/app.module.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
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 { AuthGuard } from './_guards/index';
13+
import { JwtInterceptor } from './_helpers/index';
1514
import { AuthenticationService, UserService } from './_services/index';
1615
import { LoginComponent } from './login/index';
1716
import { HomeComponent } from './home/index';
@@ -20,7 +19,7 @@ import { HomeComponent } from './home/index';
2019
imports: [
2120
BrowserModule,
2221
FormsModule,
23-
HttpModule,
22+
HttpClientModule,
2423
routing
2524
],
2625
declarations: [
@@ -32,11 +31,14 @@ import { HomeComponent } from './home/index';
3231
AuthGuard,
3332
AuthenticationService,
3433
UserService,
35-
34+
{
35+
provide: HTTP_INTERCEPTORS,
36+
useClass: JwtInterceptor,
37+
multi: true
38+
},
39+
3640
// providers used to create fake backend
37-
fakeBackendProvider,
38-
MockBackend,
39-
BaseRequestOptions
41+
fakeBackendProvider
4042
],
4143
bootstrap: [AppComponent]
4244
})

app/home/home.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class HomeComponent implements OnInit {
1515

1616
ngOnInit() {
1717
// get users from secure api end point
18-
this.userService.getUsers()
18+
this.userService.getAll()
1919
.subscribe(users => {
2020
this.users = users;
2121
});

app/login/login.component.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, OnInit } from '@angular/core';
2-
import { Router } from '@angular/router';
2+
import { Router, ActivatedRoute } from '@angular/router';
3+
import 'rxjs/add/operator/first';
34

45
import { AuthenticationService } from '../_services/index';
56

@@ -11,27 +12,33 @@ import { AuthenticationService } from '../_services/index';
1112
export class LoginComponent implements OnInit {
1213
model: any = {};
1314
loading = false;
15+
returnUrl: string;
1416
error = '';
1517

1618
constructor(
19+
private route: ActivatedRoute,
1720
private router: Router,
1821
private authenticationService: AuthenticationService) { }
1922

2023
ngOnInit() {
2124
// reset login status
2225
this.authenticationService.logout();
26+
27+
// get return url from route parameters or default to '/'
28+
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
2329
}
2430

2531
login() {
2632
this.loading = true;
2733
this.authenticationService.login(this.model.username, this.model.password)
28-
.subscribe(result => {
29-
if (result === true) {
30-
this.router.navigate(['/']);
31-
} else {
32-
this.error = 'Username or password is incorrect';
34+
.first()
35+
.subscribe(
36+
data => {
37+
this.router.navigate([this.returnUrl]);
38+
},
39+
error => {
40+
this.error = error;
3341
this.loading = false;
34-
}
35-
});
42+
});
3643
}
3744
}

0 commit comments

Comments
 (0)