I'm sending a request to an API, it returns an array of data, but I don't know how to extract the headers from that url, this is what i've tried in my service
#Injectable()
export class ResourcesService {
private resourcesurl = "http://localhost:9111/v1/resources";
constructor(private http: Http) { }
getResources() {
let headers = new Headers();
headers.append("api_key", "123456");
return this.http.get(this.resourcesurl, { headers: headers
}).map(this.extractData).catch(this.handleError);
}
getresourceheaders(){
let headers = new Headers();
headers.append("api_key", "123456");
let options = new RequestOptions();
let testsss = options.headers
let headerapi = this.http.request(this.resourcesurl, options);
let test = this.http.get(this.resourcesurl, { headers: headers });
console.log(headerapi);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
I want to get the headers from that response that in this case is resourceurl
any idea?
Clear angular 5 answer
By default, this.http.whatever's returned observable will be on the data returned, not the HttpResponse.
If you have a peak at: https://angular.io/api/common/http/HttpClient
You'll notice the options take an "observe" parameter of a HttpObserve type. While it's not documented what the HttpObserve is, if you put it as "response" then you will instead receive an instance of HttpResponse<T>(https://angular.io/api/common/http/HttpResponse)
So, here's an example request:
this.http.get(url, {observe: 'response'})
.subscribe(resp => console.log(resp.headers))
Note: Due to browser cors security, you will not be-able to see headers unless the API provides Access-Control-Expose-Headers: with your custom headers if your api and angular app do not have the same domain.
The headers are part of the Response class, so you should be able to see them in a handler like
http.get('/path/to/resource')
.subscribe((res:Response) => {
console.log(res.headers);
// you can assign the value to any variable here
});
When you do .map(this.extractData) the let body = res.json() from this.extractData function takes out everything from the response except the body.
Instead if you do following, .map((res: Response) => res), that will return the whole response and you can access all the attributes and assign them to variables.
Here's a Plunker demo.
A bit more of an exotic example in Angular 5 shown below. Using HttpClient to post to a GraphQL server, read the response and then extract a response header value and a response body value. The header is Total-Count in this case. cars is a field (array of Car) under another field data in the body. Also shows use of the rxjs first operator.
import { HttpClient, HttpHeaders, HttpResponse } from '#angular/common/http';
import { first } from 'rxjs/operators/first';
import { Car, CarPage } from '../models/car';
..........
..........
public find(filter: string, sort: string, limit: number): Observable<CarPage> {
let headers = new HttpHeaders().set("Content-Type", "application/graphql");
let carPage: CarPage = { cars: [], totalCount: 0 };
return this.http.post<HttpResponse<any>>('/graphql',
`query cars { cars(filter: "${filter}", sort: "${sort}", limit: ${limit}) {
id
make
model
year
}
}`,
{ headers: headers, observe: "response" }
)
.first((_, index) => index === 0, (response: HttpResponse<any>) => {
let totalCountHeaderValues = response.headers.getAll("Total-Count");
carPage.totalCount = (totalCountHeaderValues.length > 0) ? parseInt(totalCountHeaderValues[0]) : 0;
carPage.cars = response.body.data.cars;
return carPage;
})
}
The return type of the angular Http.get method returns a Response type. This object has a headers object that contains information about the headers. It also has a url property.
this.http.get(url).map(resp => console.log(resp));
Related
I dont know how to make an API call to such a method:
[HttpGet]
[ActionName("GetSupport")]
public HttpResponseMessage GetSupport(int projectid)
Because it is GET but still got a parameter to pass, how to do this?
Would it be something like this?
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('projectid', this.id);
this.http.get('http://localhost:63203/api/CallCenter/GetSupport', { headers: headers })
Having something like this:
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('projectid', this.id);
let params = new URLSearchParams();
params.append("someParamKey", this.someParamValue)
this.http.get('http://localhost:63203/api/CallCenter/GetSupport', { headers: headers, search: params })
Of course, appending every param you need to params. It gives you a lot more flexibility than just using a URL string to pass params to the request.
EDIT(28.09.2017): As Al-Mothafar stated in a comment, search is deprecated as of Angular 4, so you should use params
EDIT(02.11.2017): If you are using the new HttpClient there are now HttpParams, which look and are used like this:
let params = new HttpParams().set("paramName",paramValue).set("paramName2", paramValue2); //Create new HttpParams
And then add the params to the request in, basically, the same way:
this.http.get(url, {headers: headers, params: params});
//No need to use .map(res => res.json()) anymore
More in the docs for HttpParams and HttpClient
For Angular 9+ You can add headers and params directly without the key-value notion:
const headers = new HttpHeaders().append('header', 'value');
const params = new HttpParams().append('param', 'value');
this.http.get('url', {headers, params});
Above solutions not helped me, but I resolve same issue by next way
private setHeaders(params) {
const accessToken = this.localStorageService.get('token');
const reqData = {
headers: {
Authorization: `Bearer ${accessToken}`
},
};
if(params) {
let reqParams = {};
Object.keys(params).map(k =>{
reqParams[k] = params[k];
});
reqData['params'] = reqParams;
}
return reqData;
}
and send request
this.http.get(this.getUrl(url), this.setHeaders(params))
Its work with NestJS backend, with other I don't know.
Just offering up a helpful piece of code for anyone interested in the HttpParams direction mentioned in the answer from 2017.
Here is my go-to api.service which kind of "automates" params into that HttpParams for requests.
import { HttpClient, HttpParams } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Params } from '#angular/router';
import { Observable } from 'rxjs';
import { environment } from '#env';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
public get<T>(path: string, routerParams?: Params): Observable<T> {
let queryParams: Params = {};
if (routerParams) {
queryParams = this.setParameter(routerParams);
}
console.log(queryParams);
return this.http.get<T>(this.path(path), { params: queryParams });
}
public put<T>(path: string, body: Record<string, any> = {}): Observable<any> {
return this.http.put(this.path(path), body);
}
public post<T>(path: string, body: Record<string, any> = {}): Observable<any> {
return this.http.post(this.path(path), body);
}
public delete<T>(path: string): Observable<any> {
return this.http.delete(this.path(path));
}
private setParameter(routerParams: Params): HttpParams {
let queryParams = new HttpParams();
for (const key in routerParams) {
if (routerParams.hasOwnProperty(key)) {
queryParams = queryParams.set(key, routerParams[key]);
}
}
return queryParams;
}
private path(path: string): string {
return `${environment.api_url}${path}`;
}
}
An easy and usable way to solve this problem
getGetSuppor(filter): Observale<any[]> {
return this.https.get<any[]>('/api/callCenter/getSupport' + '?' + this.toQueryString(filter));
}
private toQueryString(query): string {
var parts = [];
for (var property in query) {
var value = query[propery];
if (value != null && value != undefined)
parts.push(encodeURIComponent(propery) + '=' + encodeURIComponent(value))
}
return parts.join('&');
}
I am developing an Angular application that shows some images to the user.
I would like to obtain those images from a single REST call to a web service: given the fact i am already uploading the images via a FormData object, i would like to receive those images in the same way (so, basically, via content-type: multipart/form-data).
At the moment, using the following code:
this.http.post('api/load', {}, {headers: {'Accept': 'multipart/form-data'},
responseType:'text', observe: 'response'});
i am actually receiving the full body of the response in a text format, like this:
--974b5730-ab25-4554-8a69-444664cab379
Content-Disposition: form-data; name=result
{"bar":[1,2,3,4], "foo": true}
--974b5730-ab25-4554-8a69-444664cab379
Content-Disposition: form-data; name=image; filename=image1.jpg; filename*=utf-8''image1.jpg
--- binarycontent...
But it's in a raw, text format.
How can i receive a multipart/form-data response formatted by its boundaries, or a in clean way in Angular 7.x?
One of the solution is to implement an interceptor service where you can format multipart/form-data response.
For example, your inteceptor will be - multipart.interceptor.ts :
#Injectable()
export class MultipartInterceptService implements HttpInterceptor {
private parseResponse(response: HttpResponse<any>): HttpResponse<any> {
const headerValue = response.headers.get('Content-Type');
const body = response.body;
const contentTypeArray = headerValue ? headerValue.split(';') : [];
const contentType = contentTypeArray[0];
switch (contentType) {
case 'multipart/form-data':
if (!body) {
return response.clone({ body: {} });
}
const boundary = body?.split('--')[1].split('\r')[0];
const parsed = this.parseData(body, boundary); // function which parse your data depends on its content (image, svg, pdf, json)
if (parsed === false) {
throw Error('Unable to parse multipart response');
}
return response.clone({ body: parsed });
default:
return response;
}
}
// intercept request and add parse custom response
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(customRequest).pipe(
map((response: HttpResponse<any>) => {
if (response instanceof HttpResponse) {
return this.parseResponse(response);
}
})
);
}
}
export class GitComponent implements OnInit {
http: HttpClient;
headerForAjax: HttpHeaders;
constructor(http: HttpClient) {
this.http = http;
}
ngOnInit(): void {
const headers = 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzYXNobyIsImF1dGgiOiJST0xFX1VTRVIiLCJleHAiOjE1MjQwODcyMzJ9.MUv5RgI9LxQyrrCfjfX8HR2-XiQmz4vjLqH7V_0Du7VFLC0WrK_y3FfeNoT2Nj_uguIK2ss7jv-LNiHuCGtz4A';
this.headerForAjax = new HttpHeaders().set('Authorization', headers);
const linkBindingModel = {
title: 'angular2',
linkUrl: 'angularUr2l',
imageUrl: 'imageUrl22'
};
this.http.post('http://localhost:8080/links/add', linkBindingModel, {headers: this.headerForAjax}).subscribe((e) => {
console.log(e);
});
}
}
So this ajax is send to my spring server and the server saves the data correctly in the DB, it basically all works well.
But i can't get the response status, i mean how can i get the 200 response status number or text that my server sends back?
try this, you can pass in an object with observe key to get the complete response
this.http.post('http://localhost:8080/links/add', linkBindingModel, {headers:
this.headerForAjax, observe: 'response'}).subscribe((response) => {
console.log(response.status); // response status
console.log(response.body); // response body (returned response)
});
In my Angular application service I have a method that makes a call to a mock JSON:
my.service.ts:
...
private baseUrl: string = 'http://localhost:9999/accounts-grid.json';
...
loadAccounts() {
if (this.dataStore.responseObject) {
this.refreshDataStore();
}
let token = this.authenticationService.getToken();
let headers = new Headers({ 'netx.esf.AuthenticationService': token });
let options = new RequestOptions({ headers: headers });
this.http.get(`${this.baseUrl}/positions/priorday/${accountNumber}`, options)
this.http.get(`${this.baseUrl}`, options)
.map((response: Response) => response.json())
.subscribe(
...
error => {
this.logService.error('loadAccountList() exception:', error);
this.setError(this.message[0], error._body); });
return this.responseObject$;
}
I would like to be able to load a different dummy JSON with the same call depending how many times the method was called. For example, the first time I call loadAccounts(), I would like to get a response from accounts-grid.json, the second time I make this call I would like to get a response from, say, accounts2-grid.json.
Is this possible?
Add a local variable to the service to keep track:
...
private baseUrl: string = 'http://localhost:9999/accounts-grid.json';
private callCount = 0;
...
loadAccounts() {
if ( this.callCount > 0 ) { const newUrl = this.baseUrl.substring(0, this.baseUrl.lastIndexOf('.json')) + this.callCount.toString() + this.baseUrl.substring(this.baseUrl.lastIndexOf('.json')); }
this.callCount += 1;
if (this.dataStore.responseObject) {
this.refreshDataStore();
}
let token = this.authenticationService.getToken();
let headers = new Headers({ 'netx.esf.AuthenticationService': token });
let options = new RequestOptions({ headers: headers });
this.http.get(`${newUrl}/positions/priorday/${accountNumber}`, options)
this.http.get(`${newUrl}`, options)
.map((response: Response) => response.json())
.subscribe(
...
error => {
this.logService.error('loadAccountList() exception:', error);
this.setError(this.message[0], error._body); });
return this.responseObject$;
}
You will probably also want to take care of the callCount item to subtract a count if there's an error, but this is the general idea.
TLDR: My task is to complete 3 requests instead of 1 and return the last response as a response to the first request without any additional modifications of the request initiator.
I have extended the Angular Http class to automatically append authorization headers to all of my requests and implement my own authorization error handling.
It looks like this:
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
// ... append some headers
super.request(url, options).catch((error: Response) => {
if (error.status === 401 || error.status === 403 ) {
// todo: Send refreshToken request to get new credentials
// todo: Send current request again with new credentials
// todo: If request is completed properly pretend everything was fine and return response
}
});
}
I want to catch authorization errors, fix them by sending token refresh request and return proper response to the initial request.
There's a lot of code using http now and I don't want to change it so the fixed response has to be returned as the initial would have been without anybody knowing about it.
One of the approaches was to use synchronous requests but it's not a good idea I think.
Could you tell please if the solution is possible and how can I achieve it?
PS. There may be a problem when another request is executed while the token is being refreshed and crash into an authorization causing one more token refresh. But this is not that important now.
The aim was achieved mostly by using flatMap to compose requests.
Key functions:
Check if request request returns 401
If 401: tries to fix renew necessary tokens and sends request again
The subscriber knows nothing about error if it's fixed
It's designed to work with the REST authentication model which includes:
guest token - for unauthorized users (gToken)
auth token - for authorized users - (aToken)
refresh token - to refresh expired aToken (refresh_token)
Most likely you will need to rewrite requests to fit your backend but here's a well-commented Services to be provided instead of default Http:
import {Injectable} from '#angular/core';
import {
Http, XHRBackend, RequestOptions, RequestOptionsArgs, Request, Response, RequestMethod,
Headers
} from "#angular/http";
import { Observable } from "rxjs";
import { StorageService } from "../storage.service";
import { AppService } from "./app.service";
#Injectable()
export class HttpClientService extends Http {
private autoAppendHeadersDefault = true;
constructor(
backend: XHRBackend,
defaultOptions: RequestOptions,
private storageService: StorageService,
private appState: AppService,
) {
super(backend, defaultOptions);
this.autoAppendHeadersDefault = this.appState.hoodConfig.HTTP_AUTO_APPEND_HEADERS;
}
request(url: string | Request, options?: RequestOptionsArgs, disableTryFix = false): Observable<Response> {
// Checking if the request needs headers to be appended
let assetRequest = false;
if(url instanceof Request) {
if(url.url.startsWith("/assets")) {
assetRequest = true;
}
}
// Appending headers
if(!assetRequest && this.appState.hoodConfig.HTTP_AUTO_APPEND_HEADERS && url instanceof Request) {
// append aToken || gToken
let token = this.storageService.get('aToken');
if('undefined' === typeof token || !token) {
token = this.storageService.get('gToken');
}
if('undefined' !== typeof token && token) {
url.headers.set('Authorization', `Bearer ${token}`);
} else {
// neither aToken nor gToken are set
if(disableTryFix) {
this.removeAllTokens();
return Observable.throw({error: "Can't reauth: 01"});
}
return this.tryFixAuth().flatMap(
(res:any) => {
res = res.json();
this.storageService.set('gToken', res.access_token);
return this.request(url, options, true);
}
);
}
// headers appended to every request
if(!url.headers.get('Content-Type')) {
url.headers.append('Content-Type', 'application/json');
}
}
this.appState.hoodConfig.HTTP_AUTO_APPEND_HEADERS = this.autoAppendHeadersDefault;
return super.request(url, options).catch((error: Response) => {
if (error.status === 401 /* || error.status === 403 */ ) {
if(disableTryFix) {
this.removeAllTokens();
this.navigateOnAuthFail();
return Observable.throw({error: "Can't reauth: 02"});
}
return this.tryFixAuth().flatMap(
(res: any) => {
res = res.json();
if('undefined' !== typeof res.refresh_token)
{
// got aToken & refresh_token
this.storageService.set('aToken', res.access_token);
this.storageService.set('refresh_token', res.refresh_token);
}
else if('undefined' !== typeof res.access_token)
{
// got only gToken
this.storageService.set('gToken', res.access_token);
}
else
{
console.log('tryFix: nothing useful returned')
// got no aToken, no gToken, no refresh_token
}
// retry request
return this.request(url, options, true);
}
);
}
// handle invalid refresh_token
if(disableTryFix && error.status === 400) {
console.log('Wrong refresh token (400)');
this.storageService.remove('refresh_token');
this.storageService.remove('aToken');
this.navigateOnAuthFail();
// handle invalid refresh token
}
return Observable.throw(error);
});
}
private tryFixAuth(): Observable<Response> {
console.log('Trying to fix auth');
if(this.storageService.get('refresh_token'))
{
return this.refreshToken();
}
else if(this.storageService.get('aToken'))
{
// no refresh_token, but aToken
// since aToken is dead it's not useful
this.storageService.remove('aToken');
}
else
{
// no aToken, no refresh_token
// possibly there's a gToken
// since the request is trying to fix itself (is failed) the gToken is most likely not valid
return this.guestToken();
}
}
// sends request with refresh_token to get new aToken
// the request returns only aToken and refresh_token, no gToken
private refreshToken(): Observable<Response> {
// is called only when refresh_token is set
let refreshToken = this.storageService.get('refresh_token');
// check refresh_token in case it's not checked before
if('undefined' === typeof refreshToken || !refreshToken || refreshToken == 'undefined') {
this.storageService.remove('refresh_token');
// there's no refresh_token saved
return Observable.throw({error: "Refresh token is not set"});
}
// form refresh_token request
const headers = new Headers();
headers.append('Authorization', `Bearer ${this.storageService.get('gToken')}`);
headers.append('Content-Type', 'application/json');
const url = `${this.appState.config.WEBSITE_ENDPOINT}/oauth/v2/token`;
const localData = JSON.stringify({
"client_id": this.appState.config.CLIENT_ID,
"client_secret": this.appState.config.CLIENT_SECRET,
"grant_type": 'refresh_token',
"refresh_token": refreshToken
});
this.appState.hoodConfig.HTTP_AUTO_APPEND_HEADERS = false;
// refresh_token request
return this.request(
new Request({
method: RequestMethod.Post,
url: url,
headers: headers,
body: localData
}),
null, true);
}
// sends request to get new gToken
private guestToken(): Observable<Response> {
const url = `${
this.appState.config.WEBSITE_ENDPOINT}/oauth/v2/token?client_id=${
this.appState.config.CLIENT_ID}&client_secret=${
this.appState.config.CLIENT_SECRET}&grant_type=client_credentials`;
this.appState.hoodConfig.HTTP_AUTO_APPEND_HEADERS = false;
return super.get(url);
}
// Aux methods
private navigateOnAuthFail() {
console.warn('Page is going to be refreshed');
// redirect to auth is performed after reload by authGuard
// it's possible to add some warning before reload
window.location.reload();
}
private removeAllTokens() {
this.storageService.remove('aToken');
this.storageService.remove('gToken');
this.storageService.remove('refresh_token');
}
}