Search code examples
jsonvue.jsaxiosinterceptor

Vue JS, Axios retry request. Prevent JSON quotes


I have an axios interceptor for cases, where I need the user to be authorized, but he isn't. For example, because the token is expired. Now, after a token refresh, the original request should be retried. However, currently the original requests, seems to be changed, so that the Server gives me a JSON.parse error.

SyntaxError: Unexpected token " in JSON at position 0
    at JSON.parse (<anonymous>)
    at createStrictSyntaxError (/var/app/current/node_modules/body-parser/lib/types/json.js:158:10)
    at parse (/var/app/current/node_modules/body-parser/lib/types/json.js:83:15)
    at /var/app/current/node_modules/body-parser/lib/read.js:121:18
    at invokeCallback (/var/app/current/node_modules/raw-body/index.js:224:16)
    at done (/var/app/current/node_modules/raw-body/index.js:213:7)
    at IncomingMessage.onEnd (/var/app/current/node_modules/raw-body/index.js:273:7)
    at IncomingMessage.emit (events.js:314:20)
    at IncomingMessage.EventEmitter.emit (domain.js:483:12)
    at endReadableNT (_stream_readable.js:1241:12)

This is because, instead of the original request, that is JSON, it seems to process it again, puts it in quotes etc., so it becomes a string and the bodyparser, throws the error above. So the request content, becomes:

"{\"traderaccount\":\"{\\\"traderaccountID\\\":\\\"undefined\\\",\\\"traderID\\\":\\\"2\\\",\\\"name\\\":\\\"Conscientious\\\",\\\"order\\\":99,\\\"myFxLink\\\":\\\"\\\",\\\"myFxWidget\\\":\\\"\\\",\\\"copyFxLink\\\":\\\"83809\\\",\\\"tokenLink\\\":\\\"\\\",\\\"tradertext\\\":{\\\"tradertextID\\\":\\\"\\\",\\\"traderaccountID\\\":\\\"\\\",\\\"language\\\":\\\"\\\",\\\"commission\\\":\\\"\\\",\\\"affiliateSystem\\\":\\\"\\\",\\\"leverage\\\":\\\"\\\",\\\"mode\\\":\\\"\\\",\\\"description\\\":\\\"\\\"},\\\"accountType\\\":\\\"\\\",\\\"accountTypeID\\\":1,\\\"minInvest\\\":2000,\\\"currency\\\":\\\"\\\",\\\"currencySymbol\\\":\\\"\\\",\\\"currencyID\\\":1,\\\"affiliateSystem\\\":1}\"}"

instead of

{"traderaccount":"{\"traderaccountID\":\"undefined\",\"traderID\":\"2\",\"name\":\"Conscientious\",\"order\":99,\"myFxLink\":\"\",\"myFxWidget\":\"\",\"copyFxLink\":\"83809\",\"tokenLink\":\"\",\"tradertext\":{\"tradertextID\":\"\",\"traderaccountID\":\"\",\"language\":\"\",\"commission\":\"\",\"affiliateSystem\":\"\",\"leverage\":\"\",\"mode\":\"\",\"description\":\"\"},\"accountType\":\"\",\"accountTypeID\":1,\"minInvest\":2000,\"currency\":\"\",\"currencySymbol\":\"\",\"currencyID\":1,\"affiliateSystem\":1}"}

from the original axios request content. Both are the unformated request contents, that I can see in the developer network console.

The content type, is application/json in both cases.

Below is the Interceptor code:

    Axios.interceptors.response.use(
      (response) => {
        return response;
      },
      (err) => {
        const error = err.response;
        if (
          error !== undefined &&
          error.status === 401 &&
          error.config &&
          !error.config.__isRetryRequest
        ) {
          if (this.$store.state.refreshToken === "") {
            return Promise.reject(error);
          }
          return this.getAuthToken().then(() => {
            const request = error.config;
            request.headers.Authorization =
              Axios.defaults.headers.common[globals.AXIOSAuthorization];
            request.__isRetryRequest = true;
            return Axios.request(request);
          });
        }
        return Promise.reject(error);
      }
    );
  private getAuthToken() {
    if (!this.currentRequest) {
      this.currentRequest = this.$store.dispatch("refreshToken");
      this.currentRequest.then(
        this.resetAuthTokenRequest,
        this.resetAuthTokenRequest
      );
    }
    return this.currentRequest;
  }

  private resetAuthTokenRequest() {
    this.currentRequest = null;
  }

// store refreshToken
    async refreshToken({ commit }) {
      const userID = this.state.userID;
      const refreshToken = Vue.prototype.$cookies.get("refreshToken");
      this.commit("refreshLicense");
      commit("authRequest");
      try {
        const resp = await axios.post(serverURL + "/refreshToken", {
          userID,
          refreshToken,
        });
        if (resp.status === 200) {
          return;
        } else if (resp.status === 201) {
          const token = resp.data.newToken;
          const newRefreshToken = resp.data.newRefreshToken;
          Vue.$cookies.set(
            "token",
            token,
            "14d",
            undefined,
            undefined,
            process.env.NODE_ENV === "production",
            "Strict"
          );
          Vue.$cookies.set(
            "refreshToken",
            newRefreshToken,
            "30d",
            undefined,
            undefined,
            process.env.NODE_ENV === "production",
            "Strict"
          );
          axios.defaults.headers.common[globals.AXIOSAuthorization] = token;
          commit("authSuccessRefresh", { newRefreshToken });
        } else {
          this.dispatch("logout");
          router.push({
            name: "login",
          });
        }
      } catch (e) {
        commit("authError");
        this.dispatch("logout");
      }

So, can you help me to prevent Axios on the retried request to change the request content. So it doesn't put it into quotes and quote the already exisitng quotes?


Solution

  • Thanks to the comment I found a solution. Try to parse the content before resending it:

    axios.interceptors.response.use(
     (response) => response,
     (error) => {
     const status = error.response ? error.response.status : null;
    
     if (status === 401 && error.config && !error.config.__isRetryRequest) {
      return refreshToken(useStore()).then(() => {
        const request = error.config;
        request.headers.Authorization =
          axios.defaults.headers.common["Authorization"];
        request.__isRetryRequest = true;
        try {
          const o = JSON.parse(request.data);
    
          if (o && typeof o === "object") {
            request.data = o;
          }
        } catch (e) {
          return axios.request(request);
        }
        return axios.request(request);
      });
     }
    
     return Promise.reject(error);
    });