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."
]
}
}
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
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'
})
)
}
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);
});
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>
}
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:
Bônus: como exibir mensagens de validação globais? (que não estão associadas a nenhum campo específico)
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. 📍"
]
}
}
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>
</>
}
/>
Me avisa nos comentários se precisar de alguma ajuda.
Top comments (0)