# 26. Hafta — HTTP ve JSON

## Bu hafta ne yapacağız?

Bir REST API'ye gerçek `GET` isteği gönderecek, HTTP durum kodunu kontrol edecek, JSON yanıtını Dart modellerine dönüştürecek ve yükleniyor–başarı–hata ekranlarını yöneteceğiz. Konu uygulaması uzak kullanıcı listesini; özgün uygulama günlük döviz referans kurlarını gösterecek.

## Kazanımlar

- URI ve HTTP `GET` isteği oluşturma
- Durum kodu 200 dışındaki yanıtları hata olarak ele alma
- İsteğe zaman aşımı ekleme
- JSON nesnesi, liste ve iç içe alanları çözümleme
- Ağ verisini tür güvenli Dart modeline dönüştürme
- Servise `http.Client` enjekte ederek kontrollü test yazma
- Hata sonrasında yeniden deneme ve yenileme davranışı oluşturma

## Kurulum

```yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^1.6.0
```

## Çözümlü uygulama — HTTP Kullanıcı Listesi

### 1. Kullanıcı modeli

```dart
class Kullanici {
  const Kullanici({
    required this.id,
    required this.ad,
    required this.eposta,
    required this.sehir,
  });

  final int id;
  final String ad;
  final String eposta;
  final String sehir;

  factory Kullanici.fromJson(Map<String, dynamic> json) => Kullanici(
    id: json['id'] as int,
    ad: json['name'] as String,
    eposta: json['email'] as String,
    sehir: (json['address'] as Map<String, dynamic>)['city'] as String,
  );
}
```

`address.city` iç içe bir JSON alanıdır. Önce `address` nesnesi Map'e dönüştürülür, sonra `city` okunur.

### 2. Gerçek GET isteği

```dart
final uri = Uri.parse('https://jsonplaceholder.typicode.com/users');

final yanit = await client
    .get(uri)
    .timeout(const Duration(seconds: 8));
```

`Uri.parse`, adresi güvenli URI nesnesine dönüştürür. `timeout`, bağlantı hiç tamamlanmazsa arayüzün sonsuza kadar beklemesini engeller.

### 3. Durum kodunu denetleme

```dart
if (yanit.statusCode != 200) {
  throw http.ClientException(
    'HTTP ${yanit.statusCode}',
    uri,
  );
}
```

Sunucunun yanıt vermesi işlemin başarılı olduğu anlamına gelmez. Örneğin 404 ve 500 yanıtları da bir HTTP yanıtıdır fakat başarı verisi olarak çözümlenmemelidir.

### 4. JSON listesini modellere dönüştürme

```dart
final data = jsonDecode(yanit.body) as List;

return data
    .map((e) => Kullanici.fromJson(
          Map<String, dynamic>.from(e as Map),
        ))
    .toList();
```

## Test edilebilir servis

Servis kendi içinde doğrudan yeni istemci üretmek yerine `http.Client` alır:

```dart
class KullaniciServisi {
  KullaniciServisi(this.client);
  final http.Client client;
}
```

Canlı uygulama `http.Client()` kullanır. Test ise önce 500, ikinci istekte kontrollü JSON döndüren `MockClient` verir. Böylece hata ve yeniden deneme akışı gerçek internet durumundan bağımsız doğrulanır.

## Gerçek uygulama ekranı

Test ilk HTTP 500 yanıtında hata ekranını, “Yeniden dene” sonrasında üç kullanıcının JSON'dan çözümlenmesini doğrulamıştır.

![HTTP Kullanıcı Listesi gerçek Flutter ekranı](../../screenshots/week-26-adapted.png)

## Özgün uygulama — HTTP Döviz Bilgi Kartı

Canlı uygulama anahtarsız Frankfurter API'nin güncel kur uç noktasını kullanır:

```dart
final uri = Uri.parse(
  'https://api.frankfurter.dev/v1/latest'
  '?base=TRY&symbols=USD,EUR,GBP',
);
```

Frankfurter'ın resmi v1 belgesine göre `latest` uç noktası son iş gününün kurlarını verir ve `base` parametresi temel para birimini değiştirir: <https://frankfurter.dev/v1/>

### JSON yanıtı

```json
{
  "base": "TRY",
  "date": "2026-07-21",
  "rates": {
    "USD": 0.030,
    "EUR": 0.027,
    "GBP": 0.023
  }
}
```

Bu değerler 1 TL'nin kaç birim yabancı para olduğunu gösterir. Ekranda 1 USD'nin TL karşılığını göstermek için ters oran alınır:

```dart
final yabanci = (rates[kod] as num).toDouble();
if (yabanci <= 0) throw const FormatException('Geçersiz kur');
final tlKarsiligi = 1 / yabanci;
```

Finansal uygulamalarda kaynak, referans tarihi ve verinin banka alış/satış fiyatı olmadığı açıkça belirtilmelidir.

![HTTP Döviz Bilgi Kartı gerçek Flutter ekranı](../../screenshots/week-26-original.png)

## Hata türleri

- Ağ yok veya DNS hatası: istemci exception üretir.
- Zaman aşımı: `TimeoutException` oluşur.
- HTTP 4xx/5xx: durum kodu elle reddedilir.
- Bozuk JSON: `FormatException` oluşur.
- Beklenmeyen/eksik alan: tür dönüşümü hata verir.
- Boş sonuç: başarıdır fakat ayrı boş durum ekranı gerektirir.

Öğrenciye teknik stack trace yerine anlaşılır mesaj ve yeniden deneme seçeneği gösterilir; geliştirici için ayrıntı log sistemine yazılır.

## Uygulama görevleri

### Kolay — Tek kullanıcı kartı

Bir kullanıcı uç noktasından ad, e-posta ve şehir alın. Yükleniyor ve hata ekranı ekleyin.

### Orta — Ülke bilgisi

Açık bir ülkeler API'sinden ülke adı, başkent ve nüfus çekin. Boş veya eksik alanları güvenli yönetin.

### İleri — Kur dönüştürücü

Kaynak ve hedef para birimini seçtirin, API'den oran alın ve girilen tutarı dönüştürün. Zaman aşımı, geçersiz para kodu, yenileme ve son güncelleme tarihi ekleyin.

## Test ve kontrol

- Test istemcisi istenen URI'yi doğrulamalı.
- 200 yanıtı modellere dönüşmeli.
- 4xx/5xx yanıtı başarı ekranına ulaşmamalı.
- Yeniden deneme yeni HTTP isteği göndermeli.
- İç içe JSON alanları doğru okunmalı.
- Sıfır veya eksik kur bölme hatasına yol açmamalı.
- Uygulama yükleniyor, boş, hata ve dolu durumlarını ayrı göstermeli.

## Geri bildirim ve çözüm erişimi

Öğrenci HTTP servis ve model kodunu gönderdikten sonra **İpucunu göster** istek–durum kodu–JSON akışını açıklar. **Örnek çözümü göster** gerçek servis, model ve arayüz kaynaklarını; indirme bağlantısı ise HTTP bağımlılığıyla çalışan projeyi sunar.

## Kaynak projeler

- Konu uygulaması: `07 - İnternet ve Uzak Veri/Uygulamalar/Uygulama-26-Kullanıcı Listesi`
- Özgün uygulama: `07 - İnternet ve Uzak Veri/Uygulamalar/Ozgun-26-Döviz Bilgi Kartı`
