Navigate to another route from a service - nativescript

I have service that intercepts all my Http requests so I can check if the user's token is valid or not. When a Http response is 401, I want the current user to be logged out of the application:
import { Http, ConnectionBackend, RequestOptions, RequestOptionsArgs, Request, Response } from '#angular/http'
import { Router } from '#angular/router'
import { Observable } from 'rxjs/Observable'
import { Injectable } from '#angular/core'
import { Config } from './shared/config'
import { RouterExtensions } from 'nativescript-angular/router'
import 'rxjs/add/observable/throw'
#Injectable()
export class RequestInterceptorService extends Http {
private config: Config = new Config()
constructor(
backend: ConnectionBackend,
defaultOptions: RequestOptions,
private router: Router,
private routerExtensions: RouterExtensions,
) {
super(backend, defaultOptions)
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return this.intercept(super.request(url, options))
}
intercept(observable: Observable<Response>): Observable<Response> {
return observable.catch((err, source) => {
if (err.status === 401) {
this.logout()
return Observable.empty()
} else {
return Observable.throw(err)
}
})
}
logout() {
this.config.clear()
this.routerExtensions.navigate(["/signin"], {
clearHistory: true
})
}
}
My issue is that router or routerExtensions are always undefined, so I can't really redirect the user to any other path at all using this service.

So, I found the solution. It was just a mater of understanding all of what I was doing.
I created a RequestInterceptor service that extends Http module. It includes a interceptor that checks if the status code is equal to 401. In order to make it compatible with my project, I decided to provide it as custom implementation of Http. The following code on my #NgModule does just that:
providers: [
{
provide: Http,
useClass: RequestInterceptorService
}
]
But the thing RequestInterceptorService does not have access to Http dependencies: ConnectionBackend and RequestOptions, so I had to inject them using the deps property, which is an array where I can identify an array of dependencies that will get injected. In my case, I need to inject not only the dependencies that Http requires (XHRBackend, RequestOptions), but also the one that my service will use: RouterExtensions.
My providers declaration looks like this:
providers: [
{
provide: Http,
useClass: RequestInterceptorService,
deps: [XHRBackend, RequestOptions, RouterExtensions]
}
]
After doing this, I was able to successfully redirect a user to the login page when a 401 status code is returned from any request.

Related

nest js websocket connection is not working with angular 11

Created a nest js websocket and trying to connect to that from angular app version 11. Not able to connect to socket from angular 11. I am using latest version of socket.io-client.
In websocket server log says connect and disconnects.
nest js websocket file:
import { Logger } from '#nestjs/common';
import { OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WsResponse } from '#nestjs/websockets';
import { Socket,Server } from 'socket.io';
import { EventPattern } from '#nestjs/microservices';
#WebSocketGateway(3001)
export class AppGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect{
private Logger = new Logger('AppGateway');
afterInit(server: Server) {
this.Logger.log("App Gateway Initialized");
}
handleConnection(client: Socket, ...args: any[]){
this.Logger.log(`New client connected...: ${client.id}`);
client.emit('connected', 'Successfully connected to the server.');
}
handleDisconnect(client: Socket) {
this.Logger.log(`Client disconnected: ${client.id}`);
}
#SubscribeMessage('msgToServer')
handleMessage(client:Socket, text:string):WsResponse<string> {
this.Logger.log(`got new event`);
return {event: 'msgToClient', 'data': text};
}
}
angular websocket service file:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { io } from 'socket.io-client';
#Injectable({
providedIn: 'root'
})
export class WebSocketService {
socket: any;
constructor() {
this.socket = io('http://localhost:3001');
debugger;
this.socket.on('connected', function() {
console.log("connected !");
});
}
listen(eventName: string) {
return new Observable((subscriber) => {
this.socket.on(eventName, (data) => {
subscriber.next(data);
})
});
}
emit(eventName: string, data:any) {
this.socket.emit(eventName, data);
}
}
angular app.component.ts file:
import { Component, OnInit } from '#angular/core';
import { WebSocketService } from './web-socket.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit{
title = 'dashboard';
constructor(
private webSocketService: WebSocketService
) {}
ngOnInit() {
/*this.webSocketService.listen('msgToClient').subscribe((data) => {
console.log(data);
})*/
this.webSocketService.listen('connected').subscribe((data) => {
console.log(data);
})
}
}
I am using latest version of socket.io-client.
I believe you're using socketIO client v4. (If you're working with v3, the following would still be true)
Based on NestJS Websocket documentation, the NestJS socketIO server is still in v2.
#nestjs/platform-socket.io currently depends on socket.io v2.3 and socket.io v3.0 client and server are not backward compatible. However, you can still implement a custom adapter to use socket.io v3.0. Please refer to this issue for further information.
If you check the version compatibility, you will see that socketIO server v2 is not compatible with socketIO client v4.
However, socketIO server v3 is compatible with socketIO client v4. So I believe you can take a look into this issue (as mentioned in NestJS docs) and try to convert your NestJS socketIO server to support socketIO client v3. Hopefully, that would also support socketIO client v4 as well. (I didn't test this though!)
Hope this helps you. Cheers 🍻 !!!

WebSockets on specific route in Nest.js

I'd like to create specific API route which will be used only WebSocket (/api/events) but in all examples of implementing WebSockets on Nest.js I stumbled upon module is imported in AppModule and client is emitting events toward the root URL, which I can't do because I have this middleware;
frontend.middleware.ts
import { Request, Response } from 'express';
import { AppModule } from '../../app.module';
export function FrontendMiddleware(
req: Request,
res: Response,
next: Function,
) {
const { baseUrl } = req;
if (baseUrl.indexOf('/api') === 0) {
next();
} else {
res.sendFile('index.html', { root: AppModule.getStaticAssetsRootPath() });
}
}
Here is the EventGateway and EventModule:
event.gateway.ts
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
} from '#nestjs/websockets';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Client, Server } from 'socket.io';
#WebSocketGateway({ namespace: 'events' })
export class EventGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('events')
findAll(client: Client, data: any): Observable<WsResponse<number>> {
return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
}
#SubscribeMessage('identity')
async identity(client: Client, data: number): Promise<number> {
return data;
}
}
event.module.ts
import { Module } from '#nestjs/common';
import { EventGateway } from './event.gateway';
#Module({
components: [EventGateway],
})
export class EventModule {}
Is there a way to create controller which will allow server-client communication via /api/events?
Yes, it is possible to create the WebsocketGateway on another path. You can just use the options of the WebsocketGateway to configure the underlying IO-Connection:
E.g:
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
WsResponse,
} from '#nestjs/websockets';
import { from, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Client, Server } from 'socket.io';
#WebSocketGateway({ path: '/api/events', namespace: 'events' })
export class EventGateway {
#WebSocketServer()
server: Server;
#SubscribeMessage('events')
findAll(client: Client, data: any): Observable<WsResponse<number>> {
return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
}
#SubscribeMessage('identity')
async identity(client: Client, data: number): Promise<number> {
return data;
}
}
This will start the IO-Connection on http://localhost/api/events
Remember to change the connection-path also in your client. It won't be the default /socket.io path anymore, it will be /api/events in your sample.
Websocket is running on the server, not an endpoint. Therefore you cannot have it listen to requests under a specific route, rather just a port, which for Nest's default configuration happens to be the same as the HTTP one.
You could use a reverse proxy like Nginx to redirect the requests towards /api/events facing the Websocket server and also handle the redirection to index.html without changing even the Websocket server's port. Then you would not need the FrontendMiddleware class at all. It is also better since the application does not take the burden of managing request redirections.

Call REST POST API with Angular 5 and HTTPClient

I'm creating frontend for my backend using Angular and having troubles calling POST API using HTTPClient. Below is my code:
article.service.ts
#Injectable()
export class ArticleService {
url = "//localhost:8080/deleteArticle";
constructor(private http: HttpClient) { }
deleteArticle(article: Article): Observable<HttpResponse<Article>> {
return this.http.post<Article>(this.url, article,
{
observe: 'response'
}
);
}
}
article.component.ts
#Component({
selector: 'app-article',
templateUrl: './article.component.html'
})
export class AcrticleComponent implements OnInit {
articleForm: FormGroup;
constructor(private formBuilder:FormBuilder, private articleService: ArticleService) {
}
ngOnInit() {
this.articleForm = this.formBuilder.group({
title: ['', [ Validators.required ] ]
});
}
onFormSubmit() {
let article = this.articleForm.value;
this.deleteArticle(article);
this.articleForm.reset();
}
deleteArticle(article: Article) {
this.articleService.deleteArticle(article).subscribe(
article => {
console.log(article);
},
err => {
console.log(err);
}
);
}
get title() {
return this.articleForm.get('title');
}
}
Spring Controller:
#PostMapping("/deleteArticle")
#CrossOrigin(origins = "http://localhost:4200")
public String deleteArticle(#RequestParam(value = "id") String id) {
deleteService.deleteArticle(id);
}
After entering the title and hitting submit, it returns this error (status 404):
{error: "Collection 'localhost:8080' not found"}
Can you show me what I did wrong and how my angular frontend couldn't find my backend endpoint?
The url needs to be complete.
include the http:
But I would suggest using the webpack dev server proxy.
If you put all your apis under the /api/ url then you can proxy all calls to /api/* back to your spring backend.
then when you launch your project you do the same there and proxy in /api/* using nginx or similar.
You can read more about how to proxy using angular-cli here https://github.com/angular/angular-cli/wiki/stories-proxy
Make sure you are spring application is running on the same port as 8080 and add http before your url. I hope this not your problem,but try like this..
#Injectable()
export class ArticleService {
url = "http://localhost:4200/deleteArticle";//add http here
constructor(private http: HttpClient) { }
deleteAccount(article: Article): Observable<HttpResponse<Article>> {
return this.http.post<Article>(this.url, article,
{
observe: 'response'
}
);
}
}
EDIT
Add a class Like Article and get the Id which you are sending from Article class of Angular Service
#PostMapping("/deleteArticle")
#CrossOrigin(origins = "http://localhost:4200")
public String deleteArticle(#RequestParam() Article article,
RedirectAttributes redirectAttributes) {
deleteService.deleteArticle(article.id, redirectAttributes);
}

How to catch errors with Ngxs Websocket plugin?

I am trying to build an application using Angular 6 + Ngxs + WebSocket. However, some how I am not able to connect with the WebSocket plugin provided by Ngxs.
In the documentation, Ngxs mentioned that we can catch the error using WebsocketMessageError action. (I hope I understood the documentation correctly)
Documentation Link: https://ngxs.gitbook.io/ngxs/plugins/web-socket
But I am trying to import this action in my Service class, then it says that Action is not available.
Here is the code of my WebSocketService class.
import { Injectable } from '#angular/core';
import { Store, Actions, ofActionDispatched } from '#ngxs/store';
import { WebsocketMessageError, ConnectWebSocket } from '#ngxs/websocket-plugin';
import * as Rx from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class WebSocketService {
private subject: Rx.Subject<MessageEvent>;
constructor(private store: Store, private actions$: Actions) {
this.actions$
.pipe(ofActionDispatched(WebsocketMessageError))
.subscribe(({ payload }) => {
console.log('Got action: ', payload);
});
}
public connect() {
this.store.dispatch(new ConnectWebSocket());
}
}
I am getting following error:
module #ngxs/websocket-plugin/ngxs-websocket-plugin"' has no exported member
'WebsocketMessageError'.
import WebsocketMessageError
This looks like a bug. Please report it in the github repository:
https://github.com/ngxs/store/issues

No 'Access-Control-Allow-Origin' header when retrieven data from WebApi to angular 2 application

I am trying to retrieve data from a WebApi project I have created. I have build a new Angular 2 application, which going to get this data through an Api call.
Already have made the data ready in the WebApi project. If I use postman to call the Api, I get data like this example:
[
{
"Id": 1,
"BookingNr": "123456789",
"OutboundDate": "2016-02-05T00:00:00",
"ReturnDate": "2016-04-04T00:00:00",
"Route": "Oslo - Stockholm",
"Passengers": "1 Adult and 1 Child",
"VehicleType": "Car 1.85m x 4.5m"
},
{
"Id": 2,
"BookingNr": "234567891",
"OutboundDate": "2016-03-05T00:00:00",
"ReturnDate": "2016-04-04T00:00:00",
"Route": "Stockholm - Oslo",
"Passengers": "2 Adult and 1 Child",
"VehicleType": "Car 1.85m x 4.5m"
}
]
In my angular 2 project, I have a main component, which calls a service to get the data from the api.
Main component:
#Component({
selector: 'reservation-component',
providers: [...FORM_PROVIDERS, BookingsService],
directives: [...ROUTER_DIRECTIVES, CORE_DIRECTIVES, BookingsListComponent ],
styles: [`
agent {
display: block;
}
`],
pipes: [],
template: `
***No Html in this example***
`,
bindings: [BookingsService],
})
#Injectable()
export class BookingsComponent {
bookings: Array<amendmentBookings> = [];
constructor(public bookingsService: BookingsService) {
this.bookings = this.bookingsService.getBookings();
}
}
Then there is the Service, which makes the call.
Service
#Injectable()
export class BookingsService {
constructor(private router: Router, public http: Http) {
console.log('Booking Service created.', http);
}
getBookings(): Array<amendmentBookings> {
var bookingsRetrieved: Array<amendmentBookings>
this.http.get('http://localhost:55350/api/bookings')
.map(res => res.json())
.map((bookings: Array<any>) => {
let result: Array<amendmentBookings> = [];
if (bookings) {
bookings.forEach(booking => {
result.push(
new amendmentBookings(
booking.bookingNumber,
new Date(booking.outboundDate),
new Date(booking.returnDate),
booking.route,
booking.passengers,
booking.vehicleType))
});
}
return result;
}).subscribe(data => {
bookingsRetrieved = data;
console.log(bookingsRetrieved)
},
err => console.log(err));
return bookingsRetrieved;
}
}
export class amendmentBookings {
constructor(
public bookingNumber: string,
public outboundDate: Date,
public returnDate: Date,
public route: string,
public passengers: string,
public vehicleType: string
) { }
}
When I try to call it, I get the following error:
XMLHttpRequest cannot load http://localhost:55350/api/bookings. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
Have tried to search for the problem, but can only find that something is blocking the request, but no solution for it..
This is how my bookingsController looks like in Visual Studio (I'm using entity framework)
BookingController
public class BookingsController : ApiController
{
private BookingsContext db = new BookingsContext();
// GET: Bookings
public IEnumerable<Booking> Get()
{
return db.Bookings.ToList();
}
}
You need to enable CORS on your Web API if you want to be able to call it from javascript code hosted on a different domain.
So basically in your Web API bootstrap script you would call:
config.EnableCors();
and then decorate your controller with the proper attribute:
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class BookingsController : ApiController
{
...
}
Of course enabling CORS for all domains (*) comes with its security implications so you might want to selectively enable it only for the domain on which your javascript calling code is hosted.

Resources