DEV Community

3 1

Exibindo erros de validação no servidor - Asp Core Web API + Axios + React Hook Form

Cenário

A partir do ASP.NET CORE 2.1, erros de validação no servidor, são enviados para o client no formato Validation Problem Details, que segue uma estrutura semelhante a seguir:

{
   "type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
   "title":"One or more validation errors occurred.",
   "status":400,
   "traceId":"|52643794-491d9e1d05c828e6.",
   "errors":{
      "Cnpj":[
         "'Cnpj' must not be empty."
      ],
      "CompanyPublicName":[
         "'Company Public Name' must not be empty."
      ],
      "CompanyInternalName":[
         "'Company Internal Name' must not be empty."
      ]
   }
}
Enter fullscreen mode Exit fullscreen mode

Perceba que na response, há a propriedade errors, contendo as propriedades com falhas de validação.

Estou utilizando Axios para requisições HTTP, e para checar, se o retorno da requisição é um feedback de validação, eu checo se há a presença do response header:

Content-Type: application/problem+json; charset=utf-8
Enter fullscreen mode Exit fullscreen mode

Sendo assim, vejamos meu bloco try...catch responsável por invocar o Axios:

try {
    let response = await AxiosInstance.post(
        '/general/companies', {}, {}
    )
} catch (e) {

    const serverSideErrors = e.response.data.errors;

    // Faz iteração nas propriedades do objeto Errors, e usa o método 
    // *SetError* do ReactHookForm, para informar um erro.
    Object.entries(serverSideErrors).map(([key, value]) =>
        setError(key, {
            message: value,
            type: 'serverSide'
        })
    )

}
Enter fullscreen mode Exit fullscreen mode

Axios interceptor:

Agora, antes de tratar o retorno da API, é preciso converter os nomes das propriedades para lowerCamelCase.
Eu faço isso usando um interceptor, ao criar uma instância do Axios.

// Add a response interceptor
instance.interceptors.response.use(function(response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
}, function(error) {
    let newError = Object.assign({}, error);
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    if (newError.response.status === 400) {
        if (newError.response.headers['content-type'].includes('application/problem+json')) {
            /*
            Rename Problem Json response field names, to lower camel case
             */
            let serverData = newError.response.data;

            let errorsLowerCamelCase = Object.fromEntries(
                Object.entries(serverData.errors).map(([key, value]) =>
                    // Modify key here
                    [`${stringUtils.toLowerCamelCase(key)}`, value]
                )
            )

            newError.response.data.hasValidationErrors = true;
            newError.response.data.errors = errorsLowerCamelCase;
        }
    }
    return Promise.reject(newError);
});
Enter fullscreen mode Exit fullscreen mode

Conclusão

Para renderizar os erros inline, estou usando bootstrap form-group, e o component ErrorMessage do próprio react-hook-form.

<ErrorMessage name="companyInternalName"
              errors={errors}
              render={({message}) =>
                <div className={'field-error text-danger'}>{message}</div>
              }
Enter fullscreen mode Exit fullscreen mode

Configuro esse componente pra cada campo do formulário, e ao fazer submit do formulário, uma requisição HTTP é feita, o retorno é tratado, e o resultado é exibido abaixo:

Alt Text

Bônus: como exibir mensagens de validação globais? (que não estão associadas a nenhum campo específico)

Alt Text

A mensagem de resposta do servidor, para uma mensagem de validação global, deve ser conforme a seguir:

{
   "type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
   "title":"One or more validation errors occurred.",
   "status":400,
   "traceId":"|526437a6-491d9e1d05c828e6.",
   "errors":{
      "$":[
         "essa mensagem de validação aqui, não está associada a nenhum campo específico. 📍"
      ]
   }
}
Enter fullscreen mode Exit fullscreen mode

Perceba que o identificador do campo, no objeto errors, é um símbolo $ .

Sendo assim, coloquei no topo da tela, um component < ErrorMessage /> , onde o atributo name="$" .
Isso me permite exibir no topo do formulário, erros globais de validação.

<ErrorMessage name="$"
errors={errors}
render={({message}) =>
  <>
    <FormGroup row>
      <Col md="4">
        <span>⚠️ Atenção:</span>
</Col>
<Col md="8">
  <div className={'field-error text-danger'}>{message}</div>
</Col>
</FormGroup>
</>
}
/>
Enter fullscreen mode Exit fullscreen mode

Me avisa nos comentários se precisar de alguma ajuda.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more