Návratové kódy a typy - verisoftCZ/verisoft-framework GitHub Wiki

#Návratové kódy a typy Na controlleru pomocí atributů nastavím společné návratové kódy.

[Authorize]
[ApiController]
[ApiVersion("1.0")]
[Route("v{v:apiVersion}/[controller]")]
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(UnauthorizedResult))]
[ProducesResponseType(StatusCodes.Status404BadRequest, Type = typeof(BadRequestResult))]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public class CustomerController(ICustomerService customerService)
{
    // ... endpoints
}
  • na jednotlivých endpointech specifikuji návratové kódy, které se od controlleru odchylují
  • pokud endpoint vrací něco jiného než 200, je návratový typ metody IActionResult -- IActionResult získáme pomocí extension metody ToActionResult pro Core.Contracts.BusinessResult.BusinessActionResult alternativně Core.Contracts.BusinessResult.BusinessActionResult
  • v ProducesResponseType atributu se Type nastaví na -- 200 - contract, který je výsledkem úspěšného volání service -- 422 - Core.Contracts.BusinessResult.BusinessActionErrors<contract z předchozí odrážky> -- Vše ostatní - Core.Contracts.BusinessResult.BusinessActionErrors
[HttpPut]
[Route("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Customer))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(BusinessActionErrors))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(BusinessActionErrors))]
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity, Type = typeof(BusinessActionErrors<Customer>))]
public async Task<IActionResult> UpdateCustomerAsync(CustomerEditModel customerEditModel, int id)
{
   var result = await customerService.UpdateCustomerAsync(customerEditModel, id);
   return result.ToActionResult();
}

ToActionResult() Extension metoda nad Core.Common.BusinessResult.BusinessActionResult

  • Pokud je naplněna property Data objektem, který je implementací interface IValidatedObject a tento objekt má v property ValidationErrors nějaký element => UnprocessableEntityObjectResult(new BusinessActionErrors(Data, Errors)) (422)
  • Pokud je naplněna property Data => OkObjectResult(Data) (200)
  • Pokud je v property Errors první Error s Code = BusinessResultCodes.EntityNotFound => NotFoundObjectResult(Errors) (404)
  • Pokud je v property Errors první Error s Code = BusinessResultCodes.ForbiddenErrorCode => ForbiddenObjectResult(Errors) (403)
  • Pokud je v property Errors první Error s Code = BusinessResultCodes.BadRequestErrorCode => BadRequestObjectResult(Errors) (400)
  • Pokud je v property Errors první Error s Code = BusinessResultCodes.NullInputErrorCode => BadRequestObjectResult(Errors) (400)
  • Ostatní případy => UnprocessableEntityObjectResult(Errors) (422)

Metoda se switchem, který generuje IActionResult:

private static IActionResult GetErrorResult<T>(string errorCode, BusinessActionResult<T> result, IDictionary<string, IActionResult> customCases)
{
    var defaultCases = new Dictionary<string, IActionResult>
    {
            { ErrorFactory.NotFoundErrorCode, new NotFoundObjectResult(result.Errors) },
            { ErrorFactory.ForbiddenErrorCode, new ForbiddenObjectResult(result.Errors) },
            { ErrorFactory.BadRequestErrorCode, new BadRequestObjectResult(result.Errors) },
            { ErrorFactory.NullInputErrorCode, new BadRequestObjectResult(result.Errors) { ContentTypes = HttpProblemDetailsStandard() } },
    };

    var switchCases = customCases != null
        ? defaultCases.Concat(customCases).ToDictionary(kv => kv.Key, kv => kv.Value)
        : defaultCases;

    return switchCases.TryGetValue(errorCode, out var actionResult)
        ? actionResult
        : result.Data != null
            ? new UnprocessableEntityObjectResult(new BusinessActionErrors<T>(result.Data, result.Errors)) }
            : new UnprocessableEntityObjectResult(result.Errors) };
}

Při volání metody ToActionResult() mohu rozšířit switch o další http result kódy nebo o další Error kódy pomocí Dictionary:

[HttpPut]
[Route("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Customer))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(BusinessActionErrors))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(BusinessActionErrors))]
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity, Type = typeof(BusinessActionErrors<Customer>))]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status302Found)]
public async Task<IActionResult> UpdateCustomerAsync(CustomerEditModel customerEditModel, int id)
{
   var result = await customerService.UpdateCustomerAsync(customerEditModel, id);
   var customActionResults = new Dictionary<string, IActionResult>
   {
      ["CustomErrorCode"] = new RedirectResult("https://www.redirect-url.com"),
      ["CustomErrorCode2"] = new AcceptedResult(),
   };

   return result.ToActionResult(customActionResults);
}

Způsob jak korektně do controlleru vrátit BusinessActionResult je popsán v článku Validace

⚠️ **GitHub.com Fallback** ⚠️