# ASP.NET MVC

### Tanım

* ASP.NET MVC projesi uygulama katmanını oluşturur ve Controllerlar, Viewlar, js, css gibi asset bileşenlerini içerir.
* Uygulama katmanı iş kararı vermez, görevi istemcilerin isteklerini iş katmanına iletmek ve iş katmanı sonuçlarını istemciye iletmektir.
* Uygulamanın model katmanı bu ihtiyaçları karşılayan sınıflardan oluşur. ViewModel, SessionUser, PagingInfo gibi.
* Controllerlarda iş mantığı kullanılmamalıdır. Hedef en fazla 1 iş katmanı metodu çağırmak olmalıdır.
* ViewModel oluşturma işlemi *Web.Library* projesine eklenen ViewModelFactory sınıflarıyla gerçekleştirilebilir.

### Controller

Tüm Controllerlar BaseController'dan türer.\
Farklı bir mantıkta çalışması istenen controller için de yeni bir tür temel Controller üretilmelidir.

### ControllerService

* Controllerın yapacağı işleri ve bağımlılıklarını yönetmek, controller'ı temiz tutmak için her Controller'a özel adanmış bir ControllerService sınıfı oluşturulması ve Controllerın sadece bu sınıfı bağımlılık olarak alması önerilir.
* ViewModel sınıflarını Factory aracılığıyla oluşturma işlemleri uzar ve karmaşıklaşırsa da `ControllerService` sınıfları oluşturulabilir. Bu sınıflar *Web.Library* projesinde olmalıdır. İş katmanına dahil edilmemelidir.

### View

* Viewların amacı kullanıcı arayüzünü oluşturmak ve bu arayüz aracılığıyla yapılacak istemci isteklerinin Controllerlara iletilmesini sağlamaktır.
* Viewlarda iş mantığı veya koşullu mantıklar olmamalı, bu bilgiler Controller'dan sağlanan ViewModel ile oluşturulmalı ve testlerde kontrol edilmelidir.
* Viewlarda form bilgilerini oluşturmak için statik veriler elle oluşturulmamalı, bu gibi bilgiler de ViewModel'den sağlanmalı ve testlerde kontrol edilmelidir.
* Viewlarda javascript yazılmamalı. View'a özel js dosyası oluşturulmalı.
* Javascript ve css dosyaları bundle ile kullanılmalı.
* Viewlarda HTML elemanları mümkün olduğunca HtmlHelper metotlarıyla oluşturulmalıdır.

### Validasyon

* Veri geçerliliği kontrolü için FluentValidation kütüphanesi kullanılmaktadır. (`System.ComponentModel.DataAnnotations` ile uygulanabilen özniteliklerin kullanılması önerilmez.)
* FluentValidation validatörlerinin birim testlerini yazılabilir ve yazılmalıdır.
* jquery.validate kütüphanesi ile FluentValidation entegrasyonu sayesinde istemci tarafındaki geçerlilik kontrollerini otomatik olarak yapılır. `Html.ValidationMessageFor` metodu ile veri geçerlilik kontrolü uyarı mesajları gösterilir.
* Veri geçerliliği kontrolü hem istemci tarafında, hem de sunucu tarafında yapılmalıdır.
* Action metotlarda veri geçerliliği kontrolü `ModelState.IsValid` metodu ile yapılır.

### Web Formları

* Formlar kaydedilen veya güncellenen model olarak iş modeli sınıfının kullanılması ve validatörünün kullanılması beklenir.
* Formlarda HtmlHelper sınıfları kullanılır. `LabelFor` ve `DropdownListFor` gibi yardımcı metotlarda arayüz ViewModel üzerinden oluşturulmalıdır. (Örneğin dropdown oluşturmak için elle select list oluşturulup elemanları döngüyle eklenmez.)
* Veriyi manipüle eden metotlarda HTTP Post metodu, veriyi sadece okuyan metotlarda HTTP Get metodu kullanılmalı.
* HTTP Post ile çalışan metotlara `ValidateAntiForgeryToken` etiketi eklenmeli ve CSRF saldırıları önlenmeli. Formun `Html.AntiForgeryToken()` ile token oluşturması ve göndermesi sağlanmalı.
* Form kayıt işlemi başarısız olduğunda sayfa yeniden yüklenmeli, mesaj gösterilmeli ve kullanıcının sağladığı veriler korunmalıdır. Yeniden Yönlendirme yapılarak formun boş olarak açılması engellenmelidir.

### JsonResult için ClientActionResult kullanımı

Json sonuç döndüren işlemlerde Data bilgisi `ClientActionResult` ve türevleri tarafından doldurulur.

```csharp
public class ClientActionResult
{
    public string Message { get; set; }

    public bool IsSuccess { get; set; }
}
```

İşlem sonucunda veri de `ClientDataResult` sınıfı kullanılır.

```csharp
public class ClientDataResult
{
    public string Message { get; set; }

    public bool IsSuccess { get; set; }

    public object Data { get; set; }
}
```

İşlem sonucunda yönlendirme yapılacaksa `ClientRedirectResult` sınıfı kullanılır.

```csharp
public class ClientRedirectResult
{
    public string Message { get; set; }

    public bool IsSuccess { get; set; }

    public string Url { get; set; }
}
```

Yapılacak işlem geçerlilik kontrolü de işletiyorsa `ValidatableClientActionResult` sınıfı kullanılır.

```csharp
public class ValidatableClientActionResult
{
    public string Message { get; set; }

    public bool IsSuccess { get; set; }

    public bool HasValidationError { get; set; }

    public IDictionary<string, string> ValidationErrors { get; set; }
}
```

* Benzer sınıflar ihtiyaçlara göre oluşturulabilir. Ancak JsonResult metoduna asla anonim tip veya başka bir model sınıfı sağlanmaz.
* Bu özel sonuç tiplerinin başarı ve hata durumlarına özel versiyonları sınıfların factory metotları kullanılır. Yani bu sonuç sınıfları hiçbir zaman elle oluşturulmaz.

### TempData Anahtar sözlüğü

TempData ve benzeri sözlük yapısı kullanımlarında anahtar değeri elle verilmez, `ProjeAdı.Web.Library.DictionaryKey` enumu kullanılır.

```csharp
TempData[nameof(DictionaryKey.ResultMessage)] = new ResultMessage
{
    Message = errorMessage,
    MessageType = MessageType.Error
};
```

Yeni anahtar değerleri gerekirse bu enuma eklenir.

Bu işlem için aşağıdaki BaseController yardımcı metodu kullanılır:

```csharp
protected void SetResultMessageTempData(bool success, string message)
```

### Konfigürasyon ve uygulama ayarları için anahtar sözlüğü

Konfigürasyon ve uygulama ayarları için config dosyalarından okunan anahtar değeri elle verilmez, `ProjeAdı.Framework.Model.ConfigKey` enumu kullanılır.

```csharp
string version = configService.GetAppSettingsValue(ConfigKey.ApplicationVersion);
```

### HTTPS işlemleri

Bir sayfa HTTPS protokolü ile çalışıyorsa aşağıdaki etiket kullanılır:

```csharp
[RequireHttps]
public ActionResult LogOn(LogOnViewModel model, string returnUrl)
{
    //TODO:
}
```

HTTPS gerekmediği durumlarda HTTP protokolüne dönmek için aşağıdaki etiket kullanılır. (Bu etiket web projesinde BaseController'da ortak olarak kullanılıyor)

```csharp
[ExitHttpsIfNotRequired]
public class BaseController : Controller
{
    //TODO:
}
```

Child actionlar yönlendirme yapamaz. Bu nedenle HTTPS -> HTTP ve HTTP -> HTTPS geçişlerine karışmamaları gerekir. Bu nedenle tüm child actionlarda aşağıdaki etiket kullanılır.

```csharp
[RetainHttps]
[ChildActionOnly]
public ActionResult ProfileSummary(int sectionId)
{
    //TODO:
}
```

### Web Publish konfigürasyonları

Konfigürasyonların tanımlanması için bkz: Visual Studio Derleme ve Yayınlama Konfigürasyonu

Uygulama geliştirme ortamında çalışırken `Local` ortamını kullanır.

Uygulamanın publish işlemi belirlenen ortamlara göre ayrı ayrı yapılabilir.

* `Dev`
* `Test`
* `PreProd`
* `Prod`

Konfigürasyona göre `web.config` dosyasının transform dosyası kullanılarak web.config dosyası oluşturulur. Local ortam için transform dosyası oluşturmak gerekmez. `web.config` dosyasının içerdiği değerler `Local` ortamı temsil edecek şekilde tanımlanır.

Konfigürasyona göre `web.config` dosyası aşağıdaki ayar dosyalarını, seçilen ortama göre kullanacak şekilde oluşturulur.

* `AppSettings.config`
* `ConnectionStrings.config`
* `log4net.config`

Bu ayar dosyaları `/Config` dizini altında bulunan ortam dizinlerinde bulunur.

* `Config/Local`
* `Config/Dev`
* `Config/Test`
* `Config/PreProd`
* `Config/Prod`

Bu nedenle bu dosyalarda yapılan değişikler / eklemeler / çıkarmalar tüm ortamlardaki kopyalarına yansıtılmalıdır.

Publish edilen ortama göre, Config dizini altındaki diğer ortam dizinleri ve dosyaları silinmelidir.
