I have a hard time passing the right angular request to the header. This is my service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpHeaders }
from '#angular/common/http';
import { Utente } from '../model/Utente ';
import { Prodotto } from '../model/Prodotto ';
import { OktaAuthService } from '#okta/okta-angular';
import { Observable, from } from 'rxjs';
import { Carrello } from '../model/Carrello ';
import { userInfo } from 'node:os';
import { getLocaleCurrencyCode } from '#angular/common';
const headers = new HttpHeaders().set('Accept', 'application/json');
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
constructor(
private httpClient:HttpClient, private oktaAuth:OktaAuthService ) {}
getCarr(){
return this.httpClient.get<Carrello[]>('http://localhost:8080/prodotti/utente/vedicarrelloo', {headers} );
}
}
This is my spring method:
#Transactional(readOnly = true)
public List<Carrello> getCarrello(#AuthenticationPrincipal OidcUser utente){
Utente u= utenteRepository.findByEmail(utente.getEmail());
return carrelloRepository.findByUtente(u);
}
In console I get this error (error 500):
https://i.stack.imgur.com/BiONS.png
this error corresponds in my console to "java.lang.NullPointerException: null.
But if I access localhost: 8080, I can see the answer correctly, so I assume there is a problem in passing the request header in angular, can anyone tell me where am I wrong, please? I specify that I get this error only in the methods where the OidcUser is present, the rest works perfectly. Thank you!
You need to send an access token with your request. Like this:
import { Component, OnInit } from '#angular/core';
import { OktaAuthService } from '#okta/okta-angular';
import { HttpClient } from '#angular/common/http';
import sampleConfig from '../app.config';
interface Message {
date: string;
text: string;
}
#Component({
selector: 'app-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
failed: Boolean;
messages: Array<Message> [];
constructor(public oktaAuth: OktaAuthService, private http: HttpClient) {
this.messages = [];
}
async ngOnInit() {
const accessToken = await this.oktaAuth.getAccessToken();
this.http.get(sampleConfig.resourceServer.messagesUrl, {
headers: {
Authorization: 'Bearer ' + accessToken,
}
}).subscribe((data: any) => {
let index = 1;
const messages = data.messages.map((message) => {
const date = new Date(message.date);
const day = date.toLocaleDateString();
const time = date.toLocaleTimeString();
return {
date: `${day} ${time}`,
text: message.text,
index: index++
};
});
[].push.apply(this.messages, messages);
}, (err) => {
console.error(err);
this.failed = true;
});
}
}
On the Spring side, if you want it to accept a JWT, you'll need to change to use Jwt instead of OidcUser. Example here.
#GetMapping("/")
public String index(#AuthenticationPrincipal Jwt jwt) {
return String.format("Hello, %s!", jwt.getSubject());
}
Related
I've tried to implement an oauth method using GraphQL with Google auth and for some reason I'm getting the following error
"res.setHeader is not a function" from within the authenticate method in Google Strategy
I've used passport-google-oauth20 strategy
this is my google-auth.guard.ts
import { ExecutionContext, Injectable } from '#nestjs/common';
import { GqlExecutionContext } from '#nestjs/graphql';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
const { token } = ctx.getArgs();
gqlReq.body = { token };
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
this is my google.strategy.ts
import { PassportStrategy } from '#nestjs/passport';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { Profile } from 'passport';
#Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: process.env.GOOGLE_REDIRECT_URL,
prompt: 'consent',
scope: ['email', 'profile'],
})
}
async validate(
accessToken: string,
refreshToken: string,
profile: Profile,
done: VerifyCallback,
): Promise<any> {
if (!profile) {
return done(new UnauthorizedException(), false);
}
return done(null, profile);
}
}
it's important to point out that since my app is a react SPA, the callbackURL value is the main page of the client and not another path in the server.
and the resolver which I intend to use to generate a jwt token and a refresh token, but the code never gets to this part due to the error in the strategy
#UseGuards(GoogleAuthGuard)
#Query(() => LoginResponseApi)
async googleLogin(
#Args({ name: 'token', type: () => String }) token: string,
#Req() req,
#Context() context
): Promise<LoginResponseApi> {
const res: Response = context.req.res;
const loginResponse: any = await this.authService.googleLogin(req)
const jwtToken = this.authService.createRefreshToken(loginResponse.user)
if (loginResponse.accessToken)
this.authService.sendRefreshToken(res, jwtToken)
return loginResponse;
}
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { Post } from 'src/app/sign-in/post';
#Injectable({
providedIn: 'root'
})
export class NewUserService {
constructor(private router:Router,private http: HttpClient) { }
async signIn(email:any,password:any){
const userDate = {
email:email,
password:password
}
console.log("Before request");
await this.http.post<{
Status: string;
StatusDetails: string;
token: string;
}>("http://localhost:5000/user/signin", userDate)
.subscribe(respond => {
console.log("respond");
if (respond.token) {
//sign susess
}else {
//fail
}
});
console.log("After request")
}
}
This is my servise.ts class. In hear I try to make a post request. Before completing the request other code execute. I expect output like
Before request
respond
After request
But the actual output is
Before request
After request
respond
How can I execute code like
Before request
respond
After request
I've followed the tutorial from https://tutorialedge.net/typescript/angular/angular-websockets-tutorial/
as I'm trying to setup a websocket connection but I'm getting an error after the socket gets connected. The error says "Error during WebSocket handshake: Unexpected response code: 502"
I've followed the tutorial and followed the steps but i still get the error after the socket gets connected.
websocket.service.ts
import { Injectable } from '#angular/core';
import * as Rx from 'rxjs/Rx';
#Injectable({
providedIn: 'root'
})
export class WebsocketService {
constructor() { }
private subject: Rx.Subject<MessageEvent>;
public connect(url): Rx.Subject<MessageEvent> {
if (!this.subject) {
this.subject =
this.create('ws://61c725fa.ngrok.io/auth/forgot_password/');
console.log("Successfully connected: " + url);
}
return this.subject;
}
private create(url): Rx.Subject<MessageEvent> {
let ws = new WebSocket(url);
let observable = Rx.Observable.create((obs:
Rx.Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
});
let observer = {
next: (data: Object) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
}
};
return Rx.Subject.create(observer, observable);
}
}
notifier.service.ts
import { Injectable } from '#angular/core';
import { Observable, Subject } from "rxjs-compat/Rx";
import { WebsocketService } from "./websocket.service";
import { map } from 'rxjs/operators';
let ws_scheme = window.location.protocol == "https:" ? "wss" :
"ws";
let ws_path = ws_scheme + '://' +"61c725fa.ngrok.io" +
"/auth/forgot_password/";
const URL = ws_path;
export interface Message {
username: string;
}
#Injectable({
providedIn: 'root'
})
export class NotifierService {
public messages: Subject<Message>;
constructor(wsService: WebsocketService) {
this.messages = <Subject<Message>>wsService
.connect(URL).pipe(map((response: MessageEvent): Message => {
let data = JSON.parse(response.data);
return {
username: data.message
}
}));
}
}
component.ts
import { Component } from '#angular/core';
import { NotifierService } from './notifier.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'ang-socket';
constructor(private notifier:NotifierService){
notifier.messages.subscribe(msg=>{
console.log("response from websocket:" + msg);
})
}
private message = {
username: "abc#gmail.com"
};
sendMsg() {
console.log("new message from client to websocket: ",
this.message);
this.notifier.messages.next(this.message);
this.message.username = "";
}
}
I would like to fix this issue. Great if someone helps.
Im new to the MEAN stack and having trouble with getting data changes to be refreshed in the UI. I know the data is getting saved properly in MongoDB, and also retrieved, because when I create a Todo item and I refresh the page, the newly added Todo item appears in the Todo List. The problem is that it isnt happening dynamically.
I've tried a number of different things including NgZone and ChangeDetectorRef to detect changes, not sure what I'm doing wrong..
Let me know if any more info is needed.. thank you!
The Todo List component:
import { Component, Input, OnInit, NgZone } from '#angular/core';
import { Todo } from '../todo.model';
import { TodoService } from '../../todo.service';
#Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.scss'],
providers: [TodoService]
})
export class TodoListComponent implements OnInit {
#Input() todos: Todo[] = [];
constructor(private _todoService: TodoService, private zone: NgZone){}
getTodos() {
console.log('todo list - get todos');
this._todoService.getTodos()
.subscribe(resTodoData => {
this.zone.run(() => {
this.todos = resTodoData;
});
});
}
ngOnInit() {
console.log('todo list - init');
this.getTodos();
}
}
Service Component:
import { Injectable, NgZone } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from
'#angular/http';
import { map } from 'rxjs/operators';
import { Todo } from './todos/todo.model';
#Injectable({
providedIn: 'root'
})
export class TodoService {
// these were configured in express server
private _getUrl = "/api/todos";
private _postUrl = "/api/todo";
constructor(private _http: Http, private zone: NgZone) { }
getTodos() {
let json = this._http.get(this._getUrl)
.pipe(map((response: Response) => response.json()));
return json;
}
addTodo(todo: Todo) {
let headers = new Headers({ 'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers });
return this._http.post(this._postUrl, JSON.stringify(todo),
options)
.pipe(map((response: Response) => response.json()));
}
}
I want to implement file download using this Angular 6 code:
Rest API:
private static final Logger LOG = LoggerFactory.getLogger(DownloadsController.class);
private static final String EXTERNAL_FILE_PATH = "/Users/test/Documents/blacklist_api.pdf";
#GetMapping("export")
public ResponseEntity<FileInputStream> export() throws IOException {
File pdfFile = Paths.get(EXTERNAL_FILE_PATH).toFile();
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(pdfFile.length())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new FileInputStream(pdfFile));
}
Service:
import {Injectable} from '#angular/core';
import {HttpClient, HttpParams} from "#angular/common/http";
import {Observable} from "rxjs/index";
import {environment} from "../../../environments/environment";
import {HttpUtils} from "../common/http-utils";
import { map } from 'rxjs/operators';
import {Http, ResponseContentType} from '#angular/http';
#Injectable({
providedIn: 'root'
})
export class DownloadService {
constructor(private http: HttpClient) {
}
downloadPDF(): any {
return this.http.get(environment.api.urls.downloads.getPdf, {
responseType: 'blob'
})
.pipe(
map((res: any) => {
return new Blob([res.blob()], {
type: 'application/pdf'
})
})
);
}
}
Component:
import {Component, OnInit} from '#angular/core';
import {DownloadService} from "../service/download.service";
import {ActivatedRoute, Router} from "#angular/router";
import {flatMap} from "rxjs/internal/operators";
import {of} from "rxjs/index";
import { map } from 'rxjs/operators';
#Component({
selector: 'app-download',
templateUrl: './download.component.html',
styleUrls: ['./download.component.scss']
})
export class DownloadComponent implements OnInit {
constructor(private downloadService: DownloadService,
private router: Router,
private route: ActivatedRoute) {
}
ngOnInit() {
}
export() {
this.downloadService.downloadPDF().subscribe(res => {
const fileURL = URL.createObjectURL(res);
window.open(fileURL, '_blank');
});
}
}
The file is present in the directory but when I try to download it I get error:
18:35:25,032 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-2) Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
Do you know how I can fix this issue?
Do I need to add additional configuration in order to download the file via Angular web UI?
I use spring-boot-starter-parent version 2.1.0.RELEASE
FileSaver npmjs.com/package/ngx-filesaver is the best library for file download in Angular6 but it has various issues in ios devices. We fixed it by writing own methods and conditionally handling it .
Component
download() {
this.downloadService.downloadPDF().subscribe(async (res: Blob) => {
if (this.isIOSMobileDevice) {
const file = new File([res], fileName, { type: 'application/pdf' });
const dataStringURL: any = await this.fileService.readFile(file);
this.hrefLink = this.sanitizer.bypassSecurityTrustUrl(dataStringURL);
} else {
saveFile(res, fileName);
}
});
}
export const saveFile = (blobContent: Blob, fileName) => {
const isIOS = (!!navigator.platform.match(/iPhone|iPod|iPad/)) || (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2);
const blob = new Blob([blobContent], { type: 'application/pdf' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
document.body.appendChild(link);
link.href = url;
link.target = '_self';
link.download = fileName;
link.click();
document.body.removeChild(link);
}
};
File Service
async readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
HTML code
<a *ngIf="isIOSMobileDevice" [href]="hrefLink"
target="_blank">Download</a>
<a *ngIf="!isIOSMobileDevice" href="javascript:;" (click)="download"
target="_blank">Download</a>
For ios Mobile devices ,download method has to be called in prerequisite so that we get hrefLink.