Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I need an advice how to make my code better.
I have a simple class that gets data from backend that is using jwt token auth.
export class RepositoryService {
constructor(private http: HttpClient, private envUrl: EnvironmentUrlService) { }
public getData = (route: string) => {
return this.http.get(this.createCompleteRoute(route, this.envUrl.urlAddress), this.generateHeaders());
}
private createCompleteRoute = (route: string, envAddress: string) => {
return `${envAddress}/${route}`;
}
private generateHeaders = () => {
return {
headers: new HttpHeaders({
"Content-Type": "application/json",
"Authorization": `Bearer ${localStorage.getItem("token")}`
}),
};
};
It works fine but the problem starts when I get a lot more of http methods. How can I change createCompleteRoute so I won't have to use generateHeaders() in every http method?
I though about doing something like:
private createCompleteRoute = (route: string, envAddress: string) => {
return `${envAddress}/${route}`, this.generateHeaders();
}
so http methods could look like this:
public getData = (route: string) => {
return this.http.get(this.createCompleteRoute(route, this.envUrl.urlAddress));
}
But have no idea how to write a valid function.
The best way to do what you ask for, could be to bring your logic for creating headers to an interceptor, which is going to automatically add the header parameters to every http call.
It could be something like this:
Your interceptor file (is kinda service, but have to implement HttpInterceptor:
import { Injectable } from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
} from '#angular/common/http';
// The service/way you use to get your token
import { AuthService } from '../services/auth.service';
#Injectable()
export class MyInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const url="\yourAPI\endpoint";
// Get your token
cont myToken = this.authService.getToken(); // or localStorage.getItem("token") or whatever your way to get your token
// Add authorization header with token if available
if (myToken) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.user.api_token}`,
'Content-Type': 'application/json',
},
url,
});
}
...
}
EXTRA: More info about how to adding and updating headers and how to Use the interceptor for Intercepting requests and responses:
Adding & Updating Headers
Intercepting request & responses
Related
I have a little issue from an Angular app to get a code from my own server.
I´ve built up a little Spotify App for learning more about Angular 10 and have a little backend that I only use for get the Bearer code to call the Spotify API, but the fact is that in my Angular front I can´t save the code.
Tis is my service call code to the back:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class SpotifyService {
token: any;
constructor(private http: HttpClient) {
console.log('Spotify service ready');
this.getAccesToken().subscribe(data => this.token = data['access_token']);
}
getAccesToken(){
return this.http.get(environment.server + `${environment.client_id}/${environment.client_secret}`)
.pipe(
map(res => res)
);
}
getQuery(query: any){
const headers = new HttpHeaders({
'Authorization': `Bearer ${this.token}`
});
const url = `https://api.spotify.com/v1/${query}`;
return this.http.get(url, {headers});
}
I´ve checked the recibed data and I get the request perfectly, but can´t save the data of the suscriber into a variable.
Thanks in advance!
Assuming your component code looks like below, you can make some adjustments and try it.
export class SomeComponent implements OnInit {
spotifyData;
user;
token;
constructor(private spotifyService: SpotifyService, private http: HttpClient) { }
ngOnInit() {
this.user = JSON.parse(localStorage.getItem('user'));
console.log(this.user);
this.token = this.user.token;
this.getSpotifyData();
}
getSpotifyData() {
this.spotifyService.getQuery(this.user, { headers: new HttpHeaders().set('Authorization', 'Bearer ' + this.token) }).subscribe(res => {
console.log(res);
if (res) {
spotifyData = res;
} else {
spotifyData = []
}
});
}
I'm building the front-end of an application in Angular 8. This application uses an OAuth 2 implementation to manage authentication (password grant) so any HTTP request (with the exception of ones to the token endpoint) needs to have on its header a valid access_token.
To provide said token I've made an Angular interceptor that retrieve the token from another service and then attach it to the intercepted HTTP request. The token retrieval method doesn't give directly the token but an observable which eventually resolves to a valid token, I made this choice because the access token may not be instantly available, if the token is expired the application needs to refresh it with an HTTP call and then the refreshed token can be passed to the HTTP interceptor.
The problem which I encounter is that despite my many attempts the interceptor doesn't wait for the token to be retrieved so at the end the interceptor is skipped and the HTTP request is made without any token attached.
This is the code of my interceptor, retrieveValidToken is the Observable which returns the token.
import { Injectable } from '#angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '#angular/common/http';
import { FacadeService } from './facade.service';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor {
constructor(private facadeService: FacadeService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
this.facadeService.retrieveValidToken()
.subscribe(
(res) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
},
(err) => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
}
Observables are asynchronous. The code outside the subscribe method will not wait for the code inside.
You should return observable by itself, not only result inside its subscription:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.includes('localhost:3000') && !req.url.endsWith('token')) {
return this.facadeService.retrieveValidToken()
.subscribe(
res => {
const clone = req.clone({ setHeaders: { Authorization: `Bearer ${res}` } });
return next.handle(clone);
}
);
} else {
return next.handle(req);
}
}
Something similar:
How use async service into angular httpClient interceptor
The problem is that 'intercept' method should return observable immediately, so instead of subscribing to 'this.facadeService.retrieveValidToken()' use the following code:
return this.facadeService.retrieveValidToken().pipe(
mergeMap(token =>
next.handle(req.clone({ setHeaders: { Authorization: 'Bearer ${token}' }))
)
)
I am using a restapi and it requires that I add a token to the header before I can create a new record.
Right now I have a service to create a new record which looks like this:
service.ts
create(title, text) {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
headers = headers.append('Authorization', token); // Not added yet as this is the reason for the question
return this.http.post('http://myapi/api.php/posts', {
title: 'added title',
text: 'added text'
}, { headers });
}
app.component.ts
add() {
this.service.create('my title', 'body text').subscribe(result => {
console.log(result);
});
}
The problem with this is that it won't let me add the new record because it requires a token and in order to get a token I need to run this:
getToken() {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
return this.http.post('http://myapi/api.php/user', {
username: 'admin',
password: 'password'
}, { headers });
}
My question is...How do I get this two together into one call instead of two...or when is the best way to do this?
Apart from what #Pardeep Jain already mentioned, you can add an interceptor (> Angular version 4, you mentioned you're using 5) for your HttpClient that will automatically add Authorization headers for all requests.
If you need top be authenticated for only one request, it's better to keep things simple and use Pardeep's solution.
If you want to be authenticated for most of your requests, then add an interceptor.
module, let's say app.module.ts
#NgModule({
//...
providers: [
//...
{
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
},
//...
]
//...
})
and your jwt interceptor, let's say jwt.interceptor.ts
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private injector: Injector, private router: Router) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({
headers: req.headers.set('Authorization', /* here you fetch your jwt */this.getToken())
.append('Access-Control-Allow-Origin', '*')
});
return next.handle(authReq).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (response: HttpErrorResponse) => { });
}
getToken() {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
return this.http.post('http://myapi/api.php/user', {
username: 'admin',
password: 'password'
}, { headers });
}
}
If you want to read something, more here: https://medium.com/#ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8
My question is...How do I get this two together into one call instead
of two...or when is the best way to do this?
You should not.
Authentication is one thing that should be performed a single time for the client or as the authentication ticket has expired.
Posting some content is another thing that you should not mix with authentication.
So authenticate the client once and store the ticket.
Then pass the ticket in the header for any request to a secured endpoints/methods. Or use a transverse way as an interceptor to set it in the send requests if you don't want to repeat the code.
The code should be like this -
create(title, text) {
let headers: HttpHeaders = new HttpHeaders();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
headers.append('Authorization', token);
return this.http.post('http://myapi/api.php/posts', {
title: 'added title',
text: 'added text'
}, { headers });
}
In my Angular 2 application, I'm trying to use Http (#angular/http) to make requests to my API. For these requests to work, I need certain headers to be added to every request I make to the API (including a JWT header).
What I'd like to do is have an API class that takes care of creating the Http requests and some error handling and validation etc.
As it turns out, however, I cannot use the Http class from my API class, as it will come up with the following error;
user.service.ts
import { Injectable } from '#angular/core';
import {User} from "../models/User";
import {API} from "../API";
import {Http} from "#angular/http";
#Injectable()
export class UserService
{
constructor (private http : Http) {}
getProfile (user : User)
{
let api = new API (this.http);
return api.doRequest ('/user/' + user.id + '/profile');
}
}
API.ts
import {Http, Headers, RequestOptions} from '#angular/http';
export class API
{
...
constructor (private http : Http) {}
doRequest (url : string, method : string, data?)
{
let headers = {...};
let options = new RequestOptions ({ headers: new Headers (headers), ... } );
return this.http.get (url, data, options)
.catch ((error) => { ... } );
}
}
Things work better when using Http straight from the UserService, however.
Is there a way to fix this, or perhaps a better way to achieve the desired result? Should I just extend Http?
You should be using append() method to add headers and then pass it to request object as below
doRequest (url : string, method : string, data?)
{
headers= new Headers();
headers.append(name1,value1);
headers.append(name2,value2);
....
let options = new RequestOptions ({ headers: headers, ... } );
return this.http.get (url, data, options)
.catch ((error) => { ... } );
}
That's the way today setting HTTP headers (Angular > 4):
Import:
import {HttpClient, HttpHeaders} from '#angular/common/http';
and usage:
const headers = new HttpHeaders()
.set("X-CustomHeader", "custom header value");
Notice that we are building the headers object by chaining successive set() methods. This is because HttpHeaders is immutable, and its API methods do not cause object mutation.
Instead, a call to set will return a new HttpHeaders object containing the new value properties. So this means that the following will NOT work:
const headers = new HttpHeaders ();
headers.set("X-CustomHeader", "custom header value")
I was reading angular2 code and I found some confusing syntax for me.
The full code is below.(from https://github.com/domfarolino/angular2-login-seed)
import { Injectable, Inject } from '#angular/core';
//import { Control } from '#angular/common';
import { Http, Response, Headers, RequestOptions, RequestOptionsArgs } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
/**
* Import interfaces that service depends on
*/
import { User } from './user';
#Injectable()
export class UserService {
constructor (private http: Http, #Inject('apiBase') private _apiBase: string) {
}
private _loginApi = this._apiBase + '/authorize/local';
private _logoutApi = this._apiBase + '/logout';
private _authenticatedApi = this._apiBase + '/api/authenticated';
private _registerApi = this._apiBase + '/api/users/register';
private _userExistsApi = this._apiBase + '/api/users/exists';
login(user) {
let body = JSON.stringify(user);
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post(this._loginApi, body, <RequestOptionsArgs> {headers: headers, withCredentials: true})
.map((res: Response) => res)
.catch(this.handleError);
}
authenticated() {
return this.http.get(this._authenticatedApi, <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
logout() {
return this.http.get(this._logoutApi, <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
register(user) {
let body = JSON.stringify(user);
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post(this._registerApi, body, <RequestOptionsArgs> {headers: headers, withCredentials: true})
.map((res: Response) => res)
.catch(this.handleError);
}
getUsers() {
return this.http.get(this._apiBase + "/api/users?limit=5&desc=true", <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json())
.catch(this.handleError);
}
getMe() {
return this.http.get(this._apiBase + '/api/users/me/', <RequestOptionsArgs> {withCredentials: true})
.map((res: Response) => res.json().me)
.catch(this.handleError);
}
private handleError (error: Response) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
return Observable.throw(error || "Server Error");
}
}
and I can't find out what below code as a parameter means.
<RequestOptionsArgs> {headers: headers, withCredentials: true}
Is there anyone can give me an idea?
The syntax <Type> variable is a cast. See Type Assertions on the documentation
Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.
Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.
It shows two examples, it is possible to cast using:
<string> somevar
and also with
somevar as string
The two samples are equivalent. Using one over the other is mostly a choice of preference; however, when using TypeScript with JSX, only as-style assertions are allowed.
class is work as a datatype here . ..
Ex .
Class student {
Name : String ,
RollNo: Number
}
now if i declare a variable
Public Students:Obect
Now i can push in students with a object having name and roll no.