> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Pipeline Behaviors

> MediatR IPipelineBehavior zinciri: Exception, Authorization, Validation, Permission

## Zincir

Her Command/Query, Handler'a ulaşmadan önce bir `IPipelineBehavior` zincirinden geçer. Zincir, MediatR'da **kayıt sırasıyla** çalışır. `DependencyInjection.cs`:

```csharp theme={null}
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ExceptionPipelineBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(AuthorizationPipelineBehavior<,>));
//builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationPipelineBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PermissionPipelineBehavior<,>));
```

Etkin sıra: **Exception → Authorization → Validation → Permission → Handler**

```mermaid theme={null}
flowchart LR
    REQ[Request] --> EX[Exception]
    EX --> AZ[Authorization]
    AZ --> VAL[Validation]
    VAL --> PERM[Permission]
    PERM --> H[Handler]
    H --> PERM --> VAL --> AZ --> EX --> RES[Response]
```

<Note>
  `LoggingPipelineBehavior` kayıtlı ama yorum satırına alınmıştır (disabled). Etkinleştirmek için ilgili satırın yorumunu kaldırmak yeterlidir; sıradaki konumu (Authorization'dan sonra) korunur.
</Note>

## 1. ExceptionPipelineBehavior

En dış halka. Bilinen exception'ları olduğu gibi yukarı geçirir (`throw;`), bilinmeyenleri `UndefinedApplicationException`'a sarar. **`IBusMessage` (Hangfire/bus mesajları) sarılmaz** — DLQ/retry kararı altyapıya bırakılır.

```csharp theme={null}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
    try
    {
        return await next();
    }
    catch (Exception ex) when (IsKnownException(ex))
    {
        _logger.LogWarning(ex, "Known exception in {RequestName}", typeof(TRequest).Name);
        throw;   // bilinen exception → olduğu gibi bubble-up
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Unhandled exception in {RequestName}. Request: {@Request}", typeof(TRequest).Name, request);

        if (request is IBusMessage)   // bus mesajlarında wrap yok
            throw;

        throw new UndefinedApplicationException("Beklenmeyen bir hata oluştu.", ex);
    }
}

private static bool IsKnownException(Exception ex) =>
    ex is FluentValidation.ValidationException
    || ex is DomainException
    || ex is ApplicationException
    || ex is SmsServiceException
    || ex is EmailServiceException;
```

<Tip>
  Bilinen exception'lar API'deki ProblemDetails katmanına ulaşır ve doğru HTTP koduna çevrilir: `DomainException` → 400, `ValidationException` → 422, `Sms/EmailServiceException` → 503. Yani `ForbiddenException` (bir `DomainException` alt sınıfı) bilinen sayılır ve sarılmadan geçer.
</Tip>

## 2. AuthorizationPipelineBehavior

RBAC ve multi-tenant erişim kontrolü. Yalnızca `IRequirePermissions` veya `IRequireTenantAccess` uygulayan request'lerde devreye girer; aksi halde doğrudan `next()` çağrılır. Kullanıcı bağlamını `ICurrentUserService` (BuildingBlocks.Keycloak) üzerinden okur.

```csharp theme={null}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
    bool requiresPermissions   = request is IRequirePermissions;
    bool requiresTenantAccess  = request is IRequireTenantAccess;

    if (!requiresPermissions && !requiresTenantAccess)
        return await next();

    if (!_currentUser.IsAuthenticated)
        throw new ForbiddenException();

    // Permission kontrolü — tüm izinler mevcut olmalı (AND)
    if (request is IRequirePermissions permRequest)
    {
        var missing = permRequest.RequiredPermissions
            .Where(p => !_currentUser.HasPermission(p))
            .ToList();

        if (missing.Count > 0)
            throw new ForbiddenException(missing);
    }

    // Tenant izolasyonu — token tenant'ı ile request tenant'ı örtüşmeli
    if (request is IRequireTenantAccess tenantRequest)
    {
        if (!_currentUser.IsInTenant(tenantRequest.TenantId))
            throw new ForbiddenException("Farklı bir organizasyona ait kaynağa erişim engellendi.");
    }

    return await next();
}
```

Başarısız her durumda `ForbiddenException` fırlatılır; bu da HTTP 403'e dönüşür.

## 3. ValidationPipelineBehavior

İlgili request için kayıtlı tüm `IValidator<TRequest>`'ları paralel çalıştırır. **`IBusMessage`'ı atlar** ve hiç validator yoksa devam eder. Hata varsa tek bir `ValidationException` (tüm failures ile) fırlatılır.

```csharp theme={null}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
    if (request is IBusMessage)
        return await next();

    if (!_validators.Any())
        return await next();

    var context = new ValidationContext<TRequest>(request);

    var validationResults = await Task.WhenAll(
        _validators.Select(v => v.ValidateAsync(context, ct)));

    var failures = validationResults
        .SelectMany(r => r.Errors)
        .Where(f => f != null)
        .ToList();

    if (failures.Count != 0)
    {
        _logger.LogInformation("Validation failed for {RequestName}. Fields: {Fields}",
            typeof(TRequest).Name, failures.Select(f => f.PropertyName).Distinct());

        throw new ValidationException("Bir veya daha fazla doğrulama hatası oluştu.", failures);
    }

    return await next();
}
```

<Info>
  `ValidationException` API tarafında **HTTP 422** + alan bazlı `errors` listesine çevrilir. Validator'ların nasıl yazıldığı için bkz. [Validation & Mapping](/application/validation-mapping).
</Info>

## 4. PermissionPipelineBehavior

`IRequirePermission` (tekil) uygulayan request'lerde JWT `permissions` claim'ini doğrudan `IHttpContextAccessor` üzerinden okur. Bu, API'deki `[RequirePermission]` attribute'una ek bir **defense-in-depth** katmanıdır — özellikle Hangfire job'larından veya event handler'lardan tetiklenen command'lerde değerlidir.

```csharp theme={null}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
    if (request is not IRequirePermission permissionRequest)
        return await next();

    var user = _httpContextAccessor.HttpContext?.User;

    if (user?.Identity?.IsAuthenticated != true)
        throw new DomainException("Bu işlem için kimlik doğrulaması gerekli.");

    var requiredPermission = permissionRequest.RequiredPermission;

    bool hasPermission = user.Claims
        .Where(c => c.Type == "permissions")
        .Any(c => c.Value == requiredPermission || c.Value == "*:*");   // "*:*" = wildcard süper yetki

    if (!hasPermission)
        throw new DomainException($"Bu işlem için '{requiredPermission}' yetkisi gereklidir.");

    return await next();
}
```

<Warning>
  `AuthorizationPipelineBehavior` (`IRequirePermissions`, çoğul) ile `PermissionPipelineBehavior` (`IRequirePermission`, tekil) iki ayrı marker'dır. İlki `ICurrentUserService` soyutlamasını ve `ForbiddenException` (403) üretir; ikincisi ham `HttpContext` claim'lerini okur ve `DomainException` (400) fırlatır. Çoğu admin command'i `IRequirePermissions`'ı tercih eder.
</Warning>

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="Authorization marker'ları" icon="shield-halved" href="/application/authorization">
    IRequirePermissions, IRequireTenantAccess, IRequirePermission detayları.
  </Card>

  <Card title="Validation & Mapping" icon="check-double" href="/application/validation-mapping">
    FluentValidation custom kuralları ve AutoMapper.
  </Card>

  <Card title="Command / Query deseni" icon="code" href="/application/commands-queries">
    Behavior'lardan geçen gerçek handler örnekleri.
  </Card>

  <Card title="Güvenlik" icon="lock" href="/security/overview">
    Çift JWT, RBAC ve token doğrulama mimarisi.
  </Card>
</CardGroup>
