Reputation: 16729
I am using TypeScript and Rxjs. I am sending data to the server using Rxjs. I have also implemented a custom interceptor. The interceptor returns a type:0
event and also the response from the server as two separate events. I suppose type:0
means that the request has been sent.
I don't want to do anything when type:0
event is received so I have put checks in the code to ignore it but my code still executes a piece of logic even if the event is type:0
. Have I made a mistake?
The code where I send the request is
let response:ServerResponseAPI = this.dataManagementService.addData(this.newData);
console.log("add practice question - response is ",response); //I see this print
if (response != null) { //for type:0, this should be null but isn't
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(response);
if (isResponseStructureOK) {
console.log("received response from server: " + response.result);
let dialogComponentRef: ComponentRef<DialogBoxComponent> = this.maxFilesDialogContainerRef.createComponent(this.dialogFactory);
dialogComponentRef.instance.dialogMessage = response.result + ": " + response['additional-info'];
dialogComponentRef.instance.dialogID = "maxFilesDialog";
dialogComponentRef.instance.dialogShow();
return;
} else { //response structure not ok
console.log("received incorrect response structure from server: ", response);
let dialogComponentRef: ComponentRef<DialogBoxComponent> = this.maxFilesDialogContainerRef.createComponent(this.dialogFactory);
dialogComponentRef.instance.dialogMessage = "Server error";
dialogComponentRef.instance.dialogID = "maxFilesDialog";
dialogComponentRef.instance.dialogShow();
return;
}
} //end of not null response
addData is
addData(data:Data):ServerResponseAPI{
console.log("In DataManagementService: addData: ",data);
return this.bs.createData(data).subscribe((resBody/*:any*/)=>{
if(resBody != null) { //null means that the response wasn't an HttpResponse but probably some internal Rxjs event (eg type 0)
console.log('response from server:', resBody);
<ServerResponseAPI>resBody; //returning response body which is of type ServerResponseAPI;
}
else null; //this means type0 event
});
}
and bs.createData is
public createData(data:Data):any{
console.log('contacting server at '+this.API_URL +this.NEW_DATA_URL +" with data "+data+ " with httpOptions "+httpOptions.withCredentials + ","+httpOptions.headers );
let newData = new DataAPI(data)
let body = JSON.stringify(newData);
return this.http.post(this.NEW_DATA_URL,body,httpOptions)
.map(response => { //I suppose this could be any event from custom interceptor
if(response instanceof HttpResponse)
{
console.log('response from backend service:', response);
let result = <HttpResponse<any>>response;
return result.body; //return body of response which ideally should be of type ServerResponseAPI
}
else {
console.log("not an http response")
return null; //returning null for non-HttpResponse type
}
})
.catch(this.handleError); //error handler if Observable fails
}
The interceptor is
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log("outgoing request",request);
const idToken = localStorage.getItem("id_token"); //JWT authentication stuff
console.log("id token is "+idToken);
if (idToken) {
const cloned = request.clone({
headers: request.headers.set("X-Auth-Token",
idToken)
});
console.log("new outgoing request", request);
return next.handle(cloned)
.do((ev: any) => {
console.log("got an event",ev)
})
.catch(errorResponse => {
console.log("caught error from server",errorResponse)
return observableThrowError(errorResponse);
});
}
else {
return next
.handle(request)
.do((ev: any) => {
console.log("got an event",ev)
})
.catch(errorResponse => {
console.log("caught error from server",errorResponse)
return observableThrowError(errorResponse);
});
}
}
When I run the code, I see this message which I shouldn't
got an event {type: 0}
new-data.component.ts:255 add data- response is Subscriber {closed: false, _parent: null, _parents: null, _subscriptions: Array(1), syncErrorValue: null, …}
new-practice-question.component.ts:270 received incorrect response structure from server: Subscriber {closed: false, _parent: null, _parents: null, _subscriptions: Array(1), syncErrorValue: null, …}
I suppose I get HttpEvent
from Rxjs
which is a union of HttpResponse.
type HttpEvent<T> = HttpSentEvent | HttpHeaderResponse | HttpResponse<T> | HttpProgressEvent | HttpUserEvent<T>;
How could I check that HttpEvent
is of type HttpResponse
?
Upvotes: 1
Views: 61
Reputation: 10418
Take a look at this piece of code you have:
addData(data:Data):ServerResponseAPI{
console.log("In DataManagementService: addData: ",data);
return this.bs.createData(data).subscribe((resBody/*:any*/)=>{ (...)
Now look at this one a few lines before:
let response:ServerResponseAPI = this.dataManagementService.addData(this.newData);
There are few things not correct there:
1- addData does not return an object of type/interface ServerResponseAPI, it returns a subscription object from your subscription action, so that you can do subscription.unsubscribe() later on if needed.
2- What you have in your response variable is not a response, you are treating that piece of code as if it were synchronous, it is not.
Upvotes: 0
Reputation: 16729
There were few problems (some fundamental) in the way I wrote the code
1) typescript union.
My understanding of union
type in typescript wasn't correct. HttpEvent
is a union of several types and I can only call methods/properties which are common to all types.
type HttpEvent<T> = HttpSentEvent | HttpHeaderResponse | HttpResponse<T> | HttpProgressEvent | HttpUserEvent<T>;
One common property is type
. Rather than comparing HttpEvent
with HttpResponse
using instanceof
, I had to get the type
from HttpEvent
and then compare it with HttpEventType
like the following in the interceptor code
return next
.handle(request) //next.handle is passing the transformed request (if transformed by intercept here) to the next handler. next.handle returns a Rx stream
.do((ev: HttpEvent<any>) => { // To intercept the response coming back from the server, we can simply hook on the do(..) operator. `do` applies additional Rx operators on the stream returned by next.handle
console.log("got an event",ev)
if (ev.type === HttpEventType.Response) {
console.log('event of type Http response');
} else if (ev.type === HttpEventType.Sent) {
console.log(" event of type httpsent");
} else if(ev.type === HttpEventType.DownloadProgress){
console.log("event of type download progress");
} else if (ev.type === HttpEventType.UploadProgress) {
console.log("event of type upload progress");
} else if (ev.type === HttpEventType.User) {
console.log("event of type user progress");
}else if (ev.type === HttpEventType.ResponseHeader) {
console.log("event of type response header")
} else {
console.log("don't know type",ev.type);
}
})
while, the main issue with the code wasn't this, the debug print was helpful in giving direction.
My application flow was
1) component in function f1 calls function f2 of dataservice to send message this.dataManagementService.addData(this.newData);
2) dataservice's f2 calls f3 of backendservice and also subscribes to observable returned by f3 - this.bs.createData(data).subscribe
3) backendservice's f3 sends post message, creates observable and maps response, returning the observable of map to dataservice's f2) - this.http.post(this.NEW_DATA_URL,body,httpOptions)
4) interceptor.
I misunderstood the async behaviour of Observables
of Rxjs
(which are used by post
) and thought that the data from the server will be available to me here - let response:ServerResponseAPI = this.dataManagementService.addData(this.newData);
But above are just synchronous function calls and what I get at that point is the return value of reverse path - interceptor -> backendservice -> dataservice. The interceptor returns Observable<HttpEvent<any>>
so what I get at let response:ServerResponseAPI = this.dataManagementService.addData(this.newData)
is Observable
of the interceptor. I should subscribe to that observable and wait for the response there. Note that because an interceptor could send many messages, I had to check the type to see when I get the response.
//the Observable got back all the way fromm the interceptor is subscribed to here
this.dataService.addData(this.newData).subscribe((ev:HttpEvent<any>)=>{
console.log("add data - response is ",ev);
if(ev.type === HttpEventType.Response) {
console.log('response from server: returning body '+ev.body);
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
let response:ServerResponseAPI = ev.body;
console.log("received response from server: " + response.result);
let dialogComponentRef: ComponentRef<DialogBoxComponent> = this.maxFilesDialogContainerRef.createComponent(this.dialogFactory);
dialogComponentRef.instance.dialogMessage = response.result + ": " + response['additional-info'];
dialogComponentRef.instance.dialogID = "maxFilesDialog";
//TODOM - once this view gets added, I am not deleting it from the viewcontainer once the dialog's OK is pressed. Need to find a way to do this.
dialogComponentRef.instance.dialogShow();
} else {
console.log("received incorrect response structure from server: ", ev.body);
let dialogComponentRef: ComponentRef<DialogBoxComponent> = this.maxFilesDialogContainerRef.createComponent(this.dialogFactory);
dialogComponentRef.instance.dialogMessage = "Server error";
dialogComponentRef.instance.dialogID = "maxFilesDialog";
//TODOM - once this view gets added, I am not deleting it from the viewcontainer once the dialog's OK is pressed. Need to find a way to do this.
dialogComponentRef.instance.dialogShow();
}
}
else {
console.log("not response. ignoring");
}
});
Upvotes: 1