Azure Functions HTTP Trigger Yapılandırması ve En İyi Uygulamalar

Serverless dünyasına adım attığınızda, Azure Functions’ın HTTP Trigger özelliği muhtemelen ilk karşılaşacağınız ve en sık kullanacağınız yapı taşlarından biri olacak. Bir API endpoint’i kurmak, webhook almak ya da basit bir form gönderimini işlemek istediğinizde HTTP Trigger devreye giriyor. Bu yazıda, gerçek dünya senaryoları üzerinden Azure Functions HTTP Trigger yapılandırmasını tüm ayrıntılarıyla ele alacağız.

HTTP Trigger Nedir ve Neden Kullanırız?

Azure Functions’ta bir “trigger”, fonksiyonunuzun ne zaman çalışacağını belirleyen tetikleyicidir. HTTP Trigger, adından da anlaşılacağı gibi, bir HTTP isteği geldiğinde fonksiyonunuzu ayağa kaldırır. Klasik bir web sunucusu veya API gateway kurmanıza gerek kalmadan, doğrudan kod yazıp deploy edebiliyorsunuz.

Bir sysadmin olarak bunu şöyle düşünebilirsiniz: Eskiden bir webhook almak için Nginx kurardınız, uygulama deploy ederdiniz, process manager ayarlardınız, SSL sertifikası bağlardınız. Şimdi sadece bir fonksiyon yazıp “git” diyorsunuz. Ölçekleme, yüksek erişilebilirlik, SSL, bunların hepsi Azure’un sorunu haline geliyor.

Geliştirme Ortamını Kurmak

Başlamadan önce local geliştirme ortamınızı hazırlamanız gerekiyor. Azure Functions Core Tools ve Azure CLI olmadan bu işlerin içinden çıkmak çok zorlaşıyor.

# Azure Functions Core Tools kurulumu (Ubuntu/Debian)
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg

sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list'

sudo apt-get update
sudo apt-get install azure-functions-core-tools-4

# Azure CLI kurulumu
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Kurulumu doğrula
func --version
az --version

Node.js ile çalışacaksanız npm üzerinden de kurabilirsiniz:

# npm ile kurulum
npm install -g azure-functions-core-tools@4 --unsafe-perm true

# Python sanal ortamı oluşturma (Python fonksiyonları için)
python3 -m venv .venv
source .venv/bin/activate
pip install azure-functions

İlk HTTP Trigger Fonksiyonunu Oluşturmak

Azure’a giriş yapıp yeni bir Functions projesi oluşturalım:

# Azure'a giriş
az login

# Resource group oluştur
az group create 
  --name rg-functions-demo 
  --location westeurope

# Storage account oluştur (Functions için zorunlu)
az storage account create 
  --name stfunctionsdemo2024 
  --resource-group rg-functions-demo 
  --location westeurope 
  --sku Standard_LRS

# Function App oluştur (Python runtime ile)
az functionapp create 
  --resource-group rg-functions-demo 
  --consumption-plan-location westeurope 
  --runtime python 
  --runtime-version 3.11 
  --functions-version 4 
  --name func-demo-httpapi 
  --storage-account stfunctionsdemo2024

Şimdi local’de proje iskeletini oluşturalım:

# Yeni Functions projesi başlat
func init MyHttpApi --python

cd MyHttpApi

# HTTP Trigger fonksiyonu ekle
func new --name ProcessWebhook --template "HTTP trigger" --authlevel "function"

Bu komuttan sonra ProcessWebhook adında bir klasör oluşur ve içinde __init__.py ile function.json dosyaları bulunur.

function.json Yapılandırması

function.json dosyası, fonksiyonunuzun davranışını tanımladığınız yerdir. HTTP Trigger için bu dosya kritik öneme sahip:

cat ProcessWebhook/function.json

Varsayılan içerik şöyle görünür ve bunu ihtiyaçlarınıza göre düzenleyebilirsiniz:

# function.json içeriği - bu bir JSON dosyasıdır
# Aşağıdaki yapıyı ProcessWebhook/function.json dosyasına yazın:
cat > ProcessWebhook/function.json << 'EOF'
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"],
      "route": "webhook/process"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}
EOF

authLevel değerleri ve anlamları:

  • anonymous: Herkes erişebilir, key gerekmez
  • function: Fonksiyon bazlı API key gerektirir
  • admin: Master key gerektirir, yönetim işlemleri için

methods dizisindeki değerler:

  • get: GET isteklerini kabul eder
  • post: POST isteklerini kabul eder
  • put: PUT isteklerini kabul eder
  • delete: DELETE isteklerini kabul eder
  • patch: PATCH isteklerini kabul eder

route parametresi ile varsayılan URL yapısını değiştirebilirsiniz. Belirtmezseniz URL api/FonksiyonAdi şeklinde olur.

Gerçek Dünya Senaryosu: GitHub Webhook İşleyici

Bir CI/CD pipeline’ınız var ve GitHub’dan gelen webhook’ları işlemek istiyorsunuz. Her push event’inde Slack’e bildirim göndermek, log kayıt tutmak ve gerektiğinde deployment tetiklemek istiyorsunuz.

# requirements.txt dosyasını güncelle
cat > requirements.txt << 'EOF'
azure-functions
requests
hmac
hashlib
EOF
# ProcessWebhook/__init__.py içeriği
cat > ProcessWebhook/__init__.py << 'PYEOF'
import azure.functions as func
import json
import hmac
import hashlib
import requests
import logging
import os

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info("GitHub webhook alındı, işleniyor...")

    # HMAC doğrulama - GitHub secret ile imzayı kontrol et
    github_secret = os.environ.get("GITHUB_WEBHOOK_SECRET", "")
    signature_header = req.headers.get("X-Hub-Signature-256", "")
    
    body = req.get_body()
    
    if github_secret and signature_header:
        expected_sig = "sha256=" + hmac.new(
            github_secret.encode(),
            body,
            hashlib.sha256
        ).hexdigest()
        
        if not hmac.compare_digest(expected_sig, signature_header):
            logging.warning("Geçersiz webhook imzası!")
            return func.HttpResponse(
                json.dumps({"error": "Unauthorized"}),
                status_code=401,
                mimetype="application/json"
            )
    
    # Event tipini belirle
    event_type = req.headers.get("X-GitHub-Event", "unknown")
    
    try:
        payload = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Invalid JSON payload"}),
            status_code=400,
            mimetype="application/json"
        )
    
    # Push event işleme
    if event_type == "push":
        repo_name = payload.get("repository", {}).get("full_name", "unknown")
        pusher = payload.get("pusher", {}).get("name", "unknown")
        branch = payload.get("ref", "").replace("refs/heads/", "")
        commits = payload.get("commits", [])
        
        message = f"*Yeni Push!* {repo_name}nBranch: {branch}nKim: {pusher}nCommit sayısı: {len(commits)}"
        
        # Slack bildirimi gönder
        slack_webhook = os.environ.get("SLACK_WEBHOOK_URL", "")
        if slack_webhook:
            requests.post(slack_webhook, json={"text": message}, timeout=10)
        
        logging.info(f"Push event işlendi: {repo_name}/{branch}")
        
        return func.HttpResponse(
            json.dumps({"status": "success", "processed": event_type}),
            status_code=200,
            mimetype="application/json"
        )
    
    return func.HttpResponse(
        json.dumps({"status": "ignored", "event": event_type}),
        status_code=200,
        mimetype="application/json"
    )
PYEOF

Environment Variable ve Application Settings Yönetimi

Local geliştirmede local.settings.json kullanırken, production’da Azure Portal veya CLI üzerinden Application Settings tanımlarsınız.

# local.settings.json yapılandırması
cat > local.settings.json << 'EOF'
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "GITHUB_WEBHOOK_SECRET": "supersecretkey123",
    "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/xxx/yyy/zzz"
  }
}
EOF

# Production'a settings ekle (CLI ile)
az functionapp config appsettings set 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --settings 
    "GITHUB_WEBHOOK_SECRET=supersecretkey123" 
    "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz"

# Mevcut settings'leri listele
az functionapp config appsettings list 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --output table

Önemli not: local.settings.json dosyasını asla Git’e commit etmeyin. .gitignore dosyanıza ekleyin.

CORS Yapılandırması

Frontend uygulamanız farklı bir domain’den bu API’yi çağıracaksa CORS ayarlarını yapmanız gerekiyor:

# CORS ayarlarını yapılandır
az functionapp cors add 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --allowed-origins "https://myapp.azurewebsites.net" "https://www.mycompany.com"

# Mevcut CORS ayarlarını gör
az functionapp cors show 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo

# Development için tüm origin'lere izin ver (sadece test ortamı!)
az functionapp cors add 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --allowed-origins "*"

Local geliştirmede CORS’u test etmek için local.settings.json‘a şunu ekleyebilirsiniz:

# local.settings.json'a CORS host ekle
az functionapp cors add 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --allowed-origins "http://localhost:3000"

Fonksiyonu Deploy Etmek ve Test Etmek

# Local'de çalıştır ve test et
func start

# Başka bir terminalde test isteği gönder
curl -X POST http://localhost:7071/api/webhook/process 
  -H "Content-Type: application/json" 
  -H "X-GitHub-Event: push" 
  -d '{"repository": {"full_name": "myorg/myrepo"}, "pusher": {"name": "ahmet"}, "ref": "refs/heads/main", "commits": [{"id": "abc123"}]}'

# Azure'a deploy et
func azure functionapp publish func-demo-httpapi

# Deploy sonrası fonksiyon URL'ini al
az functionapp function show 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --function-name ProcessWebhook 
  --query "invokeUrlTemplate" 
  --output tsv

# Fonksiyon key'ini al
az functionapp keys list 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo

Gerçek Dünya Senaryosu 2: REST API Endpoint Zincirleme

Daha karmaşık bir senaryo: Bir e-ticaret sisteminde sipariş alındığında stok kontrolü, ödeme doğrulama ve bildirim gönderme işlemlerini sırayla yapan bir HTTP Trigger.

# OrderProcessor/__init__.py
cat > OrderProcessor/__init__.py << 'PYEOF'
import azure.functions as func
import json
import logging
import os
import requests
from datetime import datetime

def validate_order(order_data: dict) -> tuple:
    """Sipariş verilerini doğrula"""
    required_fields = ["product_id", "quantity", "customer_email", "payment_token"]
    
    for field in required_fields:
        if field not in order_data:
            return False, f"Eksik alan: {field}"
    
    if order_data["quantity"] <= 0:
        return False, "Miktar sıfırdan büyük olmalı"
    
    return True, "OK"

def check_stock(product_id: str, quantity: int) -> bool:
    """Stok API'sini kontrol et"""
    stock_api = os.environ.get("STOCK_API_URL", "")
    if not stock_api:
        logging.warning("STOCK_API_URL tanımlı değil, stok kontrolü atlandı")
        return True
    
    try:
        response = requests.get(
            f"{stock_api}/check/{product_id}",
            params={"quantity": quantity},
            timeout=5
        )
        return response.json().get("available", False)
    except requests.RequestException as e:
        logging.error(f"Stok kontrolü başarısız: {e}")
        return False

def main(req: func.HttpRequest) -> func.HttpResponse:
    
    # Sadece POST kabul et
    if req.method != "POST":
        return func.HttpResponse(
            json.dumps({"error": "Sadece POST metodu kabul edilir"}),
            status_code=405,
            mimetype="application/json",
            headers={"Allow": "POST"}
        )
    
    try:
        order_data = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Geçersiz JSON formatı"}),
            status_code=400,
            mimetype="application/json"
        )
    
    # Sipariş doğrulama
    is_valid, message = validate_order(order_data)
    if not is_valid:
        return func.HttpResponse(
            json.dumps({"error": message, "status": "validation_failed"}),
            status_code=422,
            mimetype="application/json"
        )
    
    # Stok kontrolü
    if not check_stock(order_data["product_id"], order_data["quantity"]):
        return func.HttpResponse(
            json.dumps({"error": "Yetersiz stok", "status": "out_of_stock"}),
            status_code=409,
            mimetype="application/json"
        )
    
    # Sipariş ID oluştur
    order_id = f"ORD-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}-{order_data['product_id'][:4].upper()}"
    
    logging.info(f"Sipariş oluşturuldu: {order_id}")
    
    return func.HttpResponse(
        json.dumps({
            "status": "success",
            "order_id": order_id,
            "message": "Siparişiniz alındı",
            "estimated_delivery": "3-5 iş günü"
        }),
        status_code=201,
        mimetype="application/json"
    )
PYEOF

Monitoring ve Logging

Azure Functions’ta Application Insights entegrasyonu monitoring açısından çok değerli. Kurulum ve temel sorguları ele alalım:

# Application Insights oluştur
az monitor app-insights component create 
  --app func-demo-insights 
  --location westeurope 
  --resource-group rg-functions-demo 
  --application-type web

# Instrumentation key al
INSTRUMENTATION_KEY=$(az monitor app-insights component show 
  --app func-demo-insights 
  --resource-group rg-functions-demo 
  --query "instrumentationKey" 
  --output tsv)

# Function App'e bağla
az functionapp config appsettings set 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --settings "APPINSIGHTS_INSTRUMENTATIONKEY=$INSTRUMENTATION_KEY"

# Fonksiyon loglarını canlı izle
func azure functionapp logstream func-demo-httpapi

# Az CLI ile son execution'ları listele
az monitor app-insights query 
  --app func-demo-insights 
  --resource-group rg-functions-demo 
  --analytics-query "requests | where timestamp > ago(1h) | order by timestamp desc | take 20"

Güvenlik: IP Kısıtlama ve API Key Yönetimi

Production ortamında güvenliği sıkılaştırmak için birkaç katmanlı yaklaşım kullanmalısınız:

# Belirli IP'lere erişim kısıtlama
az functionapp config access-restriction add 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --rule-name "OfficeNetwork" 
  --action Allow 
  --ip-address 203.0.113.0/24 
  --priority 100

# GitHub webhook IP aralıklarını ekle
az functionapp config access-restriction add 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --rule-name "GitHubWebhooks" 
  --action Allow 
  --ip-address 192.30.252.0/22 
  --priority 200

# Kısıtlamaları listele
az functionapp config access-restriction show 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo

# Yeni host key oluştur (API key rotasyonu için)
az functionapp keys set 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --key-name "partner-integration-key" 
  --key-type functionKeys 
  --function-name ProcessWebhook

# Eski key'i sil
az functionapp keys delete 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --key-name "old-partner-key" 
  --key-type functionKeys 
  --function-name ProcessWebhook

Yaygın Sorunlar ve Çözümleri

Gerçek ortamlarda sıkça karşılaşılan sorunları ve çözüm yollarını ele alalım.

Cold Start problemi: Consumption plan kullandığınızda, uzun süre istek gelmezse fonksiyon uyku moduna geçer. İlk istek geldiğinde başlatma süresi 2-5 saniyeye kadar çıkabilir.

Çözüm için Premium plan veya “Always On” özelliği olan bir plan kullanabilirsiniz:

# Premium plana geçiş
az functionapp update 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --set siteConfig.alwaysOn=true

# Minimum instance sayısını ayarla (Premium plan)
az functionapp config set 
  --name func-demo-httpapi 
  --resource-group rg-functions-demo 
  --minimum-elastic-instance-count 1

Timeout ayarları: Varsayılan timeout Consumption plan’da 5 dakika, Premium’da 30 dakikadır. host.json ile değiştirebilirsiniz:

cat > host.json << 'EOF'
{
  "version": "2.0",
  "functionTimeout": "00:10:00",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "maxTelemetryItemsPerSecond": 20
      }
    }
  },
  "extensions": {
    "http": {
      "routePrefix": "api",
      "maxOutstandingRequests": 200,
      "maxConcurrentRequests": 100,
      "dynamicThrottlesEnabled": true
    }
  }
}
EOF

Rate limiting: maxOutstandingRequests ve maxConcurrentRequests parametreleri ile fonksiyonunuza gelen istek yükünü kontrol edebilirsiniz. Aşıldığında Azure otomatik olarak 429 döner.

Sonuç

Azure Functions HTTP Trigger, doğru yapılandırıldığında inanılmaz derecede güçlü ve esnek bir araç. Bu yazıda ele aldığımız konuları özetlemek gerekirse:

  • Geliştirme ortamı kurulumu ve Core Tools kullanımı production kalitesinde iş yapmanın temelidir
  • function.json içindeki authLevel ve route parametrelerini iş gereksinimlerinize göre dikkatli şekilde ayarlayın
  • Environment variable’ları asla koda gömmeyín, Application Settings kullanın
  • CORS, IP kısıtlama ve API key yönetimini birlikte kullanarak katmanlı güvenlik oluşturun
  • Application Insights entegrasyonunu en başından kurun, sonradan “keşke takip etseydim” dememeniz için
  • Cold start sorununu göz ardı etmeyin, SLA gereksinimlerinize göre plan seçimini yapın
  • host.json üzerinden concurrency ve timeout ayarlarını üretim trafiğinize göre optimize edin

Serverless geçişin en büyük tuzağı, “sadece kod yazıyoruz, altyapı yok” yanılgısına düşmektir. Altyapı var, sadece soyutlanmış. Bu soyutlamanın altında ne olduğunu anlayan sysadmin, hem daha sağlam sistemler kurar hem de sorunlar çıktığında çok daha hızlı çözer. Azure Functions HTTP Trigger için söylenen her şey bu ilke etrafında şekilleniyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir