javascript 强制Axios返回JSON响应

hiz5n14c  于 2023-03-21  发布在  Java
关注(0)|答案(2)|浏览(207)

我正在评估Axios,有一件事我似乎不知道如何强制执行响应是JSON。从我收集的信息来看,Axios会根据内容类型(https://stackoverflow.com/a/65976510/1103734)自动为我们解析JSON。然而,我希望实际强制执行响应是JSON(例如,如果我的nginx代理由于下游错误而返回HTML,我希望处理这个问题)。
我注意到Axios request config有一个responseType属性,但据我所知,这并不用于实际强制返回预期类型。

axios.get('http://cataas.com/cat?html=true', {responseType: "json"})
  .then(res => console.log(`Response:\n ${res.data}`))
  .catch((err) => console.log(`Error: ${err}`))

输出:

Response:
 
                <!DOCTYPE html>
                <html lang="en">
                    <header>
                        <meta charset="utf-8">
                    </header>
                    <body>
                        <img alt="GRxZb4kUHleQ3LhC" src="/cat/GRxZb4kUHleQ3LhC">
                    </body>
                </html>

我能找到的最好的方法是将JSON.parse放在transformResponse属性中,但这意味着如果在解析具有错误状态代码的响应时出错,我将在catch中丢失该状态代码信息。

axios.get('http://cataas.com/cat?html=true', {responseType: "json", transformResponse: JSON.parse})
  .then(res => console.log(`Response\n ${res.data}`))
  .catch((err) => console.log(`Error: ${err}`))

输出(显然,SyntaxError不包含任何关于响应的信息):

Error: SyntaxError: Unexpected token < in JSON at position 17

有没有一个好的方法来实现我想要的?

uxh89sit

uxh89sit1#

我认为术语“JSON”存在一些混淆

我想你的意思是你希望Axios的结果是一个Javascript * 对象 *,而不是一个JSON字符串。这种混淆很常见,因为我们经常把Javascript对象称为“JSON对象”作为俚语。
如果您在控制台中输入以下内容,则a的结果值将是一个Javascript对象:

const a = { x: 10}

有些人会称a为JSON对象,但严格来说它不是。a的JSON表示如下 string

{ "x": 10 }

Axios返回给你的不是JSON字符串,而是Javascript * 对象 *

这包含了各种各样的信息,在对象的不同属性中。对我们来说重要的是:

  • “data”属性,它可以是包含HTML的字符串,或Javascript对象,或其他东西。
  • 在“headers”属性中,“content-type”子属性。如果data是Javascript对象,则以“application/json”开始,如果data是HTML响应,则以“text/html”开头。

下面是显式显示服务器响应的 content-type 的代码。
一个二个一个一个

http://cataas.com/cat?html=true API返回HTML字符串

Axios忠实地在data属性中提供该字符串。

<!DOCTYPE html>
                <html lang="en">
                    <header>
                        <meta charset="utf-8">
                    </header>
                    <body>
                        <img alt="ltBmKwnyGcagdHo3" src="/cat/ltBmKwnyGcagdHo3">
                    </body>
                </html>

https://dummyjson.com/products/1 API返回 *JSON字符串 * 给Axios

Axios会自动将JSON字符串转换为Javascript * 对象 *。

{"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.com/data/products/1/thumbnail.jpg","images":["https://i.dummyjson.com/data/products/1/1.jpg","https://i.dummyjson.com/data/products/1/2.jpg","https://i.dummyjson.com/data/products/1/3.jpg","https://i.dummyjson.com/data/products/1/4.jpg","https://i.dummyjson.com/data/products/1/thumbnail.jpg"]}

一种实现你想要的方法:

  • 读取response.headers["content-type"]
  • 如果它以application/json开头,那么您很幸运:只需将response.data视为Javascript对象
  • 如果它以text/html开头,尽管你已经请求了一个JSON,那么就有问题了。你可以将response.data作为HTML阅读,并查看服务器是否提供了任何有用的信息。

我不喜欢把所有东西都 Package 在一个try/catch中,然后拾取一个失败的JSON. parse。我们已经得到了关于response.data是否是一个对象的信息,所以让我们使用它。

你甚至可以为Axios写一个 Package 器

这样就可以完成上面的任务,所以你只需要写一次代码。

wh6knrhe

wh6knrhe2#

我想我已经找到了一种方法去做我想做的事

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.com/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}`)
        }
    }
  )

对目前情况的简要总结
1.我使用transformResponse(body) => body,如this answer所示。这允许响应拦截器实际获取文本响应数据。这是使其工作的关键。
1.然后,我将实际的解析延迟到响应拦截器,这允许我手动处理解析的错误。
1.从那里,我可以创建一个包含原始响应的自定义异常,然后在错误处理中使用它。

相关问题