aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.36k stars 2.09k forks source link

Problems getting error messages for v6 versions #12943

Closed dadaguai-git closed 3 months ago

dadaguai-git commented 3 months ago

Before opening, please confirm:

JavaScript Framework

Vue

Amplify APIs

REST API

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

pnpm run dev

Describe the bug

It's not a bug, it's a question I have, sorry for using issue for questions

I'm using a custom hook function for api requests, but I'm having a problem. If the request reports a 400 error, I can't get the body content returned by the backend after accepting the error using .catch.

    const execute = async (url: string, body?: object): Promise<any> => {
    isLoading.value = true
    isFetching.value = true
    isFinished.value = false
    canAbort.value = false

    const authToken = (await fetchAuthSession()).tokens?.idToken?.toString() as string

    promise.value = restMethodsMap[methods]({
      apiName: baseUrl,
      path: url,
      options: {
        headers: {
          Authorization: `Bearer ${authToken}`
        },
        ...getRequestBody(methods, body)
      } as unknown as any
    }) as unknown as GetOperation

    return promise.value.response
      .then((res) => {
        isFetching.value = false
        return res.body.json()
      })
      .catch((error) => {
        console.log('hooks error', error.response)
        errorHandler.value!(error)
      })
      .finally(() => {
        isLoading.value = false
        isFinished.value = true
      })
    }
   promise.value = restMethodsMap[methods]({
      apiName: baseUrl,
      path: url,
      options: {
        headers: {
          Authorization: `Bearer ${authToken}`
       },
      ...getRequestBody(methods, body)
      } as unknown as any
   }) as unknown as GetOperation 

The request accepts the 400 code returned by the backend, how to get the body

Expected behavior

Want to get the content of the body returned by the backend

Reproduction steps

The code is shown below

Code Snippet

    const execute = async (url: string, body?: object): Promise<any> => {
    isLoading.value = true
    isFetching.value = true
    isFinished.value = false
    canAbort.value = false

    const authToken = (await fetchAuthSession()).tokens?.idToken?.toString() as string

    promise.value = restMethodsMap[methods]({
      apiName: baseUrl,
      path: url,
      options: {
        headers: {
          Authorization: `Bearer ${authToken}`
        },
        ...getRequestBody(methods, body)
      } as unknown as any
    }) as unknown as GetOperation

    return promise.value.response
      .then((res) => {
        isFetching.value = false
        return res.body.json()
      })
      .catch((error) => {
        console.log('hooks error', error.response)
        errorHandler.value!(error)
      })
      .finally(() => {
        isLoading.value = false
        isFinished.value = true
      })
    }

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

dadaguai-git commented 3 months ago

Looking at the source code, it seems that the convention is that the information that the body must return is in the message

export const parseJsonError: ErrorParser = async (response?: HttpResponse) => {
    if (!response || response.statusCode < 300) {
        return;
    }
    const body = await parseJsonBody(response);
    const sanitizeErrorCode = (rawValue: string | number): string => {
        const [cleanValue] = rawValue.toString().split(/[\,\:]+/);
        if (cleanValue.includes('#')) {
            return cleanValue.split('#')[1];
        }
        return cleanValue;
    };
    const code = sanitizeErrorCode(
        response.headers['x-amzn-errortype'] ??
            body.code ??
            body.__type ??
            'UnknownError'
    );
    const message = body.message ?? body.Message ?? 'Unknown error';
    const error = new Error(message);
    return Object.assign(error, {
        name: code,
        $metadata: parseMetadata(response),
    });
};

But if that's the case, I don't think we should constrain the user body's key, right?

chrisbonifacio commented 3 months ago

Hi @dadaguai-git I believe this is a duplicate of another open issue. We have a fix merged and is only pending release. Please track this issue for updates: https://github.com/aws-amplify/amplify-js/issues/12730

jimblanc commented 2 months ago

The fix for this issue has been released with Amplify JS v6.0.15. Please let us know if you're run into any other issues! Associated documentation can be found here: https://docs.amplify.aws/react/build-a-backend/restapi/fetch-data/#access-http-response-from-errors

dadaguai-git commented 2 months ago

Thank you for your contribution!

metuuu commented 1 week ago

I am having issue where amplify throws error response of

UnknownError: Unknown error
    at parseJsonError (json.mjs:34:19)
    at async parseRestApiServiceError (serviceError.mjs:26:28)
    at async job (createCancellableOperation.mjs:35:23)

when the response has content-type of "text/plain; charset=utf-8" Amplify Version: v6.0.30

metuuu commented 1 week ago

Or are this discussion just for AWS error handling? If I return my own errors from api should I read the error content from error._response instead?