I'm new to REST services, I have an Angular2 client calling a RestEasy JAX-RS service. All I am trying to get is a "Hello World" message in JSON format. I was expecting only a JSON object, but I get my response with the following structure:
_body: "{"message":"Hello World!!"}"
headers: t
ok: true
status: 200
statusText: "OK"
type: 2
url: "http://localhost:8080/helloapp/rest/hello/world"
__proto__: ...
My question is, Is that the way it should be?
I mean, I thought I would be able to access the JSON object straight from the response. Something like
this.service.getHello()
.then( result => {
console.log(JSON.parse(result)); //{message: "Hello World"}
this.message = JSON.parse(result).message;
});
But I actually have to get it from _body:
this.service.getHello()
.then( result => {
this.message = JSON.parse(result._body).message;
console.log(this.message);//Hello World
});
Is it a RestEasy configuration thing, is there a way to change that?
Or
Should I consider that I will always have a field _body in my response with my data, and that's the default response structure?
For eventual consideration, here is my backend code:
HelloWorld Service:
#Path("/hello")
#Produces({ "application/json" })
#Consumes({ "application/json" })
public class HelloWorld {
public HelloWorld() {}
#GET
#Path("/world")
public Message getHello(){
return new Message("Hello World!!");
}
}
My RestEasy version is 3.1.1.Final running in Wildfly 10.1.0.Final
What you're getting back is the Response object from the Http request. This is what all Http operations will return. The easiest way to parse the JSON from that is to just call the json() method on it
this.service.getHello()
.then((res: Response) => {
let obj = res.json();
});
If you want the getHello to just return the object without having to parse it (on the calling client), then you can do it inside the getHello method by mapping it (using the Observable.map operation)
getHello() {
this.http.get(..)
.map((res: Response) => res.json())
.toPromise();
}
As peeskillet says above, you're getting back the entire Response from the request, and while sometimes you may want to examine the headers, perhaps to handle the different return conditions (retry or redirect on 4xx or 5xx responses for example), most of the time we assume a successful request and we just want the payload.
Angular2 encourages the use of Observables, so your service might look something like this:
getHello()
{
return this.http.get(http://localhost:8080/helloapp/rest/hello/world)
}
And your component may look something like this:
data: string;
ngOnInit() {
this.service
.getHello()
.map(response => response.json())
.subscribe (
data => {
this.data = data,
},
err => console.log('Error',err),
() => console.log('data',this.data)
);
}
You call the service, which is an http.get() and returns an Observable object, and we use .map to parse the response as JSON, which also returns an Observable, which we subscribe to.
Subscribe has three callback functions,
.subscribe(success, failure, complete)
In the example above on success we assign the payload - data - to this.data, if the subscribe fails, you log the error, and when it completes, we can do whatever we like, but in this case, we log this.data to the console - that's optional, but I log out the results while developing and then strip them out later.
Related
I am trying to create an endpoint in a HTTP API that receives data periodically from remote devices.
There is a technological shift happening in this project where devices have previously reported data in XML whereas future implementations will shift towards JSON.
I am writing this API in NestJS (7.x) and TypeScript. Data will be coming in through the same endpoint (POST /) and data format is differentiated by the Content-Type header.
#Controller()
export class IngressController {
constructor(private readonly ingressService: IngressService) {
}
/* ... */
#Post('')
#Header('Cache-Control', 'none')
#HttpCode(HttpStatus.NO_CONTENT)
public async receive(
#Headers('Content-Type') contentType: string,
#Req() req: any,
#Body() body: string,
): Promise<InsertResponse> {
if (IngressController.isJson(contentType)) {
return { inserted: await this.ingressService.insertJsonString(req.body) };
}
if (IngressController.isXml(contentType)) {
return { inserted: await this.ingressService.insertXmlString(req.body) };
}
throw new BadRequestException(contentType, 'Unsupported Content-Type');
}
/* ... */
}
Future devices will report data in JSON (indicated by the Content-Type: application/json header in the HTTP request), legacy devices report in XML (Content-Type: application/xml).
It works splendidly for JSON. However, my problem is that req.body (or body respectively) is an empty object in the XML case. I presume the NestJS middleware is doing something and getting confused by XML, but I have found no hints as to allow XML payloads side-by-side with JSON. I don't mind parsing it manually.
As you suspected NestJS has a built-in bodyparser that will not be able to parse xml. What you could do is to plug in a custom middleware where you decide whether to parse the request body as xml or pass the request on the the next handler.
Something like this should work (I'm using express-xml-bodyparser in this example):
import {NestFactory} from '#nestjs/core';
import {AppModule} from './app.module';
import {Request} from "express";
const xmlParser = require('express-xml-bodyparser');
const xmlParserMidleware = xmlParser();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use((req: Request, res: any, next: any) => {
if (req.path.includes("/api/json-or-xml-handler") && req.header('Content-Type')?.includes('xml')) {
return xmlParserMidleware(req, res, next);
}
next();
});
await app.listen(8020);
}
bootstrap();
Then, in your controller body will either be the parsed json-object or an object representation of your xml:
#Controller()
export class TestControllerController {
#Post('/api/json-or-xml-handler')
receive(#Body() body: any) {
console.log(body);
// ...
}
}
i have cypress request call returning Json array
{ids:[one, two, three]}
How can i parse out one of the array values from the body of response and pass it to the next test?
Considering the given information I guess what you want is Asserting Network Calls from Cypress Tests.
There is a good example in the cypress-example-recipes
I guess you could do something like
describe('Test', () => {
let ids;
it('gets expected response', () => {
cy.server()
cy.route('GET', '/url').as('response')
cy.visit("url")
// log the request object
cy.get('#response').then(console.log)
// confirm the request status
cy.get('#response').should('have.property', 'status', 200)
// confirm the request's response
cy.get('#response').its('response').then((res) => {
expect(res.body).to.deep.equal({
"ids": ["one", "two", "three"]
})
// store the ids in a variable
ids = JSON.parse(res.body)?.ids
})
})
})
I am doing http client request
export class MapjsonService{
theUrl = 'http://localhost:4200/api/Lat_Long.json';
constructor(private http: HttpClient) { }
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
It is working about 99.99% of the time sadly this is running so often that is fails like once every 10 mins with
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:4200/api/Lat_Long.json", ok: false, …}
and
"Http failure during parsing for http://localhost:4200/api/Lat_Long.json"
Now I figured out for some reason my nrql query from newrelic (which is what is being stored in '/api/lat_long.json' does not have the final closing '}' once every orange moon. and this is what is throwing this error. my question is there any whay for me to check if the returned value is valid json and if it is not try the GET request again without terminating the process that called it. Thx
Your code is throwing an error because the json is not correct, therefore it can't be parsed, and therefore the observable throws an error:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
By default, the http client expect json because that's usually what users expect from it. It's not always the case, like the situation you are in right now.
We can tell the http client not to parse the json on its own by specifying what we want from it using the {responseType: 'text'} parameter.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'})
}
But then you need to parse the json when possible. So we will map the observable and parse the content here if possible.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
}
Then do whatever you want, the value returned by the observable will be null if it can't be parsed.
RXJS 6 syntax:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).pipe(
map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
)
}
I have this project I am working on.
the idea is to retrieve books from google book API, using angular 4
I am struggling to understand how to read the JSON response, I am still learning Angular.
I was searching the internet and found this source code on GitHub
google bookstore
I am getting the following error
Argument of type 'Response' is not assignable to parameter of type 'string'.
for this line
let bookResponse = JSON.parse(body);
I am not sure if I am doing it the correct way.
appreciate your help
below is my method for send HTTP request.
getBooks(searchparam: any){
let par = new HttpParams();
par.set('q', searchparam);
return this.httpClient.get('https://www.googleapis.com/books/v1/volumes', {
params : new HttpParams().set('q',searchparam)
}).map(
(response: Response) => {
let data = response;
return data;
}
);
}
below is the method to get data from HTTP request and read JSON response
getBook() {
const dataArrya = this.searchForm.get('param').value;
console.log(dataArrya);
this.requestService.getBooks(dataArrya)
.subscribe(
response => {
this.printData(response)
// console.log(this.bookData);
},
(error) => console.log(error)
);
}
private printData(res: Response) {
let body = res;
let books: Array<Book> = new Array<Book>();
let bookResponse = JSON.parse(body);
console.log(bookResponse);
console.dir(bookResponse);
for (let book of bookResponse.items) {
books.push({
title:book.volumeInfo.title,
subTitle: book.volumeInfo.subTitle,
categories: book.volumeInfo.categories,
authors: book.volumeInfo.authors,
rating: book.volumeInfo.rating,
pageCount: book.volumeInfo.pageCount,
image: book.volumeInfo.imageLinks === undefined ? '' : book.volumeInfo.imageLinks.thumbnail,
description: book.volumeInfo.description,
isbn: book.volumeInfo.industryIdentifiers,
previewLink: book.volumeInfo.previewLink
});
}
}
JSON.parse takes a string, but you're passing it an Angular Response, which is an object (not a string). In order to convert an Angular Response to a string, you can call toString on it, like this:
let bookResponse = JSON.parse(body.toString());
As the reference states,
The responseType value determines how a successful response body will be parsed. If responseType is the default json, a type interface for the resulting object may be passed as a type parameter to request().
HttpClient get already parses the response with JSON.parse by default and there is no need to call it twice.
The result is plain object, not Response (it belongs to Http API and can be confused with Response global when not being imported).
The mentioned repository uses Angular 2 and Http, and the code from there isn't suitable here. HttpClient is newer API that was introduced in Angular 4. The main practical difference between Http and HttpClient is that the latter doesn't require to add .map(response: Response => response.json()) to the request.
I am looking for data in an API via Get request, I need the data inside my OnInit to use in composing other data. The problem is that the method is being called but it is as an async method (without await), it passes everywhere but when the return is obtained the excution of the main method has already been finished with no results. I tried the implementation of asynchronous methods but it did not solve.
service:
getObjects(): MyClass[] {
let objects = new Array<MyClass>();
this.obterTodos<MyClass>(this.uriApi)
.map(res => res.map(x => new MyClass(x.Description, x.Id)))
.subscribe(entities => {
objects = entities;
});
return objects ;
}
get request
public getObjects<TEntity>(url: string): Observable<TEntity[]> {
return this.httpService
.get(this.serviceUrl + url, this.options)
.map(res => res.json())
.catch(this.handleError);
}
component:
ngOnInit() {
this.myObjects= this.setorService.obterSetores();
console.log('this.myObjects is empty here');
}
so you'll want to subscribe to your observable in the component. This is typically done so the component can determine when the http request should be ran & so the component can wait for the http request to finish (and follow with some logic).
// subscribe to observable somewhere in your component (like ngOnInit)
this.setorService.obterSetores().subscribe(
res => {
this.myObjects = res;
},
err => {}
)
now the component knows when the http request finishes