I have a simple angular2 http get call like below
import { Http, Response } from '#angular/http';
#Injectable()
export class ServiceBase {
protected resource: string;
constructor(private _http: Http) { }
getTnCByCountryCodeNdType(countryCode: string, tncType: string) {
let url = `${CONFIG.URL}/tnc?countryCode=${country}&tnc=${tnc}`;
return this._http.get(url, this.getHeader())
.map((res: Response) => {
return res.json();
});
}
}
The Error response from the above when received,
I tried
this.service
.getTnCByCountryCodeNdType('MY', this.tnc.toLowerCase())
.subscribe(x => {
//
},
(err: Response) => {
error = JSON.parse(err);
});
where: err is the response error;
and it threw the usual json error .
EXCEPTION: Unexpected token R in JSON at position 0
To my surprise
error = err.json();
Works fine. What is the difference between the two and why does the first one failed? Any help is appreciated
JSON.parse is a JavaScript thing. It parses a string containing JSON into whatever that string represents. You don't pass it objects. Clearly, your err isn't a string.
The res and err that Angular 2's http gives you are Response objects, which says it derives from Body. I can't see anything in the Angular 2 docs saying what Body is, but clearly it has a json function that returns the result of parsing the response data as JSON. That's not a JavaScript thing, that's something from Angular. (As noted in the link above, it's inspired by — but not the same as — fetch.)
JSON.parse expects a JSON-formatted string, but you give it a Response object, which is not JSON, hence the parsing error.
res.json() extracts the JSON-formatted data from the Response object and converts the data into a JavaScript object.
Related
I am trying to convert a javascript object array to JSON to pass it with the POST request to the cloud function. However when I use the JSON.parse() function I get an error:SyntaxError: Unexpected token u in JSON at position 0
Can someone tell me what I'm doing wrong, this is my POST request:
const body = `{
"item":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}]
}`;
const init = {
method: 'POST',
body
};
fetch('https://us-central1-web-app-bbo-prod.cloudfunctions.net/TestObject', init)
.then((response) => {
return response.json(); // or .text() or .blob() ...
})
.then((text) => {
// text is the response body
})
.catch((e) => {
// error in e.message
});
and this my cloud function:
exports.TestObject = functions.https.onRequest(async(request, response)=> {
var corsFn = cors();
corsFn(request, response, async function() {
const myJSON = request.body.item;
console.log(JSON.parse(myJSON))
//console.log(item)
})})
TLDR:
Server: remove JSON.parse
Client: change init to
const init = {
method:'POST',
body:JSON.stringify(body),
headers: {
'Content-Type':'application/json'
}
}
Explained:
You've got three issues I see.
Anytime you see the following you are calling JSON.parse on invalid stringified JSON.
SyntaxError: Unexpected token u in JSON at position 0
First, req.body should already be JSON if everything's working properly and you received JSON. To call JSON.parse(req.body.item) is to pass an Object to a function that takes strings. You can just refer to req.body.item.
Second you have to make sure you're sending the correct body, you are passing an object as the body for the fetch function. I'm not familiar with the fetch function but a quick look at the Mozilla examples indicate that should it should be stringified, i.e. body:JSON.stringify(body). The object you pass likely is either converted to form data or has toString() called on it and you're sending [object Object].
// REPL
> ({foo:'bar'}).toString()
'[object Object]'
Third, you have to make sure it gets parsed properly on the backend.
According to the GCloud nodeJS docs the nodejs google cloud functions implement an endpoint for an Express Router.
Normally if you wanted to use JSON the way you are you would need the body parser BodyParser middleware. The examples here (GCloud HTTP Functions) indicate that the body-parser middleware is already instead and working based off of Content-Type header.
Since it's deciding whether to parse it as JSON based on content type you want to set the header in your fetch function as 'content-type':'application/json'.
I get this error every time I try to use the POST method in my API.
SyntaxError: Unexpected end of JSON input at fetch.then.response
When I use the GET method I get the data normally.
This is the code:
const teste = () => {
fetch("myURL/test", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: 1,
name: "Teste",
amount: 1,
value: 3
})
})
.then(response => response.json()) //Here is the error
.then(data => {
console.log(data);
})
.catch((err)=>console.log(err))}
Can someone help me? Thank you.
EDIT:
I just add this line to see the log:
.then(response => console.log(response))
Here is what I got:
Response {
type: "cors",
url: "myURL/test",
redirected: false,
status: 201,
ok: true,
…}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 201
statusText: ""
type: "cors"
: "myURL/test"
__proto__: Response
It means that the page fetched at myURL/test does not responds with JSON content, or with malformed JSON content. This is not an error in your script, which is fine, this is an error with your server, that is not serving JSON content at myURL/test.
Also, note that servers might not respond similarly to GET requests and POST request for the same URL! If you fetch valid JSON from a GET request but, as you described, failed to fetch valid JSON from a POST request, your server might be serving different content depending on the request type. Investigate that.
Debug tips
Replace then(resp => resp.json()) by then(resp => resp.text()).then(console.log) to see what the served content looks like
Use Postman to simulate API calls, and see what the served content looks like, with more details
Examine the response with the developer tools:
In Chrome
Open the console (F12)
Go to the network tab
Click on the file server by myURL/test
Click on response: that will be the text content. It shoud be properly formatted JSON.
Basically GET method does not send your body object to the server in which you get the response. Only the POST action will send your body object to the server.
I assume that the object you wish to send is the problem. Either the server which you are trying to connect does not expects the body object as string or you should make sure you have parsed the JSON properly before processing.
Looks like the API you're calling, doesn't have a response body on this particular POST. Then when you call response.json() (converting response.body to json) it runs into error.
Or maybe the response is body is not a valid json body.
If you wanna handle this error more fashion way you could go like this:
tryGetJson = async (resp) => {
return new Promise((resolve) => {
if (resp) {
resp.json().then(json => resolve(json)).catch(() => resolve(null))
} else {
resolve(null)
}
})
}
https://github.com/github/fetch/issues/268#issuecomment-399497478
(for people coming later but dealing with this problem)
The problem is most probably server error or invalid URL but you can't see it because all examples on internet how to work with fetch are missing one important part - the server or network failure.
I think the correct way how to deal with fetch is test response if contains errors before conversion to json.
Check the part of the first then in example where it.ok is tested:
async function fetchData() {
return await fetch('https://your-server.com/some-NOt-existing-url/')
.then(it => {
if (!it.ok) {
throw `Server error: [${it.status}] [${it.statusText}] [${it.url}]`;
}
return it.json();
})
.then(receivedJson => {
// your code with json here...
})
.catch(err => {
console.debug("Error in fetch", err);
setErrors(err)
});
}
(note: it is just name convention borrowed from Kotlin, it makes JavaScript code shorter. It is alias for anything on left side of expression so in this case for response)
I am trying to send multiple objects in the response as json back to client from one route. It is some kind of middleware, that gets called, and then it calls itself another route inside to get the data and do some data processing. Here is the code:
const axios = require('axios');
var datetime = require('node-datetime');
function MiddlewareRoutes(router) {
var MiddlewareController = require('../controllers/MiddlewareController')
router.route('/Middleware/someotherLink/parametres').get(function(req,res,next) {
console.log(req.params.id, req.params.startTime, req.params.endTime);
url = `http://localhost:hidden/link/..`;
url2 = "http://localhost:port+params..."
axios.get(url) //, {responseType: 'json',}
.then(response => {
var formattedData = formatData(response.data);
[max,min] = getMinMax(formattedData);
res.write("max:",max);
res.write("min:",min);
res.write(formattedData);
res.end();
})
.catch(error => {
console.log(error);
});
})
}
However, I am getting the error:
TypeError: First argument must be a string or Buffer
at write_ (_http_outgoing.js:642:11)
at ServerResponse.write (_http_outgoing.js:617:10)
at axios.get.then.response (C:\Users\U500405\Desktop\Backend\routes\MiddleWare.js:19:13)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
What am I doing wrong? I cannot just send strings, because I have to send objects...
Write is for writing strings to the response body, the parameters accepted are (chunk[, encoding][,callback]), however an object is not a string, and your min/max values are not encodings.
As said before, you could use JSON.stringify to convert an object into a JSON string, however since this is pretty common behaviour Express provides a send method that can do exactly that.
res.write(JSON.stringify({
min,
max,
formattedData
}));
or
res.send({
min,
max,
formattedData
});
res.write(formattedData); Here formatted data is a object . As the error message says, write expects a string or Buffer object, so you must convert it. by doing so : res.write(JSON.stringify(formattedData)) . The node expects the content to not to be a object because it needs string to pass on to the server. The server only understands plain text input as mentioned in Nodejs Docs Nodejs Doc Link for res.write() , and by default the encoding is 'utf-8' . so When sending a object through the server , the server discards it and throws an error of expected buffer chunks or string data.
I have a service call that when it returns a 404 error, I want to display the message that comes from the server when the status is 404. So, in event of an error or success, I get a post json that gives me a status code and message that indicates if it was successful or not.
Currrently, I have this service call:
this._transactionService.findExistingTransaction(user, searchNumber)
.subscribe(data => {
this.transactionResponse = data;
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal'], {queryParams: {bill: searchNumber}});
this.onDismiss();
}, (err) => { this.displayErrors = true;});
on error, it will set the bool displayErrors = true and then I can show the error message in my UI.
In html code:
<input #inputtedNumber class="transactionInput" placeholder="{{numberPlaceholder | translate }}"/>
<div class="error-msg1" *ngIf="displayErrors" style="margin-left:90px;" name="errorMsg">
{{transactionResponse._errorDetails._message}} </div>
This is the json that gets posted back when I directly try to access api endpoint:
{
"_transactionNumber":null,
"_order":null,
"_errorDetails":{
"_status":"404",
"_message":"Number is not available"
}
}
I bind to the transactionResponse object that I get back from my service call. Unfortunately, although I believe this should work, I get the issue that _errorDetails is undefined and so nothing shows up.
I wonder if this is the right setup for something like this? If now, how can I fix it?
Thanks!
EDIT: Duplicate SO post with no answer: How to read Custom error message from backend in Angular 4/2
The response body from the server should be in the error property of the error response that comes back in the error callback.
Regarding HttpErrorResponse, the documentation states:
A response that represents an error or failure, either from a non-successful HTTP status, an error while executing the request, or some other failure which occurred during the parsing of the response.
Any error returned on the Observable response stream will be wrapped in an HttpErrorResponse to provide additional context about the state of the HTTP layer when the error occurred. The error property will contain either a wrapped Error object or the error response returned from the server.
If you want to use the same transactionResponse to display the errors, then assign the error property of the err that comes back to this.transactionResponse.
Service Call
this._transactionService.findExistingTransaction(user, searchNumber).subscribe(
(data) => {
this.transactionResponse = data;
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal'], {queryParams: {bill: searchNumber}});
this.onDismiss();
},
(err: HttpErrorResponse) => {
this.displayErrors = true;
// assign the error property of the err that comes back to the transactionResponse
this.transactionResponse = err.error;
});
HTML
Then this will work.
<input #inputtedNumber class="transactionInput" placeholder="{{ numberPlaceholder | translate }}"/>
<div class="error-msg1" *ngIf="displayErrors" style="margin-left:90px;" name="errorMsg">
{{transactionResponse._errorDetails._message}}
</div>
There was some work done to this part of Angular in September 2017. parse error response body for responseType "json" So you may need to update Angular depending on your version.
This solution was tested on the following:
Node v8.2.1
NPM v5.3.0
Angular CLI: 1.7.2
Angular: 5.0.0
Edit: StackBlitz example
HttpErrorResponse StackBlitz example
This example makes some assumptions about what the service looks like and what endpoint it is calling. The service makes a POST call to www.google.com. This fails and returns an HttpErrorResponse.
{
"isTrusted": true
}
The error property of the HttpErrorResponse is assigned to this._transactionResponse. This can then be accessed in the template and displayed in the browser.
Your problem is that in the event of an error, your
data => {
this.transactionResponse = data;
code does not get called - you got an error response, not a normal response afterall.
Try to get the information from the
}, (err) => { this.transactionResponse = err
part.
I think you can use a typed response:
On your error notification type you could have something like:
err => {
this.localErrorResponse = err as ErrorResponse;
this._order= this.localErrorResponse._order;
}
inside your class, also, you could have:
import { ErrorResponse } from './error-response';
localErrorResponse: ErrorResponse;
_order: string;
and then, you could have your ErrorResponse class like:
import { ErrorDetail } from './error-detail';
export class ErrorResponse{
_transactionNumber: number;
_order: string;
_errorDetails: ErrorDetail;
}
and class ErrorDetail
export class ErrorDetail {
_status: number;
_message: string
}
then you can map some other variables, as _order (this._order), or get them from your localErrorResponse (this.localErrorResponse) variable
I have a service call that when it returns a 404 error, I want to
display the message that comes from the server when the status is 404
...
I bind to the transactionResponse object that I get back from my
service call. Unfortunately, although I believe this should work, I
get the issue that _errorDetails is undefined and so nothing shows up.
Try this:
StackBlitz EXAMPLE
I'm stuck in a problem that doesn't make ANY sense. I'm using the request module to do a request for other server that I own, and this server is clearly answering with the following JSON:
{"bucket":"name-of-my-bucket","prefix":"cb012af0-ac7a-414b-b474-a9c71cbec811/"}
If I log the type of the response, will be a string:
console.log(response.body) // string
But I can't parse to a javascript object, because it will throw the following error:
Unexpected token ( in JSON at position 0
I'm stuck on this for the last 3 hours, I already tried everything, including answering with a empty JSON, but the error persists. Do someone have any idea what it is?
const request = require('request-promise');
request.post('http://...', {
formData: {
// data here
}
}).then(response => {
// request.body is application/json
console.log(response.body); // {"bucket":"name-of-my-bucket","prefix":"cb012af0-ac7a-414b-b474-a9c71cbec811/"}
const data = JSON.parse(response.body);
}).catch(console.error);
Your json data is in "response" itself.
so console.log(response) prints your json object.