Mert Özen Her Satırda Daha İleri
Geliştirici Araçları IDE'ler — #05

IDE'de Debug Configuration: launchSettings.json, launch.json ve .NET API + Angular'ı Birlikte Debug Etmek

Mert Özen 8 Kas 2026 9 dk 4 görüntülenme
IDE'de Debug Configuration: launchSettings.json, launch.json ve .NET API + Angular'ı Birlikte Debug Etmek

API ile Angular'ı tek F5'te debug etmek için VS Code'da launch.json compounds, Rider'da compound run configuration, Visual Studio'da multi-project launch profile kullanılır; gerçek çıktıda profile'sız dotnet run uygulamayı 5000'de ve Production'da açtı. launchSettings.json profile'ları, ng serve'ü bekleyen background task, proxy.conf.json, source map, HMR, attach to process ve test debug.

Sürüm notu: Bu yazıdaki kodlar .NET 10 SDK (Software Development Kit) 10.0.402, ASP.NET Core 10, Angular 22.2 (Angular CLI (Command Line Interface) 22.2.6) ve Node.js 24 ile derlenip denenmiştir; dotnet run, ng serve, proxy ve HMR (Hot Module Replacement) çıktıları gerçek koşulardan. Visual Studio Code (VS Code) 1.145 bu makinede kurulu, ancak debugger extension'larını kullanıcı profiline kurmamak için launch.json compound'u etkileşimli olarak çalıştırılmadı; dosyalar dokümana göre yazılıp JSON (JavaScript Object Notation) olarak doğrulandı ve parçaları (launch profile, task'ın beklediği log satırları, proxy) CLI ile tek tek denendi. Visual Studio 2026 ve Rider 2026.2 için anlatım resmi dokümandan.

Önce Tahmin Et

Bookstore'da yeni katılan bir geliştirici README'deki adımları izliyor: bir terminalde dotnet run, ötekinde ng serve. Angular açılıyor ama kitap listesi boş, tarayıcı konsolunda /api/books için 404 var. API'yi (Application Programming Interface) Visual Studio'da F5 ile başlatan ekip arkadaşı "bende çalışıyor" diyor. Ekip arkadaşının makinesinde API hangi portta ve hangi environment'ta açılıyor, yeni geliştiricininkinde hangisinde? Ve Angular /api isteğini nereye gönderiyor?

Bu yazı debugger'ın kendisini değil, onu başlatan configuration'ı anlatıyor. Breakpoint türleri, data breakpoint, Parallel Stacks gibi debugger özellikleri Debugging ve Problem Çözme serisinin beşinci ve altıncı yazılarında; burada amaç tek tuşla doğru process'leri doğru ayarlarla ayağa kaldırmak.

launchSettings.json: Profile'lar ve Environment Variable'lar

ASP.NET Core projesinin Properties/launchSettings.json dosyası yalnız geliştirme makinesinde okunur ve üç IDE'nin (Integrated Development Environment) ortak dilidir: Visual Studio profile'ları F5 menüsünde, Rider kendiliğinden oluşturduğu Launch Settings Profile run configuration'larında, VS Code'da C# Dev Kit run/debug listesinde gösterir; dotnet run da aynı dosyayı kullanır. Bookstore API'si için iki profile yazdık:

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": false,
      "applicationUrl": "http://localhost:5046",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "staging-local": {
      "commandName": "Project",
      "launchBrowser": false,
      "applicationUrl": "http://localhost:5047",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging",
        "Shipping__Provider": "FakeCarrier"
      }
    }
  }
}

Shipping__Provider'daki çift alt çizgi, environment variable'larda configuration hiyerarşisini ayırır; kodda config["Shipping:Provider"] olarak okunur. API, environment'ı ve bu değeri döndüren bir endpoint'e sahip:

app.MapGet("/api/info", (IHostEnvironment env, IConfiguration config) => new
{
    Environment = env.EnvironmentName,
    ShippingProvider = config["Shipping:Provider"] ?? "(not set)",
    Pid = Environment.ProcessId
});

İki profile'ı iki ayrı terminalde aynı anda çalıştırdık (log'lardan ilgili satırları aldık):

$ dotnet run --project api --launch-profile http
Using launch settings from api/Properties/launchSettings.json...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5046
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

$ dotnet run --project api --launch-profile staging-local
Using launch settings from api/Properties/launchSettings.json...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5047
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Staging

$ curl -s http://localhost:5046/api/info
{"environment":"Development","shippingProvider":"(not set)","pid":16777}
$ curl -s http://localhost:5047/api/info
{"environment":"Staging","shippingProvider":"FakeCarrier","pid":16776}

Profile vermeden, --no-launch-profile ile çalıştırınca tablo değişiyor:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production

Önce Tahmin Et'in cevabı bu iki çıktıda. IDE'de F5'e basan geliştirici launchSettings.json'daki ilk profile ile 5046'da ve Development'ta çalışıyor; aynı dosyayı okumayan bir yöntemle (container, publish edilmiş dll, profile'sız script) başlatan biri 5000'de ve Production'da. Production'da developer exception page kapalı, user secrets okunmaz, Development'a özel ayar dosyası yüklenmez; aynı kod farklı davranır. Bu yüzden README'de komutu profile adıyla yaz: dotnet run --launch-profile http.

Visual Studio, Rider ve VS Code'da Birden Fazla Process

.NET API ile Angular dev server'ını birlikte başlatmanın üç IDE'de üç adı var.

Visual Studio'da solution'a sağ tıklayıp Configure Startup Projects'ten Multiple startup projects seçilir ve her proje için Start, Start without debugging ya da None verilir. "Angular and ASP.NET Core" template'i bunu hazır kurar. Bu düzen multi-project launch profile olarak kaydedilebilir; Visual Studio 2026'da Tools > Options > Preview Features altında Enable Multi-Project Launch Profiles açılmalı. Profile solution'ın yanındaki .slnLaunch JSON dosyasına yazılır; Share Profile işaretliyse kaynak kontrolüne girecek dosyaya, değilse kullanıcıya özel dosyaya.

Rider'da Run > Edit Configurations'tan (Alt+Shift+F10, sonra 0) yeni bir Compound configuration eklenir ve içine API'nin launch profile configuration'ı ile Angular'ın npm configuration'ı konur. Dokümanın uyarısı: compound içindeki configuration'ların başlama sırası garanti değil; sıra gerekiyorsa son çalışacak configuration'a Before launch adımı olarak "Run Another Configuration" eklenir. Store as project file işaretlenirse configuration, solution klasöründeki .run klasörüne yazılır ve repo'ya commit'lenebilir.

VS Code'da aynı işi .vscode/launch.json'daki compounds dizisi yapar:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "API",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build-api",
      "program": "${workspaceFolder}/api/bin/Debug/net10.0/Bookstore.Api.dll",
      "cwd": "${workspaceFolder}/api",
      "launchSettingsProfile": "http"
    },
    {
      "name": "Web (Chrome)",
      "type": "chrome",
      "request": "launch",
      "preLaunchTask": "ng-serve",
      "url": "http://localhost:4200/",
      "webRoot": "${workspaceFolder}/web"
    }
  ],
  "compounds": [
    {
      "name": "API + Web",
      "configurations": ["API", "Web (Chrome)"],
      "stopAll": true
    }
  ]
}

coreclr tipi C# extension'ının debugger'ıdır ve launchSettingsProfile ile launchSettings.json'daki profile'ın environment variable ve URL (Uniform Resource Locator) ayarlarını okur; dokümana göre bu tip dosyanın yalnız bir alt kümesini destekliyor, tam destek C# Dev Kit'in dotnet tipinde. C# Dev Kit kullanıyorsan dokümanın önerisi zaten launch.json yazmamak ve Dev Kit'in projeden çıkardığı configuration'ı kullanmak; compound gerektiği an ise launch.json'a geçmek zorundasın. chrome tipi VS Code'un yerleşik JavaScript debugger'ıdır. stopAll: true, oturumlardan birini durdurduğunda diğerini de durdurur.

05-compound-debug-tr.png

Task'lar: Build'i ve Dev Server'ı Beklemek

Compound'un iki preLaunchTask'ı var ve ikincisinin özel bir sorunu var: ng serve bitmez. VS Code'un "task bitti, şimdi Chrome'u aç" demesi için task'ın arka planda çalıştığını ve ne zaman hazır olduğunu bilmesi gerekir. Bunu isBackground ve problem matcher'ın background pattern'leri sağlar:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build-api",
      "type": "process",
      "command": "dotnet",
      "args": ["build", "${workspaceFolder}/api/Bookstore.Api.csproj"],
      "problemMatcher": "$msCompile"
    },
    {
      "label": "ng-serve",
      "type": "npm",
      "script": "start",
      "path": "web",
      "isBackground": true,
      "problemMatcher": {
        "owner": "typescript",
        "pattern": "$tsc",
        "background": {
          "activeOnStart": true,
          "beginsPattern": { "regexp": "Changes detected" },
          "endsPattern": { "regexp": "bundle generation (complete|failed)" }
        }
      }
    }
  ]
}

Bu pattern'leri uydurmadık: Angular CLI 22, ng new ile oluşturduğu projenin .vscode/tasks.json'ına aynı ikisini yazıyor. Neye eşleştiklerini görmek için dev server'ı çalıştırdık; npm start, angular.json'a eklediğimiz "proxyConfig": "proxy.conf.json" sayesinde proxy ile açılıyor:

Application bundle generation complete. [0.532 seconds] - 2026-11-08T12:02:23.445Z

Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
  ➜  Local:   http://localhost:4200/

endsPattern ilk satırı yakalar ve VS Code Chrome'u o anda açar. Dev server'ın çıktısı değişirse (Angular'ın bir sonraki sürümünde mesaj farklılaşırsa) task hiç "bitmez" ve Chrome açılmaz; böyle bir durumda ilk bakılacak yer bu iki regex.

Angular Tarafı: Proxy, Source Map ve HMR

Önce Tahmin Et'teki 404'ün ikinci sebebi proxy. Angular dev server'ı 4200'de, API 5046'da; Angular kodu /api/books'u göreli adresle istediğinde istek 4200'e gider. Bunu API'ye taşıyan dosya:

{
  "/api": {
    "target": "http://localhost:5046",
    "secure": false
  }
}
$ curl -s http://localhost:4200/api/books
[{"isbn":"978-0135957059","title":"The Pragmatic Programmer","price":49.90},{"isbn":"978-0132350884","title":"Clean Code","price":42.50}]

İstek 4200'e gitti, cevap Kestrel'den geldi. Proxy, CORS (Cross-Origin Resource Sharing) ayarı yapmadan geliştirme yapmanı sağlar; production'da aynı işi reverse proxy ya da aynı origin'den servis yapar. target'taki port launchSettings.json'daki applicationUrl ile aynı olmalı; birini değiştirip ötekini unutmak bu kurulumun en sık kırılma sebebi.

Breakpoint'lerin TypeScript dosyalarında tutması için source map gerekiyor. Development configuration'ında angular.json "sourceMap": true diyor; iki build'in çıktısını karşılaştırdık:

$ npx ng build --configuration development
$ ls dist/bookstore-web/browser
favicon.ico
index.html
main.js
main.js.map
styles.css
styles.css.map
$ npx ng build
$ ls dist/bookstore-web/browser
favicon.ico
index.html
main-6LWL5HIQ.js
styles-5INURTSO.css

Production build'de .map dosyası yok; bu yüzden debug her zaman development configuration'ıyla yapılır. VS Code'da breakpoint gri ve "Unbound breakpoint" görünüyorsa sebep çoğu zaman webRoot'tur: Angular projesi repo kökünde değil web klasöründeyse webRoot da oraya işaret etmeli.

Çalışan dev server'da template'i değiştirdiğimizde Angular sayfayı yenilemeden component'i güncelledi:

❯ Changes detected. Rebuilding...
✔ Changes detected. Rebuilding...
Application bundle generation complete. [0.056 seconds] - 2026-11-08T12:02:32.528Z

Component update sent to client(s).

Global styles.css'i değiştirdiğimizde ise çıktı Page reload sent to client(s). oldu; her değişiklik HMR ile uygulanmıyor. .NET tarafında karşılığı Hot Reload: Visual Studio'da Hot Reload düğmesi, Rider'da Apply Hot Reload Changes (varsayılan keymap'te Ctrl+O, A), VS Code'da C# Dev Kit ile csharp.experimental.debug.hotReload ayarı, IDE'siz ise dotnet watch. Mekanizması ve sınırları Debugging serisinin beşinci yazısında gerçek çıktılarla anlatıldı.

Attach to Process, Container'lar ve Test Debug

Her şeyi debugger altında başlatmak zorunda değilsin; çalışan bir process'e sonradan bağlanabilirsin. Visual Studio'da Debug > Attach to Process (Ctrl+Alt+P), Rider'da Attach to Process (Ctrl+Alt+F5), VS Code'da Command Palette'ten "Debug: Attach to a .NET 5+ or .NET Core process". Visual Studio 18.10 ile Attach to Process penceresi Docker'ın yanında Podman container'larını da buluyor; container içindeki bir API'ye bağlanmak için ayrıca uzak debugger kurmana gerek kalmıyor.

Test debug etmek de bir configuration işidir: Visual Studio'da Test Explorer'dan Debug, Rider'da Debug Unit Tests (Ctrl+;, D), VS Code'da C# Dev Kit'in Test Explorer'ındaki debug düğmesi. Test projesinin environment variable'ları launchSettings.json'dan değil, .runsettings dosyasından ya da test kodundan gelir; API'de çalışan bir ayarın testte görünmemesinin sık sebebi budur.

Üretimde Dikkat

launchSettings.json production'a gitmez ve gitmemeli. Publish çıktısına kopyalanmaz; production'da environment'ı container'ın, App Service'in ya da orchestrator'ın environment variable'ları belirler. Profile'a production connection string'i ya da gerçek bir API key koyma: dosya repo'dadır.

Ekipte paylaşılan debug configuration'ları repo'da tut: launchSettings.json, .vscode/launch.json ve tasks.json, Rider'ın .run klasörü, Visual Studio'nun paylaşılan .slnLaunch dosyası. Kişisel olanları (kullanıcıya özel .slnLaunch, .vs, .idea içi workspace dosyaları) dışarıda bırak.

Port çakışmalarını izle. Bir mikroservis ekibinde her API'nin applicationUrl'i ayrı olmalı; Angular proxy'si, Docker Compose dosyası ve Aspire gibi orkestrasyon araçları aynı portları bilmeli. Port bir yerde değişince diğerlerini aynı commit'te güncelle.

Sık Düşülen Tuzaklar

Profile'sız çalıştırıp Development sanmak. Gerçek çıktı: --no-launch-profile ile uygulama 5000'de ve Production'da açıldı.

Proxy'nin portunu API ile senkron tutmamak. launchSettings.json'da port değişir, proxy.conf.json eski portu gösterir; Angular'da sessiz 504 ya da 404'ler başlar.

Background task'ın bitiş pattern'ini yanlış yazmak. endsPattern dev server'ın gerçek çıktısına eşleşmezse VS Code task'ın bitmesini sonsuza kadar bekler ve Chrome hiç açılmaz.

Production build ile debug etmeye çalışmak. Source map yok, dosya adları hash'li; breakpoint'ler bağlanmaz.

Debugger özelliklerini configuration'da aramak. Conditional breakpoint, tracepoint, exception ayarları launch configuration'ın değil debugger'ın işidir; onlar Debugging serisinde.

Küçük Bir Deneme

★☆☆ Kendi API'nde launchSettings.json'a ASPNETCORE_ENVIRONMENT=Staging ile ikinci bir profile ekle ve dotnet run --launch-profile ile iki profile'ın log'undaki "Hosting environment" satırlarını karşılaştır.

★★☆ Angular projene proxy.conf.json ekle, angular.json'da proxyConfig olarak bağla ve curl http://localhost:4200/api/... ile cevabın API'den geldiğini doğrula. Sonra API'nin portunu değiştir ve hata mesajını not et.

★★★ Bu yazıdaki launch.json ve tasks.json'ı kendi repo'na uyarla ve tek F5 ile API ve Angular'ı birlikte debug et; bir breakpoint'i Angular service'inde, bir breakpoint'i API endpoint'inde tut. Aynı düzeni Rider'da compound configuration ya da Visual Studio'da multi-project launch profile olarak kur ve dosyayı repo'ya ekle.

Debug configuration'ı üç IDE'de kurduk. Bir sonraki yazıda VS Code'a yakından bakıyoruz: C# Dev Kit ile yalnız C# extension'ı arasındaki fark, Angular Language Service, profile'lar ve .NET 10 SDK ile Node LTS (Long-Term Support) ortamını repo'ya koyan Dev Container'lar.