#c_sharp #шаблоны_проектирования #инспекция_кода #rest #aspnet_web_api
Есть вот такая сущность:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string CategoryName { get; set; } //доступно для редактирования только
пользователю «admin»
// в реальном проекте свойств больше
// и среди них также есть доступные для редактирования только для admin
}
Необходимо на стороне сервера создать метод PUT, который обеспечил бы редактирование
свойства CategoryName только для авторизованного пользователя admin.
Какие практики REST существуют для реализации подобном логики?
Моя реализация такова:
public class ProductDto
{
[Required]
public string Name { get; set; }
[Required]
public string CategoryName { get; set; }
}
[Authorize]
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpPut]
[Route("{id}")]
public HttpResponseMessage Put([FromBody] ProductDto productDto)
{
if (!ModelState.IsValid)
{
throw new ArgumentException("dto");
}
var productId = RequestContext.RouteData.Values["id"].ToString();
var userName = User.Identity.Name; //извлекаем имя авторизованного пользователя
var isAdmin = userName == "admin";
_productService.Update(productId, productDto, isAdmin);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
public interface IProductService
{
void Update(string id, ProductDto dto, bool isAdmin);
}
public class ProductService : IProductService
{
private readonly IGenericRepository _productRepository;
public ProductService(IGenericRepository productRepository)
{
_productRepository = productRepository;
}
public void Update(string id, ProductDto dto, bool isAdmin)
{
if (dto == null || id == null)
{
throw new ArgumentNullException();
}
var product = _productRepository.GetById(id);
if (product == null)
{
throw new ArgumentException(nameof(id));
}
product.Name = dto.Name;
if(isAdmin)
{
product.CategoryName = dto.CategoryName;
}
_productRepository.Update(product);
}
}
Какими изъянами обладает данный код, учитывая то, что входе масштабирования у сущности
Productбудут появляться новые свойства, среди которых также могут быть те которые редактировать
может только admin.
Или стоило попробовать использовать атрибуты валидации/перезагрузку PUT-метода и т.п?
Ответы
Ответ 1
Привязывать функционал к имени пользователя не лучшее решение в подобных ситуациях. Для разграничения доступа к функционалу есть специальные средства под названием "роли". Каждому зарегистрированному пользователю можно добавить одну или несколько ролей описывающих его права в системе. Исходя из этого следует второе правило, это разделение прав доступа к функционалу на отдельные методы - "action". В вашем случае первый метод будет вызывать обновление ограниченное количество полей модели, второй метод будет обновлять расширенные поля модели. Конечно для этого придется с клиента вызывать разные методы в зависимости от роли пользователя и в случае нехватки уровня доступа клиентский метод возвратит вменяемую ошибку (403) [RoutePrefix("api/test"), Authorize] public class TestController : ApiController { [HttpPut, Route("{id}")] public void PutAll(int id, [FromBody]BaseModelDto model) { } [HttpPut, Route("{id}/admin"), Authorize(Roles = "admin")] public void PutAdmin(int id, [FromBody]ModelDto model) { } } Что такое роли и как с ними работать можно почитать тут https://habrahabr.ru/post/231807/Ответ 2
Потенциальное дублирование кода который проверяет что пользователь admin. Можно сделать ActionFilterAttribute и еще на подходе к методу разрешать/запрещать редактирование.
Комментариев нет:
Отправить комментарий