I am trying to send any errors, exceptions that Angular is catching to my server. I made my own class called GlobalErrorHandler that is extending ErrorHandler. Please check below
import { ErrorHandler, Injectable, Injector } from "#angular/core";
import {HttpHeaders, HttpClient} from "#angular/common/http";
import { TestServiceService } from "../_services/test-service.service";
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(
private injector: Injector,
private http: HttpClient,
private service: TestServiceService,
) {}
url = 'http://127.0.0.1:4000/post';
handleError(error) {
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
try {
let msg = JSON.parse(error)
console.log('>>>>>>>>> message is ', msg)
this.http.post(this.url, msg, httpOptions);
}
catch (e) {
console.log('>>>>>>>>> err in catch is ', e)
}
}
}
I am able to console.error(error) whenever an error occurs, but I cannot make a post request to my server.
What am I missing in my code to make post request from ErrorHandler?
After changing the code to the following (replacing JSON.parse with JSON.stringify and catching the post errors successfully):
handleError(error) {
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
let subscription = this.http.post(this.url, JSON.stringify(error), httpOptions).subscribe(
(data => {console.log('>>>>>>> data is ', data));subscription.unsubscribe();},
error => {console.log('>>>>>>>> err', error);subscription.unsubscribe();}
)
}
The error was discovered to be on the serverside, but the code above should be useful to anyone trying to trasmit clientside errors(in Angular2+) to the server provided that the server has been implemented correctly.
Related
I am working on an app in Angular 14 that requires authentication/authorization, reason for witch I use Keycloak Angular
.
I need to get the currently logged in user's data from the application.
For this purpose, I have a service:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from '../../../models/user';
#Injectable({
providedIn: 'root'
})
export class UserFormService {
httpOptions: object = {
headers: new HttpHeaders({
'Content-Type' : 'application/json'
})
}
apiURL: string = 'http://localhost:8080';
constructor(private http: HttpClient) { }
public currentUserEmail: any;
public currentUserData: any;
public async getUserEmail(){
let currentUser = await this.keycloakService.loadUserProfile();
this.currentUserEmail = currentUser.email;
}
public getUserByEmail(email: string): Observable<User>{
return this.http.get<User>(`${this.apiURL}/getUserByEmail/${email}`, this.httpOptions);
}
}
I use it in a component:
public getUserByEmail() {
this.supplierFormService.getUserByEmail(this.currentUserEmail).subscribe(response => {
this.currentUser = response;
console.log('currentUser: ', response);
});
}
In keycloak.init.ts I have:
import { KeycloakService } from 'keycloak-angular';
export function initializeKeycloak(keycloak: KeycloakService) {
return () =>
keycloak.init({
config: {
url: 'http://localhost:8085',
realm: 'MyRealm',
clientId: 'my-app'
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
ngOnInit(): void {
// Get user's email
this.getUserEmail();
// Get user's data by email
this.getUserByEmail();
}
The problem
Instad of returning the user's data, the service throws a 500 (Internal Server Error) and the email is undefined, as can be seen below:
http://localhost:8080/getUserByEmail?email=undefined
How do I fix this problem?
You should sync those two calls, the getUserByEmail may be excecuted faster then currentUserEmail is set:
async ngOnInit(): void {
// Get user's email
await this.getUserEmail();
// Get user's data by email
this.getUserByEmail();
}
decode jwt token returned from keycloak. It contains current user data and Id
Then get user by this id
I am completely new to Angular and I've created a project using SpringBoot 2.0.5.RELEASE, Angular 5 and spring data to build an end to end single page java web application. I use spring boot 1.5 to expose REST APIs and angular5 with routing to build the client that will consume the APIs exposed by the server.
I've defined this component:
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { User } from '../models/user.model';
import { UserService } from './user.service';
#Component({
templateUrl: './add-user.component.html'
})
export class AddUserComponent {
user: User = new User();
constructor(private router: Router, private userService: UserService) {
}
createUser(): void {
alert ('lala');
this.userService.createUser(this.user)
.subscribe( data => {
alert('User created successfully.');
});
}
}
in the page I can see the alert lala, but not 'User created successfully.' but I have no idea why
The link address when I create a user is this is this one http://localhost:4200/api/users
This is my proxy.config.json file:
{
"/api/*": {
"target": "http://localhost:8080/user-portal",
"secure": false
}
}
and from curl is fine :
curl -X POST -H "Content-Type: application/json" "http://localhost:8080/user-portal/api/users"
and user.service.ts:
import {Injectable} from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from '../models/user.model';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable()
export class UserService {
constructor(private http: HttpClient) {}
private userUrl = '/api/users';
public getUsers() {
return this.http.get<User[]>(this.userUrl);
}
public deleteUser(user) {
return this.http.delete(this.userUrl + '/'+ user.id);
}
public createUser(user) {
return this.http.post<User>(this.userUrl, user);
}
}
Firstly, best not to use alert. Use console.log. Secondly, you are only handling success, you are not handling failure. Do this:
createUser(): void {
console.log('lala');
this.userService.createUser(this.user)
.subscribe(data => {
console.log('User created successfully', data);
},
err => {
console.log('There was an error', err);
},
() => {
console.log('I have completed now and nothing will ever be emitted from this Observable again');
});
}
The error handler will be executed if the HTTP response is not a success response, viz if the status code of the response is not in the 2xx range.
Check your browser network tab also to see if the HTTP request is failing.
You prob also want to debug this:
public createUser(user) {
console.log('userUrl', this.userUrl)
console.log('user', user)
return this.http.post<User>(this.userUrl, user);
}
To make sure all is as expected.
In Chrome hit F12 to open the dev tools and go to the network tab. Make sure that a request is being made to the end point and that it is not throwing and error.
I'm coding the angular 5 app. There is refreshAccessToken in authentication service
refreshAccessToken(): Observable<ICredentials> {
const refreshTokenUrl = this.urlsService.getUrl(Urls.TOKEN);
const httpParams = new HttpParams()
.append('grant_type', 'refresh_token')
.append('refresh_token', this.credentials.refresh_token)
.append('client_id', Constants.CLIENT_ID)
.toString();
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post(refreshTokenUrl, httpParams, { headers })
.map((response: any) => {
this.setCredentials(response);
localStorage.setItem(credentialsKey, JSON.stringify(this.getCredentials()));
return response;
});
}
I want to implement next alghorithm:
Any http request failed because of unauthorized with status 401
Try to get new access token from server
Repeat the request
At the time while getting new access token, new http requests can be created, in this case I want to store them and repeat after new access token was recieved. To reach this purpose I've written the interceptor.
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { AuthenticationService } from '#app/core/authentication/authentication.service';
import { Urls, UrlsService } from '#app/shared/urls';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class UnauthorizedRequestInterceptor implements HttpInterceptor {
newAccessToken$: Observable<ICredentials> = null;
constructor(
public authService: AuthenticationService,
private router: Router,
private urlsService: UrlsService) {
}
addAuthHeader(request: HttpRequest<any>) {
if (this.authService.getCredentials()) {
return request.clone({
setHeaders: {
'Authorization': 'Bearer ' + this.authService.getCredentials().access_token
}
});
}
return request;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = this.addAuthHeader(request);
return next.handle(request).catch((error: HttpErrorResponse) => {
let handleRequests$ = null;
if (this.isNeedNewAccessToken(error, request)) {
handleRequests$ = this.handleRequestWithNewAccessToken(request, next);
}
return handleRequests$ ||
(this.isUnathorizedError(error)
? Observable.empty()
: Observable.throw(error));
});
}
logout() {
this.authService.logout();
this.router.navigate(['login']);
}
private isNeedNewAccessToken(error: HttpErrorResponse, request: HttpRequest<any>): boolean {
return this.isUnathorizedError(error)
&& this.authService.isAuthenticated()
&& this.isSignInRequest(request);
}
private getNewAccessToken(): Observable<ICredentials> {
if (!this.newAccessToken$) {
this.newAccessToken$ = this.authService.refreshAccessToken();
}
return this.newAccessToken$;
}
private isUnathorizedError(error: HttpErrorResponse) {
return error.status === 401;
}
private handleRequestWithNewAccessToken(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
return this.getNewAccessToken()
.mergeMap(() => {
request = this.addAuthHeader(request);
return next.handle(request);
})
.catch((err: HttpErrorResponse) => {
if (err.error.error === 'invalid_grant') {
this.logout();
}
return Observable.empty();
});
}
private isNotSignInRequest(request: HttpRequest<any>): boolean {
return request.url !== this.urlsService.getUrl(Urls.TOKEN);
}
}
The behaviour of this interceptor is really strange. On each mergeMap on the handleRequestWithNewAccessTokenthe angular starts new post httpRequest. I've expected that the observable returned from refreshAccessToken(function from authenticationService, code at the top) would be resolved only once. I don't understand why it is fired for each merge map? I expected the next:
I have observable - http request for token
I use mergeMap - when http request finished, all callbacks that was added with mergeMap will be executed.
I was think to store requests that I need to handle in the global variable and invoke them in the subscribe() to http request, but there is problem, that each request should be resolved in the initial stream inside interceptor. I can't do smth like this: .subscribe(token => this.httpClient.request(storedRequest) because this will create new request, so all actions should be happened inside the observable chain.
Can you please help me to find solution?
PS This solution is working, but I want to get rid off unnecessary TOKEN requests, f.e. if page need to make 5 requests and token have expired - interceptor will make 5 requests for token.
I think your code is good and all you have to do is share the request for the new token.
refreshAccessToken(): Observable<ICredentials> {
const refreshTokenUrl = this.urlsService.getUrl(Urls.TOKEN);
const httpParams = new HttpParams()
.append('grant_type', 'refresh_token')
.append('refresh_token', this.credentials.refresh_token)
.append('client_id', Constants.CLIENT_ID)
.toString();
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post(refreshTokenUrl, httpParams, { headers })
.map((response: any) => {
this.setCredentials(response);
localStorage.setItem(credentialsKey, JSON.stringify(this.getCredentials()));
return response;
})
.share(); // <- HERE
}
Note share operator at the end of return
EDIT:
I also think you don't ever set back this.newAccessToken$ to null. Maybe consider adding set to null to finally like this:
private getNewAccessToken(): Observable<ICredentials> {
if (!this.newAccessToken$) {
this.newAccessToken$ = this.authService.refreshAccessToken()
.finally(() => {
this.newAccessToken$ = null;
});
}
return this.newAccessToken$;
}
Is it possible to make a redirect from a Nest controller without the usage of the #Response object?
For now I know that we can only do this via direct #Response object injection into the route handler.
You can write a RedirectInterceptor:
#Injectable()
export class RedirectInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
const response = context.switchToHttp().getResponse();
response.redirect('redirect-target');
return stream$;
}
}
Then use it in your controller like this:
#Get('user')
#UseInterceptors(RedirectInterceptor)
getUser() {
// will be redirected.
}
It is important not to return anything from your controller, otherwise you will get the following error: Can't set headers after they are sent.
If needed the RedirectInterceptor can be dynamic as well:
#Injectable()
export class RedirectInterceptor implements NestInterceptor {
constructor(private readonly target: string) {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
const response = context.switchToHttp().getResponse();
response.redirect(this.target);
^^^^^^^^^^^
return stream$;
}
}
and then in the controller:
#UseInterceptors(new RedirectInterceptor('redirect-target'))
(a bit of a different implementation to another answer here...)
I created a RedirectError which can be thrown more dynamically than a decorator
import { ExceptionFilter, Catch, ArgumentsHost } from '#nestjs/common';
import { Response } from 'express';
export class RedirectError extends Error {
constructor(public readonly status: number, public readonly url: string) {
super();
}
}
#Catch(RedirectError)
export class RedirectFilter implements ExceptionFilter {
public catch(exception: RedirectError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
return response.redirect(exception.status, exception.url);
}
}
and then in main.ts set it:
app.useGlobalFilters(new RedirectFilter());
and finally to use it:
throw new RedirectError(302, '/some-target');
I've done it more complex, but I think it is good enough.
Create a class such as util/RedirectException like this:
The code like this:
import { HttpException, HttpStatus } from '#nestjs/common';
export class RedirectException extends HttpException {
constructor(message?: string | object) {
super(message, HttpStatus.CONTINUE);
}
}
Create a RedirectFilter by: nest g f RedirectFilter
Write the code like this:
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '#nestjs/common';
import { RedirectException } from './util/redirect-exception';
#Catch()
export class RedirectFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const res = host.switchToHttp().getResponse(),
req = host.switchToHttp().getRequest();
try {
if (exception instanceof RedirectException) {
Object.keys(exception.message).forEach(k => {
req.session[k] = exception.message[k];
});
req.session.save(() => {
res.redirect(exception.message.url);
});
return;
}
if (exception instanceof HttpException) {
return res.status(exception.status).json(exception.message)
}
res.status(500).json({status: 500, message: 'Internal Server error'})
} catch (e) {
return res.status(500)
.json({
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: e.message
});
}
}
}
This class help you handle all the response when an exception is throw. And yes, this include the Redirect exception. Now we can throw the exception with exactly params, and it work!
Use the filter in main.ts: app.useGlobalFilters(new RedirectFilter());
And in controller, if you want to redirect to an url, just do this any time you want
Code:
throw new RedirectException({
url: 'the url you want to redirect',
field1: 'The first field you want to pass to session'
field2: 'The second field you want to pass to session'
});
Don't forget setup express-session if you want to pass data by session when redirect: https://www.npmjs.com/package/express-session.
If you don't want to use this, just replace the code inside if (exception instanceof RedirectException) {} to: res.redirect(exception.message.url);. It don't check and setup the session anymore.
I have an angled application where I am trying to perform an insert methods on a backend. I need that after the response of the backend display an alert like for example the one of SweetAlert. But I do not know any components that do this. SweetAlert works at the click of a button, not the response of a backend.
For exemple:
My real question is:
How do I enable SweetAlert in typescript
Can anybody help me?
Make a http request and subscribe it in you desired component and on success of http request, as you get response- show alert.
service.ts
import { Injectable } from '#angular/core';
import { HttpClient, Response, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
export class CcDataServiceService {
constructor(private http: HttpClient) { <-- httpclient for angular6
}
getData(): Observable<any[]> {
return this.http.get('https://')
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
const body = res.json();
return body || [];
}
private handleError(error: any) {
const errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
console.log('Server Error!');
return Observable.throw(errMsg);
}
}
component.ts
import swal from 'sweetalert'
constructor(public jsonDataService: CcDataServiceService) {
}
ngOnInit() {
let thisx = this;
this.jsonDataService.getData().subscribe(
function (success) {
// alert here on success
swal("Hello world!");
},
error => console.log('Getting Server Data Error :: ' +
JSON.stringify(error)));
}
Link:- https://stackblitz.com/edit/angular6-7scyt7?file=app/app.component.ts