My first time here, I hope that you can help me.
I'm trying to create a response interceptor on Angular 5. I want to handle responses with status [202] successfully.
I'm doing a call to a webservice, the interceptor is working well it catches the error. If I return Observable.throw('') this will arrive to the error function on subscribe, but when I try to return a new Observable to resolve the exception, it is not arriving on success method of subscribe.
The interceptor code:
import {Injectable} from '#angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor,
HttpRequest, HttpResponse} from '#angular/common/http';
import {Observable} from 'rxjs/Rx';
#Injectable()
export class Handle2xxResponseInterceptor implements HttpInterceptor{
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req).catch(error => {
if (error instanceof HttpErrorResponse) {
if (error.status === 202) {
return Observable.of(error); // or return Observable.empty();
}
}
return Observable.throw(error);
});
}
}
Thank you people.
You're not able to catch 202 because 202 isn't an http error code and catch only catches errors. Instead, just use a normal map or do operator or higher order operator like switchMap to do whatever you want with it:
return next.handle(req).switchMap(response => {
if (response.status === 202) {
// process error
return Observable.empty();
}
return Observable.of(response);
});
Related
I have a #Get() method in a Controller. I get the response, but after the response is successfully I would like to call an async method after a delay of some milliseconds.
I'm using a Middleware, but seems like the response is pending for the time specified in the async method.
How can I do to solve the response in time, and after the delay call the custom method from the Custom Service?
Here is the used sample code:
#Controller()
export class CustomController {
#Get("custom-controller-route")
getCustomValue(#Res() response: Response) {
return response.status(200).send({
value: 1
})
}
}
The middleware code is the following:
#Injectable()
export class CustomMiddleware implements NestMiddleware {
constructor(private readonly customService: CustomService) {}
use(req: any, res: any, next: () => void) {
let send = res.send
res.send = async (customResponse: CustomResponse) => {
const { value } = customResponse
await customService.customMethod(value, 5000) // Delay of 5 seconds
res.send = send
return res.send(exchangeRateResponse)
}
next()
}
}
And the CustomService has the next code:
#Injectable()
export class CustomService {
async customMethod(value: any, delay) {
await firstValueFrom(
timer(delay).pipe(
tap(() => {
// Here is the logic that needs to be run in the delay time after the response is finished
console.log(`Custom Service - custom method called with: ${value} after: ${delay} milliseconds.`)
})
)
)
}
}
I could do it instead of returning the response in the Controller method, I only call the res.status(200).send({}) with the specific response and after that I call the specific method call with the delay.
#Controller()
export class CustomController {
#Get("custom-controller-route")
getCustomValue(#Res() response: Response) {
response.status(200).send({ value: 1 })
// Call the delayed Service method
}
}
Any other better option is welcome
I'm implementing an interface that has a function that returns Observable.
I also need to pass some value to the Observable, but it may take some time to receive that value.
How can I still return the Observable and also make it wait for the needed value?
To be more specific, I'm implementing an HttpInterceptor and I want to set a token to the request header.
The token value could be unavailable, so need to wait a little (asynchronously) and try again, until the value is received.
Then set the token in the request header and continue.
How can I implement such mechanism?
#Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenService: HttpXsrfTokenExtractor) { }
getToken(callback) {
let token = this.tokenService.getToken();
if (!token) {
// a valid token wasn't received. wait a little and try again
setTimeout(() => {
this.getToken(callback); //recursive call
}, 1000);
} else {
// found valid token
callback(token);
}
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// this part should set req when a token is received, but it is asynchronous
this.getToken((token) => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
});
// this returns Observable. I must return Observable, but req is not ready at this point
return next.handle(req);
}
}
The easiest thing to do is use RxJs operators. Using switchMap should be a good solution here. Essentially in this case, switchMap allows you to chain dependent observables together and only return the inner observable. It should look something like this:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return getToken.pipe(
switchMap(token => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(req);
}
);
}
Please note, You'll need to adjust your getToken to return an observable as well in order for this to work.
Looking at the code, it seems that we have callback from getToken and observable from intercept. It is better to always use observable if possible.
We could convert getToken(callback) to use observable. RxJS has retryWhen operator that we could use to handle retry.
getToken() {
const tokenFromService = of(this.tokenService.getToken()); // convert to observable
return tokenFromService
.pipe(
map(token => {
if (!token) {
throw new Error('token is not specified'); // it will be caught by retryWhen
}
return token;
}),
retryWhen(error => {
return error
.pipe(
tap(() => console.log('error happened, retry request token')),
delay(1000)
)
})
)
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.getToken()
.pipe(
switchMap(token => {
const modifiedReq = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(modifiedReq);
})
)
}
Hope it helps
Reference:
retryWhen
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.
all!
I need to get axios Promise reject in my vue component using vuex.
I have serviceApi.js file:
export default {
postAddService(service) {
return axios.post('api/services', service);
}
}
my action in vuex:
actions: {
addService(state, service) {
state.commit('setServiceLoadStatus', 1);
ServicesAPI.postAddService(service)
.then( ({data}) => {
state.commit('setServiceLoadStatus', 2);
})
.catch(({response}) => {
state.commit('setServiceLoadStatus', 2);
console.log(response.data.message);
return Promise.reject(response); // <= can't catch this one
});
}
}
and in my vue component:
methods: {
addService() {
this.$store.dispatch('addService', this.service)
.then(() => {
this.forceLeave = true;
this.$router.push({name: 'services'});
this.$store.dispatch('snackbar/fire', {
text: 'New Service has been added',
color: 'success'
}).then()
})
.catch((err) => { // <== This never hapens
this.$store.dispatch('snackbar/fire', {
text: err.response.data.message || err.response.data.error,
color: 'error'
}).then();
});
}
When i use axios directly in my component all work well. I get both success and error messages.
But when i work using vuex i can't get error message in component, hoever in vuex action console.log prints what i need.
I'm always getting only successfull messages, even when bad things hapen on beckend.
How can i handle this situation using vuex ?
Wellcome to stackoverflow. One should not want to expect anything back from an action. After calling an action. Any response should be set/saved in the state via mutations. So rather have an error property on your state. Something like this should work
actions: {
async addService(state, service) {
try {
state.commit('setServiceLoadStatus', 1);
const result = await ServicesAPI.postAddService(service);
state.commit('setServiceLoadStatus', 2);
} catch (error) {
state.commit("error", "Could not add service");
state.commit('setServiceLoadStatus', 2);
console.log(response.data.message);
}
}
}
And in your component you can just have an alert that listens on your state.error
Edit: Only time you are going expect something back from an action is if you want to call other actions synchronously using async /await. In that case the result would be a Promise.
Here's an example of some code I picked up for an Angular2 tutorial by Max Schwarzmueller on YouTube: https://www.youtube.com/playlist?list=PL55RiY5tL51olfU2IEqr455EYLkrhmh3n.
import {Injectable} from "angular2/core";
import {CONTACTS} from "./mock-contact";
#Injectable()
export class ContactService {
getContacts() {
return Promise.resolve(CONTACTS);
}
insertContact(contact: Contact) {
Promise.resolve(CONTACTS)
.then(
(contacts: Contact[]) => contacts.push(contact)
);
}
}
In this example, the CONTACTS object is static JSON. A promise isn't necessary here, but was used to show usage of a service in the real world.
I pretty much understand it, but I'm trying to migrate this idea into a service where I'm using an observable instead of a promise.
I want to make a change to the CONTACTS array, and then have the Observable emit .then again to tell all the observers to do their thing one more time.
On an observable, what would be analogous to Promise.resolve here? The documentation for RxJS observables is a lot to wade through.
If this is just a dumb idea, or there's a better way, please let me know.
With
getContacts() {
return Observable.of(CONTACTS);
}
the observable will complete after it emitted the CONTACTS.
See also http://paqmind.com/posts/rxjs-error-and-completed-events-demystified/
The following is functioning code that works as far as my original question goes. I've replaced the JSON formatted array from CONTACTS with data pulled from a local JSON file, and I'm dealing with products instead of contacts.
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Product } from './product';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ProductsService {
private _productsUrl = "./products.json";
private _productObservable: Observable<Product[]>;
private _products: Product[];
constructor(private _http: Http) {
}
getProducts() : Observable<Product[]> {
this._productObservable = this._http.get(this._productsUrl)
.map((response:Response) => {
if (this._products == null)
{
this._products = response.json();
}
return this._products;
})
.catch((error:any) => Observable.throw(error || 'Server error'));
return this._productObservable;
}
deleteProduct()
{
this._products.pop();
return Observable.of(this._products);
}
}