DEV Community

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.

Top comments (0)