ElasticSearch Client Using Nest, ElasticSearch Net, GenericRepository, Nancy

Daha önceki elasticsearch ile ilgili yazımızda ElasticSearch Nedir ? Windows Üzerinde Kurulumu konularına değinmiştik. Bu yazımızda ise bir elastic search client uygulaması geliştireceğiz ve bu uygulamayı geliştirirken ElasticSearch.Net-Nest ve Nancy kütüphanelerinden faydalanacağız.

Elasticsearch java dilinde open-source olarak geliştirilen, dağıtık mimariye uygun, kolay ölçeklenebilir, enterprise düzeyde bir big-data arama motorudur bizlere sahip olduğu. Sahip olduğu API ile rest-call yaparak birçok crud ve çeşitli query-filter işlemlerini yapabilmemizi sağlamaktadır. .Net uygulamalarında bu api yi doğrudan kullanabilmemizi sağlayan nuget üzerinden indirilip kullanılabilen ElasticSearch.Net ve Nest adında 2 kütüphane mevcut. Client uygulamamızı geliştirirken bu 2 kütüphaneyi kullanacağız.

Geliştirmiş olduğumuz bu uygulamayı da host ederken daha önceki Nancy Nedir (NancyFx) yazısında detaylıca bahsettiğimiz Nancy framework kullanacağız.

Projemiz şu şekilde olsun yukarıda bahsettiğimiz gibi bir elasticsearch client uygulaması oluşturalım ve bu uygulama üzerinden product_index adında bir index oluşturarak bu index'e Product modellerimizi document olarak insert edelim sonrasında bu index üzerinde select update delete gibi işlemler yapalım.

İlk olarak vs da ElasticSearchClient adında bir console app. oluşturalım. Sonrasında proje referanslarına sağ tıklayarak nuget üzerinden sırasıyla aşağıdaki kütüphaneleri indirip kuralım.

1-Nest 

Install-Package NEST

2-ElasticSearch.Net

Install-Package Elasticsearch.Net

3-Nancy

Install-Package Nancy 

4-Nancy.Hosting.Self

Install-Package Nancy.Hosting.Self

Yukarıdaki bu 4 paketi kurduktan sonra projede yüklü olan nuget paketleri listesi aşağıdaki gibi listelenecektir. Ek olarak Nest paketi Newtonsoft paketine depended olduğundan listede Newtonsoft.Json paketide bulunacaktır.

İlgili paketleri projemize kurduktan sonra Product entity'sini ve projemizde yer alacak bütün entity'ler de bulunması gereken Id bilgisini içeren BaseEntity ve Product sınıflarını aşağıdaki gibi oluşturalım.

    public abstract class BaseEntity
    {
        public Guid Id { get; set; }
    }

    public class Product : BaseEntity
    {
        public string Name { get; set; }
        public int Quantity { get; set; }
        public decimal Price { get; set; }
    }

Projede kullanılanılacak bazı appSettings değerleri ise aşağıdaki gibidir. Bunları config dosyamıza ekleyelim yada kod içinde doğrudan da yazabilirsiniz. Geliştirmeyi local de yaptığımızdan es'e ait url localhost şeklinde tanımlı ancak bambaşka bir server'da da elasticsearch'ü host edebilirsiniz.

  <appSettings>
    <add key="NancyAddress" value="http://localhost:7880" />
    <add key="ElasticSearchApiAddress" value="http://localhost:9200" />
    <add key="ProductIndexName" value="product_index" />
  </appSettings>

Örneğin data katmanı için tıpkı bir db ile veri alış verişi yapıyormuş gibi bir GenericRepository katmanı oluşturarak tasarlayalım ve elasticsearch'e yapacağımız CRUD metotlarını bu class içerisine tanımlayalım. 

    public interface IGenericRepository<T> where T : class
    {
        Guid Save(T entity);
        T Get(Guid id);
        void Update(T entity);
        bool Delete(Guid id);
        IEnumerable<T> All();
        IEnumerable<T> Search(BaseSearchModel search);
    }

    public abstract class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
    {
        private readonly ElasticClient _elasticClient;
        private readonly string _indexName;

        protected GenericRepository(string indexName)
        {
            _elasticClient = ElasticSearchClientHelper.CreateElasticClient();
            _indexName = indexName;
        }
    }

Yukarıda tanımladığımız GenericRepository contructor inj. parameter olarak ilgili tipe ait index adı bilgisini almaktadır ve nuget'ten kurduğumuz ElasticSearchClient nesnesini initialize etme işini ElasticSearchClientHelper adındaki class'ta bulunan metoda verdik.

    public static class ElasticSearchClientHelper
    {
        public static ElasticClient CreateElasticClient()
        {
            var node = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
            var settings = new ConnectionSettings(node);
            return new ElasticClient(settings);
        }

        public static void CheckIndex<T>(ElasticClient client, string indexName) where T : class
        {
            var response = client.IndexExists(indexName);
            if (!response.Exists)
            {
                client.CreateIndex(indexName, index =>
                   index.Mappings(ms =>
                       ms.Map<T>(x => x.AutoMap())));
            }
        }
    }

Şimdi sırasıyla interface içerisinde bulunan metotlara ait elasticsearch geliştirmelerini baserepository'miz içerisinde yapalım.

İlk olarak All() metodu ile başlayalım. Bu metot isminden de anlaşılacağı üzre elasticsearch belirtmiş olduğumuz indexde ilgili tipimize ait bulunan bütün kayıtları return edecektir.

        public IEnumerable<T> All()
        {
            return _elasticClient.Search<T>(search =>
                search.MatchAll().Index(_indexName)).Documents;
        }

Get(Guid id) metodu ise index'de bulunan kaydı aldığı id parametresi ile bulup return eder bulamadığı durumda exception throw eder.

        public T Get(Guid id)
        {
            var result = _elasticClient.Get<T>(id.ToString(), idx => idx.Index(_indexName));
            if (!result.IsValid)
            {
                throw new Exception(result.OriginalException.Message);
            }
            return result.Source;
        }

Üçüncü olarak Delete(Guid id) metodunu yazalım.

        public bool Delete(Guid id)
        {
            var result = _elasticClient.Delete<T>(id.ToString(), idx => idx.Index(_indexName));
            if (!result.IsValid)
            {
                throw new Exception(result.OriginalException.Message);
            }
            return result.Found;
        }

Generic Save(T entity) metodu aldığı nesne için ilk olarak o index'de o tip için bir tanımlama yapılmışmı kontrol eder sonrasında ise Save işlemini yapar.

        public Guid Save(T entity)
        {
            ElasticSearchClientHelper.CheckIndex<T>(_elasticClient, _indexName);

            entity.Id = Guid.NewGuid();
            var result = _elasticClient.Index(entity, idx => idx.Index(_indexName));
            if (!result.IsValid)
            {
                throw new Exception(result.OriginalException.Message);
            }
            return entity.Id;
        }

Update(T entity) metodunu da aşağıdaki gibi tanımlayalım.

        public void Update(T entity)
        {
            var result = _elasticClient.Update(
                    new DocumentPath<T>(entity), u =>
                        u.Doc(entity).Index(_indexName));
            if (!result.IsValid)
            {
                throw new Exception(result.OriginalException.Message);
            }
        }

Son olarak ise Search metodunu oluşturalım. Elasticsearch'ün bize sunduğu en büyük avantajlardan biride çok farklı şekillerde search/query işlemleri yapabilmemizdir. Es. üzerinde Query DSL adı verilen bir search language ile json formatında farklı farklı filtrelere sahip query'ler yazabiliriz. Bu konu başlı başına ayrı bir yazı konusu olduğundan daha sonraki yazılarda değineceğiz. Şimdilik buradan bilgi alabilirsiniz. Biz projemzide search işlemi yaparken Match Query'den faydalanacağız. Search metodu BaseSearchModel adında içerisinde aşağıdaki property'leri içeren bir parametre almaktadır.

    public class BaseSearchModel
    {
        public int Size { get; set; }
        public int From { get; set; }
        public Dictionary<string, string> Fields { get; set; }
    }

Bu class içerisinde bulunan "Fields" bizim o ürün ile ilgili hangi alanları hangi değerlerle search edeceğimiz bilgisini tutacak olan bir çeşit dynamic search property'si. "Size" request başına kaç document return edecek bilgisi için. "From" property'si ise pageIndex olarak kullanacağız.

        public IEnumerable<T> Search(BaseSearchModel request)
        {
            var dynamicQuery = new List<QueryContainer>();
            foreach (var item in request.Fields)
            {
                dynamicQuery.Add(Query<T>.Match(m => m.Field(new Field(item.Key.ToLower())).Query(item.Value)));
            }

            var result = _elasticClient.Search<T>(s => s
                                       .From(request.From)
                                       .Size(request.Size)
                                       .Index(_indexName)
                                        .Query(q => q.Bool(b => b.Must(dynamicQuery.ToArray()))));

            if (!result.IsValid)
            {
                throw new Exception(result.OriginalException.Message);
            }

            return result.Documents;
        }

Yukarıda yazmış olduğumuz sorgu gönderilen request filed'larında bulunan key'i index de bulunan mapping'e de bulunan tipin field'ı value'su ise bu değere karşılık aranan değer.

Repository katmanı için base'de bulunan işlemleri içeren geliştirmeyi tamamladık. Şimdi ise sırada ProductRepository sınıfını oluşturmak var.

İlk olarak IBaseRepository<Product> interface'ini implement eden IProductRepository adındaki interface'i ve sonrasında BaseRepository<Product> abstract class'ını inherit alan ve IProductRepository interface'ini implement eden ProductRepository adındaki implementation sınıfını oluşturalım.

   public interface IProductRepository: IBaseRepository<Product>
    { }
    public class ProductRepository : BaseRepository<Product>, IProductRepository
    {
        public ProductRepository() : base("product_index")
        { }
    }

ProductRepository sınıfı BaseRepository sınıfının constructor'ını product tipine ait index ismini parametre geçerek çağırmaktadır.

Repository katmanı ile ilgili geliştirmelerimiz bitti. Şimdi ise araya bir Service layer yazalım. ProductService adındaki sınıf tanımlayacağımız end-point'ler ile doğrudan iletişim kurarak repository için gerekli crud işlemlerini call etmeden sorumlu olacak kısaca repository'i doğrudan dışarıya açmak yerine araya bir service layer geliştiriyoruz. 

    public interface IProductService
    {
        List<Product> Search(SearchProductRequest reqModel);
        SaveProductResponse Save(SaveProductRequest reqModel);
        UpdateProductResponse Update(UpdateProductRequest reqModel);
        List<Product> GetAll();
        bool Delete(Guid productId);
    }

Yukarıda metotlara ait olan request ve response sınıflarını aşağıdaki gibi oluşturabilirsiniz yada ihtiyacınıza göre farklı şekilde de tanımlayabilirsiniz.

    public class SearchProductRequest : BaseSearchModel
    {   }
    public class SaveProductRequest
    {
        public Product Product{ get; set; }
    }
    public class UpdateProductRequest
    {
        public Product Product{ get; set; }
    }
    public class SaveProductResponse
    {
        public Product Product { get; set; }
    }
    public class UpdateProductResponse
    {
        public Product Product { get; set; }
    }

IProductService interface'ine ait implementasyonu da aşağıdaki gibi oluşturalım.

    public class ProductService : IProductService
    {
        private readonly IProductRepository _productRepository;

        public ProductService()
        {
            _productRepository = new ProductRepository();
        }

        public List<Product> Search(SearchProductRequest reqModel)
        {
            return _productRepository.Search(reqModel).ToList();
        }

        public SaveProductResponse Save(SaveProductRequest reqModel)
        {
            var entityId = _productRepository.Save(reqModel.Product);
            return new SaveProductResponse { Product = _productRepository.Get(entityId) };
        }

        public UpdateProductResponse Update(UpdateProductRequest reqModel)
        {
            _productRepository.Update(reqModel.Product);
            return new UpdateProductResponse { Product = _productRepository.Get(reqModel.Product.Id) };
        }

        public List<Product> GetAll()
        {
            return _productRepository.All().ToList();
        }

        public bool Delete(Guid productId)
        {
            return _productRepository.Delete(productId);
        }
    }

Projemiz hazır gibi tek eksik kalan şey end-point'leri tanımlayarak NancyHost'u ayağa kaldırmak. Bunun için ilk olarak ProductModule.cs sınıfını oluşturalım. Nancyfx projelerinde end-point tanımlanırken ilgili sınıf NancyModule sınıfından inherit olur ve tanımlanan bu sınıflara yaygın olarak sonuna _Module eki getirilir. Bizde Product nesnesi için gerekli olan service adreslerini tanımlayacağımız sınıfa ProductModule adını verelim.

    public class ProductModule : NancyModule
    {
        public ProductModule()
        {
            IProductService productService = new ProductService();
            
            Post["/product/save"] = parameters =>
            {
                var request = this.Bind<SaveProductRequest>();

                return productService.Save(request);
            };

            Put["/product/update"] = parameters =>
            {
                var request = this.Bind<UpdateProductRequest>();

                return productService.Update(request);
            };

            Delete["/product/delete/{productId}"] = parameters =>
            {
                var productId = parameters.productId;
                return productService.Delete(productId);
            };

            Post["/product/search"] = parameters =>
            {
                var request = this.Bind<SearchProductRequest>();

                return productService.Search(request);
            };

            Get["/product/all"] = parameters =>
            {
                return productService.GetAll();
            };
        }
    }

Yukarıda görüldüğü üzre save-update-delete-search ve all metotlarını tanımladık. Post metodunda gönderilen request'i almak için this.Bind<RequestModel>() yazarak gönderilen json request'i model'imize bind edebiliriz. Delete metodunda da query-string kuıllanım örneği bulunmakta. Query string de gönderilen productId değerini almak için Delete["/product/delete/{productId}"] şeklinde kullanabiliriz.

İkinci olarak NancyHost'u ayağa kaldırmak var. Program.cs içerisinde aşağıdaki gibi gerekli konfigurasyonları yapalım ve uygulamamızı http://localhost:7880 adresinde network'e açalım.

    class Program
    {
        private readonly NancyHost _nancy;

        public Program()
        {
            var uri = new Uri("http://localhost:7880");
            var hostConfigs = new HostConfiguration { UrlReservations = { CreateAutomatically = true } };
            _nancy = new NancyHost(uri, new DefaultNancyBootstrapper(), hostConfigs);
        }

        private void Start()
        {
            _nancy.Start();
            Console.WriteLine($"Started listennig address {"http://localhost:7880"}");
            Console.ReadKey();
            _nancy.Stop();
        }

        static void Main(string[] args)
        {
            var p = new Program();
            p.Start();
        }
    }

İlk olarak Program const. metodu içerisinde configde yazılı olan nancyHostUrl'ini alıp Nancy'den bize bu  url'i reserve etmesini istiyoruz. Sonrasında ise NancyHost sınıfını initialize ediyoruz. Sonrasında Main function içerisinde start metodunu çağırarak projemizi run ediyoruz ve uygulamamız http://localhost:7880 adresinde host ediliyor olacak.

Şimdi postman veya herhangi bir rest-call app. kullanarak yazmış olduğumuz end-point'lere request atalım.

İlk olarak Save işlemiyle başlayalım; http://localhost:7880/product/save adresine aşağıdaki gibi bir Post işlemi yapalım.

Request;

{
	"Product":{
		"Name":"Tomato Soup",
		"Quantity":13,
		"Price":5.29
	}
}

Response olarak ise bize save işlemi yapılan nesneyi return edecektir.

{
    "product": {
        "name": "Tomato Soup",
        "quantity": 13,
        "price": 5.29,
        "id": "a55841a4-3817-475c-bc68-aafcbd452bf8"
    }
}

İkinci olarak kaydettiğimiz bu nesneyi http://localhost:7880/product/update adresine httpPut request'i göndererek Quntity bilgisi 18 olarak Update edelim.

{
	"Product":{
		"Name":"Tomato Soup",
		"Quantity":18,
		"Price":5.29,
		"Id":"a55841a4-3817-475c-bc68-aafcbd452bf8"
	}
}

Response olarak ise yine save işleminde olduğu gibi update edilen nesneyi bize return edecektir.

İki ürün daha Save edelim sonrasında ilk kaydettiğimiz ürünü silelim.

{
	"Product":{
		"Name":"Cheese",
		"Quantity":20,
		"Price":11.49
	}
}
{
	"Product":{
		"Name":"Tomato",
		"Quantity":30,
		"Price":0.49
	}
}

Silme işlemi için ise delete metodunun ProductModule içerisinde Http Delete request'i kabul ettiğini belirtmiştik. http://localhost:7880/product/delete/a55841a4-3817-475c-bc68-aafcbd452bf8 adresine sonuna Id bilgisini ekleyerek httpDelete request'i atıyoruz ve cevap olarak bize true string değerini dönüyor.

Son olarak ise Search işlemi yapalım. Name değeri Cheese olan ürünü search edelim. Bunun için aşağıdaki gibi bir request'i mizi hazırlayalım. 

 

Search işlemi için tam olmasa da bir çeşit dynamic query yazabileceğimiz bir key-value dictionary parameteresi istedik request te. Bu işimizi görüyor olsada aslında pekte güvenilir sayılmaz veya tamda ihtiyacımızı karşılamayabilir. ElasticSearch bize multi-field query yazabileceğimiz bir api sağlıyor ancak repsoitory katmanınızı dışarı açtığınızı düşündüğünüzde işin içine güvenlik girdiğinde tercih etmememiz gereken bir yapı haline geliyor yada kullandığınız taktirde bu dictionary içerisinde bulunan field'lara bir çeşit filtering uygulayarak daha güvenli hale getirmemiz gerekmekte.

Diğer bir seçenek ise Search kısmını generic değilde her repository'e ait model için yazılmasını sağlamak. Bunun için ISearchableRepository adında bir interface tanımlayarak içerisine IEnumerable<T> Search(T search) gibi bir metot eleyerek kullanmak isteyen repository bu interface'i implement eder ve kendi query'sini yazabilir.

    public interface ISearchableRepository<T>
    {
        IEnumerable<T> Search(T search);
    }

IProductRepository interface'i ve ProductRepository class'ının ise son olarak aşağıdaki gibi olacaktır. 

    public interface IProductRepository: IBaseRepository<Product>, ISearchableRepository<Product>
    { }
    public class ProductRepository : BaseRepository<Product>, IProductRepository
    {
        public ProductRepository() : base("product_index")
        { }

        public IEnumerable<Product> Search(Product search)
        {
            throw new System.NotImplementedException();
        }
    }

İhityaç duyduğunuz gibi yukarıdaki Search metodunun içerisini doldurarak daha secure bir şekilde data-layer'ı dışarıya açabiliriz.

Elasticsearch client uygulamamız ile ilgili geliştirme şimdilik bu kadar. Yazının başında da belirttiğim gibi E.S son derece esnek query'ler yazabileceğimiz şahane bir api diline sahip. İhtiyaca göre çok daha farklı türlerde filtering ve searching işlemleri yapabiliriz. Daha detaylı bilgi için elastic.co adresine göz atabilirsiniz.

Nancy Nedir (NancyFx)

Nancy .Net ve Mono için HTTP protokolü üzerinde çalışan uygulamalar geliştirmemizi sağlayan bir lightweight framework dür. Ruby de kullanılan Sinatra framework'ün den esinlenerek geliştirilmiştir ve az kaynak tüketmesinden dolayı performansıyla ön plana çıkmıştır.

Nancy developer'ları MVC(Model-View-Controller) pattern'nini veya başka herhangi bir pattern kullanmaya zorlamadan basit bir şekilde geliştirme yapmamıza olanak sağlar. Sebebi ise yukarıda bahsettiğimiz gibi sadece HTTP isteklerine cevap veren küçük ve orta ölçekli bir uygulama görevi görüyor olması.

MVC pattern'nini implement etmeye zorlamıyor derken edemeyeceğimiz anlamına da gelmemekte. Tıpkı ASP.Net MVC yada WebApi projelerinde olduğun gibi solution'da View klasörü yaratarak projeniz için olan .cshtml'leri bu dosya altında oluşturabilir veya Model klasörü yaratarak projede kullandığınız request response yada viewModel sınıflarınızı bu klasör altına oluşturabilirsiniz. Özetle Nancy ASP.Net MVC ve Web Api'nin bir alternatifi diyebiliriz. 

En büyük özelliği ise IIS e bağımlı olmadan Windows'da çalışmakta kalmayıp OSX, Linux hatta Raspberry Pi üzerinde bile çalışabilmektedir. Raspberry Pi üzerinden ASP.Net MVC çalıştırmak nasıl olurdu acaba..

Nancy ile örnek bir api projesi yapalım. İlk olarak  vs. da Nancy_Sample adında bir console app. oluşturalım ve sonrasında aşağıdaki gibi nuget üzerinden ihtiyacımız olan dll leri kuralım.

 

Nancy kütüphanesini kullanabilmek için Nancy ve host edebilmemizi sağlayan Nancy.Hosting.Self ve cshtml view'lerini kullanabilmemizi sağlayan Nancy.Viewengines.Razor paketlerini projemize ekleyelim.

Sonrasında Program.cs içerisine aşağıdaki gibi nancy konfigurasyonlarımızı yapalım.

class Program
{
    private readonly NancyHost _nancy;

    public Program()
    {
        var uri = new Uri("http://localhost:7880");
        var hostConfigs = new HostConfiguration { UrlReservations = { CreateAutomatically = true } };
        _nancy = new NancyHost(uri, new DefaultNancyBootstrapper(), hostConfigs);
    }

    private void Start()
    {
        _nancy.Start();
        Console.WriteLine($"Started listenig address {"http://localhost:7880"}");
        Console.ReadKey();
        _nancy.Stop();
    }

    static void Main(string[] args)
    {
        var p = new Program();
        p.Start();
    }
}

Yukarıdaki kod bloğunda Nancy bizim için host edilen makinada http://localhost:7880 portunu reserv ederek dinlemeye başlayacaktır. Bu adrese gelen http isteklerini ilgili route'a yönlendirecektir.

Browser üzerinden bu adrese gittiğimizde aşağıdaki gibi bir ekran ile karşılaşırız.

404 Not Found sayfasını almamızın sebebi projemizde henüz endpoint'leri tanımlayacağımız NancyModule class'ından türeyen bir Module olmaması.

Hemen projemize SampleModule adında NancyModule class'ından inherit alan bir class oluşturalım ve içerisine httpGet isteği alan bir end-point tanımlayalım.

public class SampleModule : NancyModule
{
    public SampleModule()
    {
        Get["/"] = parameters => "Que pasa primo !";
    }
}

Projeyi tekrar run edip browser'dan kontrol ettiğimizde aşağıdaki gibi Get metodunun return ettiği response'u göreceğiz.

Şimdi birde HttpPost örneği yapalım. Request olarak 2 sayı alan ve geriye bu 2 sayının toplamını return eden bir end-point yazalım. Request ve Response modellerimiz aşağıdaki gibi olacak şekilde oluşturalım.

public class SumRequestModel
{
    public int X { get; set; }
    public int Y { get; set; }
}

public class SumResponseModel
{
    public int Result { get; set; }
}

SampleModule içerisine yazacağımız end-point ise aşağıdaki gibi gönderilen request parametrelerine göre geriye toplamlarını dönecektir.

public class SampleModule : NancyModule
{
    public SampleModule()
    {
        Post["/sum"] = parameters =>
        {
            var request = this.Bind<SumRequestModel>();

            return new SumResponseModel { Result = request.X + request.Y };
        };
    }
}

Postman üzerinden aşağıdaki gibi bir httpPost request'inde bulunduğumuzda request olarak gönderilen parametrelere göre response da toplamlarını dönmektedir.

 

Yukarıda yaptığımız örnek ile Nancy kullanarak basit bir api nasıl geliştirebiliriz inceledik. Yazının başında da söylediğimiz üzre Nancy ile geriye View de yani html sayfaları da return edebiliriz.

Örnek olarak solution'da View adında bir klasör ve içerisine Home adında .cshml uzantılı bir htmlFile oluşturalım. İçerisine de aşağıdaki gibi body tagleri arasına basit bir form input'ları ekleyelim.

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <form method="post" name="myForm">
        First name: <input type="text" name="fname"><br>
        Last name: <input type="text" name="lname"><br>
        <input type="button" value="Send">
    </form>
</body>
</html>

Oluşturduğumuz bu sayfaya solution'da sağ tıklayıp Properties'den Copy to Output Directory özelliğini Copy always olarak değiştirmemiz gerekmekte aksi taktirde proje run edildiğinde Home.cshtml dosyasına erişemiyor.

Yukarıdakileri yaptıktan sonra browser'dan http://localhost:7880/home adresine bir istek geldiğinde Home.cshtml sayfamıza yönlendirecek kodu yazalım.

public class SampleModule : NancyModule
{
    public SampleModule()
    {
        Get["/home"] = parameters =>
        {
            return View["View/Home.cshtml"];
        };
    }
}

Projeyi run edip browser'dan http://localhost:7880/home adresine gittiğimizde bizi Home.cshtml sayfasına yönlendirip ekrana formu yazdıracaktır.

 

Özetle; Nancy Microsoft tarafından ASP.Net'in core dll'i olan System.Web'e bağımlı olmadan özgürce geliştirdiği şahane bir framework dür. Genelde çok büyük ölçekli projelerde tercih edilmese de ihtiyaç olduğunda bizleri çok fazla iş yükünden kurtararak ve de bence en önemlisi IIS'e bağlı kalmadan belli bir göreve hizmet eden küçük ölçekli lightweight uygulamalar geliştirmemize olanak sağlar. Daha fazla bilgi için nancyfx.org sayfasına göz atabilirsiniz.