Deoarece incerc sa pastrez controllerul cat mai curat, ce contine un singur request catre serviciu si un log, toate validarile le-am mutat in serviciu, iar daca vre’una nu este indeplinita, atunci arunc o ApplicationException cu un ErrorHandler convertit in string.
Cum arata o metoda din Controller:
[HttpPost]
public async Task<IActionResult> Create([FromBody] DataSource model)
{
Data.Entities.User user = null;
string className = nameof(DataSourcesController);
string actionName = nameof(DataSourcesController.Create);
string ip = string.Empty;
string msg = string.Empty;
try
{
var requestData = await GetRequestData();
user = requestData.CurrentUser;
ip = requestData.IpAddress;
var createdModel = await dataSourcesService.Create(model, user.Id);
msg = "Created new data source";
await loggerService.LogInfo(className, actionName, msg, user, ip);
return Ok(createdModel);
}
catch (Exception ex)
{
return await HandleError(ex, className, actionName, user, ip);
}
}
private async Task<IActionResult> HandleError(Exception ex, string className, string actionName, Data.Entities.User user, string ip)
{
try
{
if (ex.GetType() == typeof(ApplicationException))
{
var err = JsonSerializer.Deserialize<ErrorType>(ex.Message);
var clientMsg = !string.IsNullOrEmpty(err.ClientMessage) ? err.ClientMessage : err.ServerMessage;
await loggerService.LogWarning(className, actionName, err.ServerMessage, user, ip);
if (err.StatusCode == 400)
{
return BadRequest(clientMsg);
}
else if (err.StatusCode == 409)
{
return StatusCode(409, clientMsg);
}
}
else
{
await loggerService.LogError(className, actionName, ex.Message, user, ip);
}
}
catch { }
return StatusCode(500);
}
Serviciul:
public async Task<DataSource> Create(DataSource model, int userId)
{
await packagesService.CanCreateDataSource(userId);
await CheckNameAvailable(model.Id, model.Name, userId);
var newEntity = new Data.Entities.DataSource()
{
Name = model.Name,
};
dataContext.Add(newEntity);
await dataContext.SaveChangesAsync();
return await GetById(newEntity.Id);
}
public async Task<bool> CanCreateDataSource(int userId)
{
var status = await GetStatus(userId);
if (status.CurrentDataSources < status.MaxDataSources)
{
return true;
}
var error = new ErrorType()
{
StatusCode = 403,
ClientMessage = $"You have reached the limit of allowed data sources for your package.",
ServerMessage = $"User #{userId} reached limit of allowed data sources."
};
throw new ApplicationException(JsonSerializer.Serialize(error));
}
De exemplu, inainte sa creez entitatea, verific prin metoda CanCreateDataSource daca operatiunea este valida. Metoda respectiva returneaza “true” (ceea ce ar trebui sa fie un void de fapt, pentru ca nu fac nimic cu rezultatul) sau arunca o exceptie ca mai sus. O alta problema urata este ca obiectul pe care il trimit ca exceptie trebuie convertit in string (ApplicationException accepta doar string), iar in handlerul controllerului, verific tipul exceptiei, dupa care convertesc stringul in obiect. Am facut asta pentru ca am nevoie ca in db sa salvez un mesaj al erorii, iar in client sa trimit un alt mesaj, “personalizat”.
Am destul de multe validari care arunca exceptii tocmai pentru ca le-am mutat in servicii, si nu puteam returna direct de acolo tipuri de HttpResponse. Mi s-a parut cea mai buna solutie gasita pana acum. Ce parere aveti? Ma gandesc ca la un numar mai mare de requesturi sa nu fie probleme cand serverul va ajunge sa primeasca zeci de ApplicationExceptions pe zi