admin管理员组文章数量:1296453
I'm evaluating Axios and one thing I can't seem to figure out how to enforce that a response is JSON. From what I've gathered, Axios will automatically parse the JSON for us based on the content type (). However, I was hoping to actually enforce that the response is JSON (e.g. if my nginx proxy returns HTML due to a downstream error, I would want to handle that).
I noticed that the Axios request config has a responseType
property, but as near as I can tell, this is not used to actually enforce an expected type is returned. Here's an example snippet that demonstrates what I'm talking about
axios.get('', {responseType: "json"})
.then(res => console.log(`Response:\n ${res.data}`))
.catch((err) => console.log(`Error: ${err}`))
Output:
Response:
<!DOCTYPE html>
<html lang="en">
<header>
<meta charset="utf-8">
</header>
<body>
<img alt="GRxZb4kUHleQ3LhC" src="/cat/GRxZb4kUHleQ3LhC">
</body>
</html>
The best thing I can find is to put JSON.parse
in the transformResponse
property, but this means that if there's an error in parsing a response with a bad status code, I will lose that status code information in my catch
.
axios.get('', {responseType: "json", transformResponse: JSON.parse})
.then(res => console.log(`Response\n ${res.data}`))
.catch((err) => console.log(`Error: ${err}`))
Output (obviously, SyntaxError
does not contain any information about the response):
Error: SyntaxError: Unexpected token < in JSON at position 17
Is there a nice way to achieve what I want?
I'm evaluating Axios and one thing I can't seem to figure out how to enforce that a response is JSON. From what I've gathered, Axios will automatically parse the JSON for us based on the content type (https://stackoverflow./a/65976510/1103734). However, I was hoping to actually enforce that the response is JSON (e.g. if my nginx proxy returns HTML due to a downstream error, I would want to handle that).
I noticed that the Axios request config has a responseType
property, but as near as I can tell, this is not used to actually enforce an expected type is returned. Here's an example snippet that demonstrates what I'm talking about
axios.get('http://cataas./cat?html=true', {responseType: "json"})
.then(res => console.log(`Response:\n ${res.data}`))
.catch((err) => console.log(`Error: ${err}`))
Output:
Response:
<!DOCTYPE html>
<html lang="en">
<header>
<meta charset="utf-8">
</header>
<body>
<img alt="GRxZb4kUHleQ3LhC" src="/cat/GRxZb4kUHleQ3LhC">
</body>
</html>
The best thing I can find is to put JSON.parse
in the transformResponse
property, but this means that if there's an error in parsing a response with a bad status code, I will lose that status code information in my catch
.
axios.get('http://cataas./cat?html=true', {responseType: "json", transformResponse: JSON.parse})
.then(res => console.log(`Response\n ${res.data}`))
.catch((err) => console.log(`Error: ${err}`))
Output (obviously, SyntaxError
does not contain any information about the response):
Error: SyntaxError: Unexpected token < in JSON at position 17
Is there a nice way to achieve what I want?
Share asked Mar 19, 2023 at 20:43 ollienollien 4,79610 gold badges39 silver badges60 bronze badges 12- 2 No, there is no way to enforce a JSON response. The server can always decide to respond what it want. The client can ask for specific response type, but the server doesn't have to follow. – jabaa Commented Mar 19, 2023 at 20:46
-
Of course; I can't control the server response, but I'm looking for a client-side assertion to ensure that the data in
response.data
is actually JSON, and not just a string (in the example I gave above, axios does not throw an error in this case; it simply carries on). Ideally, the response would have a properContent-Type
header, and Axios could see it wasn't JSON, and throw an error. – ollien Commented Mar 19, 2023 at 20:48 -
1
If you want to make sure the response is JSON just try/catch
JSON.parse(response)
and you'll know? – custommander Commented Mar 19, 2023 at 20:52 - 1 TBH, I still don't understand what you want. The response is always a string. It can be a JSON string or something. How do you want to handle a JSON string and how do you want something else? – jabaa Commented Mar 19, 2023 at 21:06
-
1
I want Axios to throw an error when it receives a non-JSON response (whether the content type indicates otherwise or the response is just simply not parsable as json). In other words, I want to guarantee that by the time my application code reads
response.data
, it is actually parsed JSON. Apologies for the confusion – ollien Commented Mar 19, 2023 at 21:07
2 Answers
Reset to default 4I think I've found a way to do what I want
import axios, { AxiosResponse } from "axios";
class BadResponseFormatError extends Error {
constructor (public response: AxiosResponse) {
super("Malformed response");
}
}
axios.interceptors.response.use(
(response: AxiosResponse) => {
if (response.headers["content-type"] !== "application/json") {
throw new BadResponseFormatError(response);
}
try {
response.data = JSON.parse(response.data);
return response;
} catch {
throw new BadResponseFormatError(response);
}
}
)
axios.get('http://cataas./cat?html=true', {responseType: "json", transformResponse: (body) => body})
.then((res) => console.log(`Got response with data ${JSON.stringify(res.data)}`))
.catch((err) => {
// This could also be moved to a response interceptor,
// I just did it here for the sake of demonstration
if (err instanceof BadResponseFormatError) {
console.error(`Got a bad format response with status code ${err.response.status}: ${err.response.data}`)
} else {
console.error(`Got some other error: ${err}`)
}
}
)
A brief summary of what's going on
- I'm using
transformResponse
with a value of(body) => body
, as presented in this answer. This allows the response interceptor to actually get at the textual response data. This was the key to make this work. - I then delay the actual parse to the response interceptor, which allows me to error handle the parse manually.
- From there, I can create a custom exception that contains the original response, which I then use in my error handling.
I think there is some confusion about the term "JSON"
I think what you mean is that you want the result from Axios to be a Javascript object, not a JSON string. The confusion is mon because we often call Javascript objects "JSON objects" as a slang term.
If you type the following into the console, the resulting value of a
will be a Javascript object:
const a = { x: 10}
Some people would call a
a JSON object, but strictly speaking it is not. The JSON representation of a
is the following string:
{ "x": 10 }
What Axios returns to you_ not a JSON string, but a Javascript object
This contains various pieces of information, in different properties of the object. Important to us here are:
The "data" property, which may be a string containing HTML, or a Javascript object, or something else.
Within the "headers" property, the "content-type" subproperty. This will begin with "application/json" if
data
is a Javascript object, and "text/html" ifdata
is an HTML response.
Here is your code showing the content-type of the server response explicitly.
axios.get('http://cataas./cat?html=true')
.then(response => {
console.log("Example of an API returning an HTML response")
const contentType = response.headers["content-type"];
const data = response.data;
console.log("Type of response data is:", contentType)
console.log("Because it is a long string, I am just going to show a few characters of it:", data.slice(0, 40))
})
.catch((err) => console.log(`Error: ${err}`))
axios.get('https://dummyjson./products/1')
.then(response => {
console.log("Example of an API returning an JSON response")
const contentType = response.headers["content-type"];
const data = response.data;
console.log("Type of response data is:", contentType)
console.log("Because it is a small object, I am going to show it all:", data)
})
.catch((err) => console.log(`Error: ${err}`))
<script src="https://cdnjs.cloudflare./ajax/libs/axios/1.3.4/axios.min.js" integrity="sha512-LUKzDoJKOLqnxGWWIBM4lzRBlxcva2ZTztO8bTcWPmDSpkErWx0bSP4pdsjNH8kiHAUPaT06UXcb+vOEZH+HpQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
The http://cataas./cat?html=true
API returns an HTML string
Axios faithfully gives you that string in the data
property.
<!DOCTYPE html>
<html lang="en">
<header>
<meta charset="utf-8">
</header>
<body>
<img alt="ltBmKwnyGcagdHo3" src="/cat/ltBmKwnyGcagdHo3">
</body>
</html>
The https://dummyjson./products/1
API returns a JSON string to Axios
Axios automatically converts that JSON string into a Javascript object for you.
{"id":1,"title":"iPhone 9","description":"An apple mobile which is nothing like apple","price":549,"discountPercentage":12.96,"rating":4.69,"stock":94,"brand":"Apple","category":"smartphones","thumbnail":"https://i.dummyjson./data/products/1/thumbnail.jpg","images":["https://i.dummyjson./data/products/1/1.jpg","https://i.dummyjson./data/products/1/2.jpg","https://i.dummyjson./data/products/1/3.jpg","https://i.dummyjson./data/products/1/4.jpg","https://i.dummyjson./data/products/1/thumbnail.jpg"]}
One way to achieve what you want:
Read
response.headers["content-type"]
If it begins with
application/json
, then you are in luck: just treatresponse.data
as a Javascript objectIf it begins with
text/html
, despite you having requested a JSON, then something has gone wrong. You could readresponse.data
as HTML, and look for whether the server said anything helpful.
I don't like the idea of wrapping everything in a try
/catch
, and picking up a failed JSON.parse. We are already being given information on whether response.data
is an object or not, so let's use that.
You could even write a wrapper for Axios
That could do the above, so you only have to write the code once.
本文标签: javascriptEnforce that JSON response is returned with AxiosStack Overflow
版权声明:本文标题:javascript - Enforce that JSON response is returned with Axios - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741628419a2389215.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论