@liangshiwei Will this fix also work if we are still using abp 8.3.X but need to update our Swashbuckle.AspNetCore to 6.8.1?
Ok, that is how we are doing it. If the related keys are not valid do you typically throw a BusinessException
or a AbpValidationException
? We originally implemented custom BusinessException
classes but are thinking of swapping to an AbpValidationException
for consistency with the other errors.
Thank you. I have one more follow-up related to this. In our Domain Managers we confirm that key references to other entities are valid. In some cases these are loose relationships to entities defined in other modules. For example, a Customer may have a SalesRepId. Currently we verify these are valid Ids in our domain manager CreateAsync and UpdateAsync using repositories for entities in the same module and app services for those in other modules and throw an exception if they are not. Is there a recommended approach to this?
We specifically used this method of implementation because the Validation documentation recommends not to add this type of validation on the Dto. https://docs.abp.io/en/abp/latest/Validation#resolving-a-service
Ok, I thought we might be overlooking a more automatic way to take care of it. If our fluent validation rules were also on the Dto and not just the domain object would the app service automatic validation also apply those rules? Is duplicating the rules from the domain in that way recommended so we don't have to explicitly call await _objectValidator.ValidateAsync(entity);
?
Thank you for the confirmation. I made this change and it is working as expected now.
We are using AddDomainTenantResolver to use a wildcard subdomain to identify our tenants. This is configured in the manner of the following example:
Configure<AbpTenantResolveOptions>(options =>
{
options.AddDomainTenantResolver("{0}.app.mydomainhere.com");
});
When our users request a password reset, the link is on the base domain and the __tenant in the query string is ignored, so they get a 404 User Id not found message. Is the recommended way to deal with this to set the App SelfUrl to https://{{tenantName}}.app.mydomainhere.com
or is there some other way for this to be handled? I could not find any documentation on the recommended setting for SelfUrl so currently it is set to the base domain https://app.mydomainhere.com
.
In our AppService methods like CreateAsync and UpdateAsync we have custom logic that may throw an AbpValidationException
. We also have fluent validation logic on Domain entities that may throw an AbpValidationException
where we explicitly validate the entity after mapping using IObjectValidator.ValidateAsync. Here is an example UpdateAsync method showing the common pattern we are using:
public async Task UpdateAsync(Guid id, UpdateCustomerDto input)
{
var entity = await _customerRepository.GetAsync(id);
entity = ObjectMapper.Map<UpdateCustomerDto, Customer>(input, entity);
entity = await _customerManager.UpdateAsync(
entity
);
// Validate the entity.
await _objectValidator.ValidateAsync(entity);
await _customerRepository.UpdateAsync(entity);
}
When we call this AppService method via REST api we get the expected behavior. Any AbpValidationException
causes a 400 and shows the validation messages in the api JSON response. However, we are experiencing an issue in our Razor Page UI that injects this AppService and calls UpdateAsync in a Post page handler.
In our Razor Page, the user experience we want when an AbpValidationException
is thrown should be the same as a traditional ModelState validation error. The default behavior shows the user a 400 error page. We have tried catching the AbpValidationException
in our page handler and adding the errors to the ModelState. This gives us the desired UI behavior but has other side effects. Here is an example page handler implementation.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
try
{
await _customerAppService.UpdateAsync(CustomerGuid, EditCustomer);
}
catch (AbpValidationException ex)
{
foreach (var error in ex.ValidationErrors)
{
ModelState.AddModelError($"EditCustomer.{error.MemberNames.FirstOrDefault()}", error.ErrorMessage);
}
return Page();
}
return RedirectToPage("Index");
}
The problem we are experiencing with this implementation is that even though the exception was thrown, because we catch it, the changes are still being saved to the database. This is being caused because we are mapping to the domain entity then validating it. At this point we are stuck on how to get our desired user experience without allowing the entity to be incorrectly saved.
What guidance or advise do you have on how to properly implement our use case?
Thank you for the ideas. After making the post, I saw some documentation about ICurrentPrincipalAccessor.Change
https://docs.abp.io/en/abp/latest/CurrentUser#changing-the-current-principal
Could this also be used in my case and allow the entities to have a proper audit history of the user that created the entity?
In our application we allow users to create many records by importing from a file. Because this file may take a long time to process, the creation of the records is performed from a BackgroundJob. Unfortunately, we are then unable to call any app service methods that require authorization. We really want the file to process under the permissions of the user who uploaded it.
Is there something comparable to using (CurrentTenant.Change(tenantId)) { }
for setting the CurrentUser? If not that, then what is the recommended approach for dealing with long running actions so that they run in the context of the user performing the action? This is just one example of a number of possible long running actions in our application.