After i looked up the www, stackoverflow and youtube, i couldn't find an concrete answer about, how to fetch an image correctly from:
https://graph.microsoft.com/beta/me/photo/$value
in TypeScript/JavaScript. The solution is really easier as i thought. You just need this an additional information in your GET Request:
responseType: ResponseContentType.ArrayBuffer
The complete solution looks like this:
import { DomSanitizer } from "#angular/platform-browser";
import { Http, Headers, ResponseContentType } from "#angular/http";
import "rxjs/add/operator/map";
import { AlertController } from "ionic-angular";
export class GraphService {
constructor(
private http: Http,
private alertCtrl: AlertController,
private sanitizer: DomSanitizer
) {}
transform(html) {
return this.sanitizer.bypassSecurityTrustUrl(html);
}
getProfileImage(accessToken: string) {
// fetch User Profile Image
let headers: Headers = new Headers();
headers.append("Authorization", "Bearer " + accessToken);
this.http
.get("https://graph.microsoft.com/beta/me/photo/$value", {
responseType: ResponseContentType.ArrayBuffer,
headers: headers
})
.map(res => res)
.subscribe(
(data: any) => {
let blob = new Blob([data.arrayBuffer()], {
type: data.headers.get("content-type")
});
let imageUrl = window.URL.createObjectURL(blob);
return this.transform(imageUrl));
},
error => {
let alert = this.alertCtrl.create({
title: "GraphProvider",
subTitle: "Can't fetch profile image!",
buttons: ["OK"]
});
alert.present();
}
);
}
}
The most important part is: don't append the responseType: ResponseContentType.ArrayBuffer in your header! You have to provide it beside the header! And don't forget to import it from #angular/http!
I hope I could help all the lost souls out there :D
PS: Thanks to Micheal Mainer for the Graph Explorer Repo: https://github.com/microsoftgraph/microsoft-graph-explorer/tree/e2a376615d14c5eabd51e972478b18827800d323
here's my fetched Image from Microsoft Graph API:
Related
I spent many hours without success. I know it's a common problem, many solutions but for me works only Interceptor that I want to avoid.
My service - here I get email with plus like john.doe+100#gmail.com
#Injectable({
providedIn: 'root',
})
export class UsersHttpService {
httpParams = new HttpParams({encoder: new CustomEncoder()});
removeUsersFromGroup(groupId: string, email: string): Observable<any> {
console.log(email); //john.doe+100#gmail.com
let parsedEmail = encodeURI(email); //one of many attempts
return this.http.delete(`${this.env.URI}/monitoring/api/v1/groups/${groupId}/users/`, {
params: {
groupId,
email: email.replace(' ', '+')
},
});
}
And my CustomEncoder:
export class CustomEncoder implements HttpParameterCodec {
encodeKey(key: string): string {
return encodeURIComponent(key);
}
encodeValue(value: string): string {
// console.log('encodeValue encodeValue');
// console.log(value);
// console.log(encodeURIComponent(value));
return encodeURIComponent(value);
}
decodeKey(key: string): string {
return decodeURIComponent(key);
}
decodeValue(value: string): string {
// console.log('decodeValue decodeValue');
// console.log(value);
// console.log(decodeURIComponent(value));
return decodeURIComponent(value);
}
}
When I send request from Angular then in the Network tab in web browser I see:
DELETE https://myapp/groups/d39a4f50-8ebd-11ea-a9ae-5103b15ad73b/users/?groupId=d39a4f50-8ebd-11ea-a9ae-5103b15ad73b&email=john.doe 100#gmail.com
with a space! What's wrong? Were is the problem? IN the console I get email with + but in Network tab without space instead of + sign.
My params are properly encoded (there is 200 status from backend (spring boot), email with +) ONLY when I use global interceptor (which should be avoided):
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpParams,
HttpRequest,
} from "#angular/common/http";
import {Injectable} from "#angular/core";
import {Observable} from "rxjs";
import {CustomEncoder} from "./customEncoder";
#Injectable()
export class EncodeHttpParamsInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const params = new HttpParams({
encoder: new CustomEncoder(),
fromString: req.params.toString(),
});
return next.handle(req.clone({params}));
}
}
Does anyone have any idea??? I tried to use:
return this.http.delete(${this.env.ORBITAL_URI}/monitoring/api/v1/groups/${groupId}/users/, {
params: {
groupId,
email: encodeURI(email) //or encodeURIComponent(email)
},
});
and then in Network tab I see something like john.doe%2B%40gmail.com but I get 500 error from backend
My solution - without any interceptor:
removeUsersFromGroup(groupId: string, email: string): Observable<any> {
const params = new HttpParams({
encoder: new CustomEncoder(),
fromObject: {
groupId,
email,
},
});
return this.http.delete(`${this.env.URI}/myapp/v1/groups/${groupId}/users/`, {
params: params,
});
}
Now it works as expected:)
I hooked up a front end to a graphql server. Most if not all the mutations are protected while all the queries are not protected. I have an auth system in place where if you log in, you get an access/refresh token which all mutations are required to use. And they do which is great, backend receives the headers and everything!
HOWEVER. There is one query that needs at least the access token to distinguish the current user! BUT the backend does not receive the two headers! I thought that the middlewareLink I created would be for all queries/mutations but I'm wrong and couldn't find any additional resources to help me out.
So here's my setup
apollo-client.js
import { InMemoryCache } from "apollo-cache-inmemory"
import { persistCache } from "apollo-cache-persist"
import { ApolloLink } from "apollo-link"
import { HttpLink } from "apollo-link-http"
import { onError } from "apollo-link-error"
import { setContext } from "apollo-link-context"
if (process.browser) {
try {
persistCache({
cache,
storage: window.localStorage
})
} catch (error) {
console.error("Error restoring Apollo cache", error)
}
}
const httpLink = new HttpLink({
uri: process.env.GRAPHQL_URL || "http://localhost:4000/graphql"
})
const authMiddlewareLink = setContext(() => ({
headers: {
authorization: localStorage.getItem("apollo-token") || null,
"x-refresh-token": localStorage.getItem("refresh-token") || null
}
}))
const afterwareLink = new ApolloLink((operation, forward) =>
forward(operation).map(response => {
const context = operation.getContext()
const {
response: { headers }
} = context
if (headers) {
const token = headers.get("authorization")
const refreshToken = headers.get("x-refresh-token")
if (token) {
localStorage.setItem("apollo-token", token)
}
if (refreshToken) {
localStorage.setItem("refresh-token", refreshToken)
}
}
return response
})
)
const errorLink = onError(({ graphQLErrors, networkError }) => {
...
// really long error link code
...
})
let links = [errorLink, afterwareLink, httpLink]
if (process.browser) {
links = [errorLink, afterwareLink, authMiddlewareLink, httpLink]
}
const link = ApolloLink.from(links)
export default function() {
return {
cache,
defaultHttpLink: false,
link
}
}
Is there a way to target ALL mutations/queries with custom headers not just mutations? Or apply some headers to an individual query since I could probably put that as an app middleware?
edit: Haven't solved the SSR portion of this yet.. will re-edit with the answer once I have.
I'm following the step by step tutorial of NativeScript but I'm facing the following issue:
When I try to make a post request through my Angular service I get the following error:
JS: {"message":"The specified content type was not found.","errorCode":611}
My code looks pretty much the same as the one in the official docs from NativeScript:
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { User } from './user';
import { Config } from '../config';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
constructor(private _http: Http) { }
register(user: User): Observable<Response> {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const url = `${Config.apiUrl}/Users`;
const body = JSON.stringify({
Username: user.email,
Email: user.email,
Password: user.password
});
return this._http.post(url, body, { headers })
.catch(this.handleErrors);
}
handleErrors(error: Response) {
console.error(JSON.stringify(error.json()));
return Observable.throw(error);
}
}
Component class:
signUp() {
this._user.register(this.user).subscribe(
() => {
alert('Your account has been successfully created.');
this.toggleDisplay();
},
() => {
// errors here...
alert('Unfortunately we were unable to create your account.');
}
);
}
Tested under Android and IOs, both throws the same error.
Isn't application/json the expected Content-Type in this case?
If I change the type I get another error saying that the type is missing or is invalid.
Can someone please point out what am I missing?
i used Visual Studio for a WebApp-Projekt with Angular2 as frontend and asp.core as backend.
Now i want to get a status from my API. The Api works, i used it first with postman.
In my Angular-Component, the component says it does not know about toPromise(). i think the code works, but visual studio blocked to build the app.
import { Injectable } from '#angular/core';
import { ImportState } from './importstate';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class ImportService {
private headers = new Headers({ 'Content-Type': 'application/json', 'user': 'Superadmin', 'password':'Rest!12345' });
private heroesUrl = 'api/import/'; // URL to web api
constructor(private http: Http) { }
getStatus(type : string): Promise<ImportState> {
return this.http.get(this.heroesUrl + 'getStatus/' + type, { headers: this.headers })
.toPromise()
.then(response => response.json() as ImportState)
.then(r => console.log(r))
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
//console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
I hope somebody can help me.
Is there currently a way within Angular 2 to retrieve the progress (i.e. percentage done) of an ajax call, using the angular2/http module?
I use the following code to make my HTTP calls:
let body = JSON.stringify(params);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, body, options)
.timeout(10000, new Error('Timeout exceeded during login'))
.toPromise()
.then((res) => {
...
}).catch((err) => {
...
});
The goal is to write a synchronisation system. The post will return a lot of data, and I want to give the user an indication on how long the syncing will take.
Currently (from v. 4.3.0, when using new HttpClient from #ngular/common/http) Angular provides listening to progress out of the box. You just need to create HTTPRequest object as below:
import { HttpRequest } from '#angular/common/http';
...
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
});
And when you subscribe to to request you will get subscription called on every progress event:
http.request(req).subscribe(event => {
// Via this API, you get access to the raw event stream.
// Look for upload progress events.
if (event.type === HttpEventType.UploadProgress) {
// This is an upload progress event. Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% uploaded.`);
} else if (event instanceof HttpResponse) {
console.log('File is completely uploaded!');
}
});
More info here.
You could leverage the onprogress event provided by XHR (see this plunkr: http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview).
This allows to get hints about the progress of the download. This isn't supported out of the box by Angular2 but you can plug it by extended the BrowserXhr class:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor(private service:ProgressService) {}
build(): any {
let xhr = super.build();
xhr.onprogress = (event) => {
service.progressEventObservable.next(event);
};
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
See this question for more details:
Angular2 / RxJS - updating variable after getting data from Http observable
When you make http cals in angular2, it returns an Observable of type Response, this response is created inside class called XHRConnection where all the magic happens.
The XHRConnection builds the response by listening to XMLHttpRequest's load event, this means it will return one response at the end of the request.
Now to be able to alter this behavior we need to make our connection class listen to the progress event.
So we need to create custom Connection class, to handle the response as we see fit.
I did it this way,
Take note that my php API returns multi response in a single request and this responses are plain strings.
my_backend.ts
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Observer} from "rxjs/Observer";
import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
import {ResponseOptions} from "angular2/src/http/base_response_options";
import {Request} from "angular2/src/http/static_request";
import {Response} from "angular2/src/http/static_response";
import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
import {Headers} from 'angular2/src/http/headers';
import {isPresent} from 'angular2/src/facade/lang';
import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
export class MyConnection implements Connection {
readyState: ReadyState;
request: Request;
response: Observable<Response>;
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
// save the responses in array
var buffer :string[] = [];
// load event handler
let onLoad = () => {
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
//_xhr.respons 1 = "Loading data!"
//_xhr.respons 2 = "Loading data!Ready To Receive Orders."
// we need to fix this proble
// check if the current response text contains the previous then subtract
// NOTE: I think there is better approach to solve this problem.
buffer.push(body);
if(buffer.length>1){
body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
}
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
let state:number = _xhr.readyState;
if (status === 0) {
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({ body, status, headers, url });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
let response = new Response(responseOptions);
//check for the state if not 4 then don't complete the observer
if(state !== 4){
//this will return stream of responses
responseObserver.next(response);
return;
}
else{
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err: any) => {
var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
};
if (isPresent(req.headers)) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
_xhr.addEventListener('progress', onLoad);
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.send(this.request.text());
return () => {
_xhr.removeEventListener('progress', onLoad);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
};
});
}
}
#Injectable()
export class MyBackend implements ConnectionBackend {
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
createConnection(request: Request):MyConnection {
return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
}
}
And in the app.component.ts
import {Component, provide} from 'angular2/core';
import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';
import {MyBackend} from './my_backend';
#Component({
selector: 'my-app',
providers: [
HTTP_PROVIDERS,
MyBackend,
provide(XHRBackend, {useExisting:MyBackend})
]
.
.
.
Now calling http.get will return a steam of responses.
#Bartek Chichoki's answer is correct but it was not working for my case.
Adding observe: 'events' did the trick for me
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
observe: 'events'
});
Hope it helps
I strongly recomend using this
https://www.npmjs.com/package/angular-progress-http
otherwise messing around with xhr will make you miss sessions cookies and other stuffs
besides it'll be more portable and way easier to implement