Skip to content

ODataControllerBase

Tomas Fabian edited this page Oct 5, 2021 · 5 revisions

ODataControllerBase (v3.0.0)

List of breaking changes.

  • Patch does not call OnPut anymore but OnPatch.
  public interface IUser
  {
    string UserId { get; }
  }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Joker.Contracts.Data;
using Joker.Domain;
using Joker.OData.Controllers;
using Microsoft.AspNet.OData;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
  [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "ECommerceUsers", Roles = "User")]
  public class ODataControllerWithAuth<TEntity> : ODataControllerBase<TEntity>
    where TEntity : class, IUser, IDomainEntity
  {
    private readonly IRepository<TEntity> repository;

    public ODataControllerWithAuth(IRepository<TEntity> repository) 
      : base(repository)
    {
      this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }
    
    protected string IdentityUserId => HttpContext.User.Claims
                                                        .Where(c => c.Type == ClaimTypes.NameIdentifier)
                                                        .Select(c => c.Value)
                                                        .SingleOrDefault();

    protected override Task<IActionResult> ValidatePostAsync(TEntity entity)
    {
      var result = ValidateUserId(entity);

      if (result != null)
        return result;

      return base.ValidatePostAsync(entity);
    }

    protected Task<IActionResult> ValidateUserId(TEntity entity)
    {
      if (entity.UserId != IdentityUserId)
      {
        IActionResult unauthorizedResult = Unauthorized();

        return Task.FromResult(unauthorizedResult);
      }

      return null;
    }

    protected override Task<IActionResult> ValidatePutAsync(TEntity entity)
    {
      var result = ValidateUserId(entity);

      if (result != null)
        return result;

      return base.ValidatePutAsync(entity);
    }

    protected override Task<IActionResult> ValidatePatchAsync(Delta<TEntity> delta, TEntity entity)
    {
      var result = ValidateUserId(entity);

      if (result != null)
        return result;

      return base.ValidatePatchAsync(delta, entity);
    }

    protected override async Task<IActionResult> ValidateDeleteAsync(object[] keys)
    {
      int id =  (int)keys[0];

      var entity = await repository.GetAll().FirstOrDefaultAsync(c => c.Id == id);

      var result = ValidateUserId(entity);

      if (result != null)
        return await result;
      
      return await base.ValidateDeleteAsync(keys);
    }
  }

Full OData operations regardless of the key type

  public class ProductsController : ODataControllerBase<Product>
  {
    public ProductsController(IRepository<Product> repository) 
      : base(repository)
    {
    }
  }

Key is extracted from KeyAttribute

  [Microsoft.OData.Client.Key(nameof(Id))]
  public class Product : Joker.Domain.DomainEntity<string>
  {
  }

Intercepting or overriding of base functionalities

  public class ProductsController : ODataControllerBase<Product>
  {
    public ProductsController(IRepository<Product> repository) 
      : base(repository)
    {
    }

    protected override IQueryable<Product> OnGetAll(ODataQueryOptions<Product> queryOptions)
    {
      return base.OnGetAll(queryOptions);
    }

    protected override Task<int> OnPost(Product entity)
    {
      return base.OnPost(entity);
    }

    protected override Task<int> OnPut(Product entity)
    {
      return base.OnPut(entity);
    }

    protected override Task<int> OnDelete(params object[] keys)
    {
      return base.OnDelete(keys);
    }

    protected override dynamic TryGetDbSet(Type entityType)
    {
      return base.TryGetDbSet(entityType);
    }

    protected override Task<IActionResult> OnCreateRef(string navigationProperty, Uri link)
    {
      return base.OnCreateRef(navigationProperty, link);
    }

    protected override Task<IActionResult> OnDeleteRef(string navigationProperty)
    {
      return base.OnDeleteRef(navigationProperty);
    }
  }