I've post method at my angular service say AuthService.ts like this
#Injectable({
providedIn: 'root'
})
export class AuthService {
...
...
login(username: string, password: string) {
let headers = new Headers(); //1
headers.append('username', username); //2
let options = {
headers: headers
}; //3
return this.http.post < any > (`${environment.authBaseUrl}`, {
username,
password
}, options) //4
.pipe(map(user => {
if (user && user.token) {
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
}
return user;
}));
}
}
My purpose is to add the username at the POST request header. Cause there in the API it is expecting that a username will be found at the request header. With the help of this post in StackOverflow, I'm trying to add the header (from comment 1 to 4). I'm getting the below error message -
TS2345:
Argument of type '{ headers: Headers; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'.
Types of property 'headers' are incompatible.
Type 'Headers' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'.
Type 'Headers' is not assignable to type '{ [header: string]: string | string[]; }'.
Index signature is missing in type 'Headers'.
If I remove the option from the post method then everything works fine.
Can anyone help me with this?
Thanks in advance
Use HttpHeaders instead of Headers,
import { HttpHeaders } from '#angular/common/http';
let options = {
headers: new HttpHeaders({ 'username': username })
}
Consider using HttpHeaders.
For example:
(I just put application/json content-type header for example purpose).
import { HttpHeaders } from '#angular/common/http';
[...]
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json', 'username': username)
};
[...]
return this.http.post<any>(`${environment.authBaseUrl}`, { username, password }, httpOptions) //4
.pipe(map(user => {
if (user && user.token) {
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
}
return user;
}));
Related
I trying to make http request to the spring rest API.. API returns a string value ("success" or "fail")... but I dont know how to set the response type as string value while making call to the API..its throwing error as Backend returned code 200, body was: [object Object]
My angular code is like below,
order.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { ProductSearch } from '../_models/product-search';
import { ProductView } from '../_models/product-view';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorHandlerService } from './error-handler.service';
import { Category } from '../_models/category';
#Injectable({
providedIn: 'root'
})
export class OrderService {
constructor(private http: HttpClient, private errorHandlerService: ErrorHandlerService) { }
addToCart(productId: number, quantity: number): Observable<any> {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
console.log("--------order.service.ts----------addToCart()-------productId:"+productId+":------quantity:"+quantity);
return this.http.post<any>('http://localhost:8080/order/addtocart',
{ dealerId: 13, createdBy: "-1", productId: productId, quantity: quantity},
{headers: headers})
.pipe(catchError(this.errorHandlerService.handleError));
}
}
error-handler.service.ts
import { Injectable } from '#angular/core';
import { HttpErrorResponse, HttpResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ErrorHandlerService {
constructor() { }
public handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError(
'Something bad happened; please try again later.');
};
}
You should not use those headers, the headers determine what kind of type you are sending, and you are clearly sending an object, which means, JSON.
Instead you should set the option responseType to text:
addToCart(productId: number, quantity: number): Observable<any> {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.post(
'http://localhost:8080/order/addtocart',
{ dealerId: 13, createdBy: "-1", productId, quantity },
{ headers, responseType: 'text'}
).pipe(catchError(this.errorHandlerService.handleError));
}
To get rid of error:
Type '"text"' is not assignable to type '"json"'.
Read the Angular HTTP guide and use
responseType: 'text' as const
import { HttpClient, HttpHeaders } from '#angular/common/http';
.....
return this.http
.post<string>(
this.baseUrl + '/Tickets/getTicket',
JSON.stringify(value),
{ headers, responseType: 'text' as const }
)
.map(res => {
return res;
})
.catch(this.handleError);
On your backEnd, you should add:
#RequestMapping(value="/blabla", produces="text/plain" , method = RequestMethod.GET)
On the frontEnd (Service):
methodBlabla()
{
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.get(this.url,{ headers, responseType: 'text'});
}
Use like below:
yourFunc(input: any):Observable<string> {
var requestHeader = { headers: new HttpHeaders({ 'Content-Type': 'text/plain', 'No-Auth': 'False' })};
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.post<string>(this.yourBaseApi+ '/do-api', input, { headers, responseType: 'text' as 'json' });
}
For me this way worked. Like requestOptions as object
returnObservable(): Observable<any> {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
const requestOptions: Object = {
headers: headers,
responseType: 'text'
}
return this.http.get<any>(this.streamURL , requestOptions);
}
To fix the compiler error, remove the generic type argument from the post method call.
DO THIS
return this.http.post('example', postBody, {
responseType: 'text'
});
NOT THIS
return this.http.post<any>('example', postBody, {
responseType: 'text'
});
The error appears because the post method signature does not contain a generic type argument when responseType: 'text.
See the different method signatures below:
With responseType: 'json' (the default)
post<T>(url: string, body: any | null, options?: {
...
responseType?: 'json';
...
}): Observable<T>;
With responseType: 'text'
post(url: string, body: any | null, options: {
...
responseType: 'text';
...
}): Observable<string>;
Notice the generic type argument only exists for type 'json'.
Remove it to fix the error.
The default assumption of the HttpClient is 'json' responseType.
If you want to change it to 'text', you should do it like so:
public signIn(dto: UserCredentialsDto): Promise<string> {
return this.http.post<string>(
`${this.url}/userCredentials/signIn`, dto, { responseType: 'text' as 'json'}).toPromise();
}
By Default angular return responseType as Json, but we can configure below types according to your requirement.
responseType: 'arraybuffer'|'blob'|'json'|'text'
Ex:
this.http.post(
'http://localhost:8080/order/addtocart',
{ dealerId: 13, createdBy: "-1", productId, quantity },
{ headers, responseType: 'text'});
Have you tried not setting the responseType and just type casting the response?
This is what worked for me:
/**
* Client for consuming recordings HTTP API endpoint.
*/
#Injectable({
providedIn: 'root'
})
export class DownloadUrlClientService {
private _log = Log.create('DownloadUrlClientService');
constructor(
private _http: HttpClient,
) {}
private async _getUrl(url: string): Promise<string> {
const httpOptions = {headers: new HttpHeaders({'auth': 'false'})};
// const httpOptions = {headers: new HttpHeaders({'auth': 'false'}), responseType: 'text'};
const res = await (this._http.get(url, httpOptions) as Observable<string>).toPromise();
// const res = await (this._http.get(url, httpOptions)).toPromise();
return res;
}
}
I'm working on web-app with authorization via JWT and Angular 2. I've Nodejs/express server with API and client-side on Angular2.
So, my server answers GET request correctly and gives data like this:
{
"success": true,
"user": {
"_id": "5a6ef70edb04dd29e24bb03b",
"email": "danko",
"username": "ivan"
}
}
Next, here is my auth.service.ts. Functions createAuthenticationHeaders() and getProfile() takes part in handling HTTP responses:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpRequest, HttpResponse, HttpParams} from '#angular/common/http';
import { Http, Headers, RequestOptions} from '#angular/http'; // Http, Headers, RequestOptions
import 'rxjs/add/operator/map';
#Injectable()
export class AuthService {
domain = "http://localhost:8080";
authToken;
user;
options;
constructor(
private http: HttpClient,
private httplegacy: Http) { }
createAuthenticationHeaders() {
this.loadToken();
this.options = new RequestOptions({
headers : new Headers({
'Content-Type' : 'application/json',
'authorization' : this.authToken
})
});
}
loadToken() {
this.authToken = localStorage.getItem('token');
}
registerUser(user) {
return this.http.post(this.domain + '/authentication/register', user);
}
loginUser(user) {
return this.http.post(this.domain + '/authentication/login', user);
}
storeUserData(token, user) {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
this.authToken = token;
this.user = user;
}
getProfile() {
this.createAuthenticationHeaders();
return this.httplegacy.get(this.domain + '/authentication/profile', this.options);
}
}
Also, here is my profile.component.ts:
import { Component, OnInit } from '#angular/core';
import { AuthService} from '../../services/auth.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
username;
email;
constructor(
private authService: AuthService
) { }
ngOnInit() {
this.authService.getProfile().subscribe(profile => {
console.log(profile);
this.username = profile.user.username;
this.email = profile.user.email;
})
}
}
Expected behavior of these lines of code: after handling server's response with user's data with auth.service.ts(mainly createAuthenticationHeaders() and getProfile() functions), user's data is transmitted to profile.component.ts to show it on web-page using next code:
<h2 class="page-header">Profile Page</h2>
<ul class="list-group">
<li class="list-group-item">Username: {{ username }} </li>
<li class="list-group-item">Email: {{ email }}</li>
</ul>
But, while compiling I got an error: property 'user', doesn't exist on type 'Response'. Would You like to explain why i got such error, and how to fix it?
P.S.: yep, console.log(profile) gives me such info:
Response {_body: "{"success":true,"user":{"_id":"5a6ef70edb04dd29e24bb03b","email":"danko","username":"ivan"}}", status: 200, ok: true, statusText: "OK", headers: Headers, …}
headers:Headers {_headers: Map(1), _normalizedNames: Map(1)}
ok : true
status : 200
statusText : "OK"
type : 2
url : "http://localhost:8080/authentication/profile"
_body : "{"success":true,"user":{"_id":"5a6ef70edb04dd29e24bb03b","email":"danko","username":"ivan"}}"
__proto__ : Body
constructor : ƒ Response(responseOptions)
toString : ƒ ()
__proto__ :
Object
But how can I get data from _body field of response?
P.S.: code for router from server side:
router.get('/profile', (req, res) => {
User.findOne({ _id: req.decoded.userId }).select('username email').exec((err, user) => {
if (err) {
res.json({ success: false, message: err });
} else {
if(!user) {
res.json({ success: false, message: 'User not found'});
} else{
res.json({ success: true, user: user });
}
}
});
});
you try to read your data directly from the Response Object of express. You need smth like:
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = JSON.parse(profile._body)
this.username = p.user.username;
this.email = p.user.email;
})
This will take the JSON string from the body of your HTTP Response and make it an accessible object.
NOTE:
It would be much better to tell the server to answer with a standard json due to this is web standard nowadays.
Update: #messerbill's was 50/50 correct. Such construction works:
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = JSON.parse(profile._body)
this.username = p.user.username;
this.email = p.user.email;
})
My web-page got user's info and show it in correctly, but an error left and i've to comment these lines of code to compile and run my application, and uncomment after to see user's info on webpage.
An error message: property '_body' does not exists on type 'Response'.
So, at this moment i've no idea how it works with an error and how to create really correct structure.
Try this
ngOnInit() {
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = profile.json();
this.username = p.user.username;
this.email = p.user.email;
})
}
I am trying to make a simple email form for one of my websites that allows people to contact me. This site is using angular 4, and mailgun as the mail service. In my mail service file I have this method that sends the message, but I am getting a Bad Request error saying from is not present.
public sendMail(){
let url = 'https://api.mailgun.net/v3/XXXXXXXXXXXX.mailgun.org/messages';
let headers: Headers = new Headers();
headers.append('Authorization','Basic '+ btoa('api:key-XXXXXXXXXXXXXXXXXXX'));
headers.append("Content-Type", "application/x-www-form-urlencoded");
let opts: RequestOptions = new RequestOptions();
opts.headers = headers;
this.http.post(url,
{
from: '"Mailgun Sandbox" <postmaster#XXXXXXXXXX.mailgun.org>',
to: "Test <test#gmail.com>",
subject: 'Hello ',
text: 'Congratulations, you just sent an email with Mailgun! You are truly awesome!'
},
opts
).subscribe(
success => {
console.log("SUCCESS -> " + JSON.stringify(success));
}, error => {
console.log("ERROR -> " + JSON.stringify(error));
}
);
}
I am having a hard time understanding why from is showing up not as present when I send the request. Any help is great.
import { Injectable } from '#angular/core';
import { HttpHeaders, HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class PostService {
constructor(private http: HttpClient) {
}
sendMail() {
const headers = new HttpHeaders({
'enctype': 'multipart/form-data',
'Authorization': 'Basic ' + btoa('api:xxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-xxxxxx')
});
const formData = new FormData();
formData.append('from', 'Mailgun Sandbox <postmaster#sandboxxxxxxxxxxxxxx.mailgun.org>');
formData.append('to', 'xxxxxxxxxxxxxx.com');
formData.append('subject', 'Hello');
formData.append('text', 'This is cool !');
this.http
.post(
'https://api.mailgun.net/v3/sandboxxxxxxxxxxxxxxxxxxxxxxxxxxb.mailgun.org/messages',
formData,
{ headers }
).subscribe(
res => { console.log('res : ', res); },
err => { console.log('err : ', err); }
);
}
}
I'm new to angular2. In 1.* everything was fine with interceptors, just add them: and you have everywhere your headers, and you can handle your requests, when token became invalid...
In angular2 i'm using RxJs.
So i get my token:
getToken(login: string, pwd: string): Observable<boolean> {
let bodyParams = {
grant_type: 'password',
client_id: 'admin',
scope: AppConst.CLIENT_SCOPE,
username: login,
password: pwd
};
let params = new URLSearchParams();
for (let key in bodyParams) {
params.set(key, bodyParams[key])
}
let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded'});
let options = new RequestOptions({headers: headers});
return this.http.post(AppConst.IDENTITY_BASE_URI + '/connect/token', params.toString(), options)
.map((response: Response) => {
let data = response.json();
if (data) {
this.data = data;
localStorage.setItem('auth', JSON.stringify({
access_token: data.access_token,
refresh_token: data.refresh_token
}));
return true;
} else {
return false;
}
});
}
and then how can i use this token in every request? i don't want to set .header in every request. It's a bad practice.
And then: for example when i do any request, and get 401-error, how can i intercept, and get a new token, and then resume all requests, like it was in angular 1?
i tried to use JWT from here jwt, but it doesn't meet my requirements, btw in first angular i was using Restangular - and everything was fine there (also with manual on tokens:https://github.com/mgonto/restangular#seterrorinterceptor)
You can either extend the default http service and use the extended version, or you could create a method that gets some parameters (if necessary) and return a RequestOptions objects to pass default http service.
Option 1
You can create a service:
#Injectable()
export class HttpUtils {
constructor(private _cookieService: CookieService) { }
public optionsWithAuth(method: RequestMethod, searchParams?: URLSearchParams): RequestOptionsArgs {
let headers = new Headers();
let token = 'fancyToken';
if (token) {
headers.append('Auth', token);
}
return this.options(method, searchParams, headers);
}
public options(method: RequestMethod, searchParams?: URLSearchParams, header?: Headers): RequestOptionsArgs {
let headers = header || new Headers();
if (!headers.has('Content-Type')) {
headers.append('Content-Type', 'application/json');
}
let options = new RequestOptions({headers: headers});
if (method === RequestMethod.Get || method === RequestMethod.Delete) {
options.body = '';
}
if (searchParams) {
options.params = searchParams;
}
return options;
}
public handleError(error: Response) {
return (res: Response) => {
if (res.status === 401) {
// do something
}
return Observable.throw(res);
};
}
}
Usage example:
this._http
.get('/api/customers', this._httpUtils.optionsWithAuth(RequestMethod.Get))
.map(res => <Customer[]>res.json())
.catch(err => this._httpUtils.handleError(err));
This example is using cookies to store and access the token. You could use a parameter as well.
Option 2
Second option is to extend http service, for example like this:
import { Injectable } from '#angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class MyHttp extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = 'fancyToken';
options.headers.set('Auth', token);
super(backend, options);
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = 'fancyToken';
if (typeof url === 'string') {
if (!options) {
options = {headers: new Headers()};
}
options.headers.append('Auth', token);
} else {
url.headers.append('Auth', token);
}
return super.request(url, options).catch(this.handleError(this));
}
private handleError (self: MyHttp) {
return (res: Response) => {
if (res.status === 401) {
// do something
}
return Observable.throw(res);
};
}
}
And in your #NgModule:
#NgModule({
// other stuff ...
providers: [
{
provide: MyHttp,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new MyHttp(backend, options);
},
deps: [XHRBackend, RequestOptions]
}
]
// a little bit more other stuff ...
})
Usage:
#Injectable()
class CustomerService {
constructor(private _http: MyHttp) {
}
query(): Observable<Customer[]> {
return this._http
.get('/api/customers')
.map(res => <Customer[]>res.json())
.catch(err => console.log('error', err));
}
}
Extra:
If you want to use refresh token to obtain a new token you can do something like this:
private handleError (self: MyHttp, url?: string|Request, options?: RequestOptionsArgs) {
return (res: Response) => {
if (res.status === 401 || res.status === 403) {
let refreshToken:string = 'fancyRefreshToken';
let body:any = JSON.stringify({refreshToken: refreshToken});
return super.post('/api/token/refresh', body)
.map(res => {
// set new token
})
.catch(err => Observable.throw(err))
.subscribe(res => this.request(url, options), err => Observable.throw(err));
}
return Observable.throw(res);
};
}
To be honest, I haven't tested this, but it could provide you at least a starting point.
We solved the issue with extension of AuthHttp. We added a method a on AuthHttp to set a new header dynamically like that (X-RoleId is a custom header)
declare module 'angular2-jwt' {
interface AuthHttp {
setRoleId(config: {});
}
}
AuthHttp.prototype.setRoleId = function (roleId) {
let jsThis = <any>(this);
jsThis.config.globalHeaders = [
{'Content-Type': 'application/json'},
{'X-RoleId': roleId}
];
};
I am using ionic 2 / angular 2.
I need to do a http request, but before I have to get a token using Ionic Storage.
I created a class ApiRequest for that
import {Http, Headers, RequestOptions} from '#angular/http';
import {Injectable} from '#angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Storage } from '#ionic/storage';
#Injectable()
export class ApiRequest {
access_token: string;
constructor(private http: Http, public storage: Storage) {
this.storage.get('access_token').then( (value:any) => {
this.access_token = value;
});
}
get(url) {
let headers = new Headers({
// 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.access_token,
'X-Requested-With': 'XMLHttpRequest'
});
let options = new RequestOptions({ headers: headers });
return this.http.get(url, options)
.map(res => res.json());
}
}
Then I can call like that
apiRequest.get(this.URL)
.subscribe(
data => {
this.element= data;
},
err => {
console.log(JSON.stringify(err));
});
My problem is, this.storage.get is asynchronous, http.get is asynchronous too, and I have to return http.get because I want to call subscribe outside the function.
In this case http.get is called before this.acess token received the value.
How Can I organize my code in that scenario?
This might work (not tried myself):
#Injectable()
export class ApiRequest {
access_token: string;
constructor(private http: Http, public storage: Storage) {
this.storagePromise = this.storage.get('access_token').then( (value:any) => {
this.access_token = value;
});
}
get(url) {
let headers = new Headers({
// 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.access_token,
'X-Requested-With': 'XMLHttpRequest'
});
let options = new RequestOptions({ headers: headers });
return this.storagePromise.then(
return token => this.http.get(url, options)
.map(res => res.json());
);
}
}
apiRequest.get(this.URL)
.then(observable =>
observable.subscribe(
data => {
this.element= data;
},
err => {
console.log(JSON.stringify(err));
}
);