What is Microsoft Azure?

Photo by Rubaitul Azad on Unsplash

Microsoft Azure is a platform developed by Microsoft that offers cloud computing services. In this article, I will provide a comprehensive overview of Microsoft Azure and share information about its features, services, benefits, and use cases. I hope this article will be useful for you as well.

Cloud computing and its importance

Cloud computing is an information technology model that provides access to computer resources (servers, storage, networks, software) via the internet. It offers a more flexible, scalable, and cost-effective solution compared to traditional computing methods.

Businesses and individuals can utilize resources based on their requirements without the need to invest in physical servers and infrastructure. This optimizes costs and allows businesses to grow more flexibly.

Users can request, use, and access computer resources based on their needs through the internet. Data and applications are stored and processed on servers, while users can access these resources from any device with an internet connection.

The importance of cloud computing is increasing day by day. Businesses can securely store their data, run their applications in the cloud, and provide access to their users anytime, anywhere. Additionally, with cloud computing, businesses can quickly obtain the necessary infrastructure for their growth and innovative projects. Cloud computing also supports critical functions such as data backup, disaster scenarios, and business continuity, providing advantages in terms of security and risk management for businesses.

Cloud computing offers many benefits not only to businesses but also to individuals. It provides a user-friendly solution for storing personal data and files in the cloud, enabling synchronization between devices and facilitating collaboration. Additionally, cloud-based services allow you to instantly access entertainment services such as music, movies, and games.

Cloud computing encompasses a wide range of applications, including data storage, application development, database management, analytics, artificial intelligence, Internet of Things (IoT), web hosting, and many more services. Users can select and use these services based on their needs to perform their tasks more efficiently and flexibly.

In conclusion, cloud computing technology enables businesses to work in a more flexible, efficient, and secure manner. It is a flexible and scalable information technology model that provides access to computer resources via the internet. It helps businesses and individuals obtain more efficient, secure, and innovative solutions. Therefore, many businesses and individuals are turning to cloud computing solutions in today’s world.

What is the purpose of the Microsoft Azure platform?

Microsoft Azure is designed as a platform that offers cloud computing services. Azure provides businesses and developers with flexibility, scalability, and security by delivering cloud-based solutions.

Azure supports users in various areas such as application development, data storage, analytics, artificial intelligence, networking, security, and more. Businesses can host their applications and store their data on Azure, optimize their business processes, and deliver fast and secure services to their customers.

Flexibility and scalability

Flexibility and scalability are important features of the Microsoft Azure platform.

Azure provides users with the ability to scale their workloads rapidly according to their needs. Businesses can add resources or remove unnecessary resources based on demand. This allows resources to be automatically scaled even in situations such as periodic demands or unexpected traffic increases. Scalability offers advantages to businesses in terms of high performance, fast response times, and user experience.

Flexibility allows Azure users to create customized solutions by offering various services and configuration options to meet their needs. Users can select the most suitable services for their workloads and requirements, and configure these services as desired. Azure provides users with flexibility through preconfigured templates, quick start tools, and a wide range of service offerings for different workloads.

These features provide significant advantages for businesses and developers. With Azure, businesses can scale their resources according to their needs, respond to demands, optimize costs, and increase efficiency. Additionally, flexibility allows businesses to choose the most suitable solutions for their own workflows and requirements, and configure these solutions as desired.

Rapid application development

Azure accelerates and simplifies the application development process by offering developers various platforms, services, and tools.

Azure provides ready-made templates and starting points. These templates offer pre-configured solutions for different types of applications. Developers can use these templates to quickly build the application infrastructure and save time. For example, a developer who wants to develop a web application can use the web application template provided by Azure to create the basic infrastructure and then add the business logic.

Azure also offers various database services. Developers can use relational or NoSQL databases according to their needs. A database service like Azure Cosmos DB enables scalable and distributed storage of data. This allows application developers to easily access data without dealing with database management.

Azure allows developers to integrate different services into their applications. For example, identity authentication services, data storage services, messaging services, and various other services are provided by Azure, and application developers can integrate these services into their applications to enhance functionality.

In addition to these services, Azure also supports developers working with different programming languages and tools. For instance, you can develop applications using widely used languages such as C#, Java, Python, and integrate popular integrated development environments (IDEs) into the platform.

High security and compliance

Microsoft Azure is a cloud computing platform that adheres to high-security and compliance standards. It takes comprehensive security measures to provide its customers with a secure and reliable cloud computing platform.

Azure offers various identity management and access control mechanisms to ensure user authentication. This allows only authorized users to access systems and prevents unauthorized access to data. Additionally, it provides additional security layers such as multi-factor authentication.

In terms of data security, Azure utilizes encryption technologies to securely store data. Data is encrypted both at rest and in transit, providing protection against unauthorized access. Furthermore, applications and systems running on Azure undergo regular security audits and ensure compliance with up-to-date security standards.

Azure also demonstrates compliance with various industry-specific regulatory standards. For example, it offers services that comply with regulations and standards specific to sectors such as finance, healthcare, and public sector. This enables customers to process and store their data in accordance with the regulations in these sectors.

These security and compliance measures ensure the safety of customer data and compliance with legal requirements. They help businesses keep sensitive data secure and gain customer trust.

High availability and durability

Azure utilizes a range of mechanisms to provide its customers with a continuously available, uninterrupted service.

To ensure high availability, it leverages servers and resources across multiple data centers. This means that even if there is an issue in one data center, the service continues without interruption. With data center-level backups and recovery plans, customer data and applications are kept secure.

Azure also offers automatic scaling capabilities. This feature allows the system to automatically scale resources in the event of unexpected traffic spikes or high demands. As a result, applications maintain their performance and provide users with a seamless experience.

In terms of durability, data stored in Azure is protected using multiple replication techniques. Data is backed up across multiple disks and automatically repaired when needed. This minimizes the risk of data loss and ensures the secure preservation of customer data.

Azure also provides disaster recovery mechanisms. In the event of a disaster, data and applications are swiftly transferred to servers in another geographic region. This ensures uninterrupted service for customers and enables business continuity.

These high availability and durability measures offer customers a reliable platform that operates continuously.

Data analytics and artificial intelligence

Azure provides a comprehensive platform for businesses to analyze their data, discover important insights, and develop artificial intelligence applications.

It enables businesses to effectively process their data with its big data processing capabilities. You can store, query, and analyze your data. With a fast and scalable data processing infrastructure, you can process your data quickly and achieve faster results.

In terms of data visualization, Azure offers various tools and services. You can visualize your data through interactive charts, tables, and dashboards. This makes it easier to present and understand your data in a meaningful way.

Azure supports machine learning and deep learning capabilities. Developers can use Azure Machine Learning service to create and train machine learning models. With these models, you can make predictions, classifications, and more from your data. Azure also allows you to use pre-trained artificial intelligence models, enabling you to quickly develop your AI applications.

Azure Cognitive Services provides ready-to-use artificial intelligence services to businesses. You can easily integrate your AI applications with features such as text understanding, image recognition, and speech recognition. These services enable you to automate your business processes, improve customer services, and build smarter applications.

Global reach

Azure provides users with a wide range of accessibility by having data centers in various geographical locations worldwide.

The global network of data centers makes it easier for users to store their applications and data in remote locations. Fast and secure network connections are established between the data centers, allowing users to access their applications with high performance.

Azure offers data storage and processing capabilities in data centers that are located close to users. This enables faster transmission of data to customers and allows for low-latency service delivery. Additionally, users can choose specific data centers in a particular region to meet their local data storage requirements.

With the extensive network of data centers provided by Azure, users can distribute their applications and data worldwide, allowing them to be closer to customers and meet local requirements.

Cost effectiveness

Azure helps users optimize costs by allowing them to use only the services and resources they need.

The scalable nature of the Azure platform enables users to adjust their workloads according to their needs. As demand increases, resources are automatically scaled up to prevent unnecessary costs. Similarly, when demand decreases, resources are automatically scaled down to avoid unnecessary resource usage. This way, users only pay for the resources they actually use.

Azure also offers various payment options. Users can make payments flexibly based on their needs and preferences. For example, with the “pay-as-you-go” model, users only pay for the resources they use, allowing them to manage their budgets more effectively.

Autonomous and smart services

Azure provides autonomous and intelligent services using artificial intelligence and automation technologies.

For instance, Azure Cognitive Services offers artificial intelligence services equipped with features such as text understanding, image recognition, and speech recognition. These services provide businesses with automated and intelligent solutions in various areas, including customer service, content analysis, and sentiment analysis.

Azure automation, on the other hand, is designed to streamline business processes and increase efficiency. Services like Azure Logic Apps and Azure Functions enable businesses to create automatic connections between different systems and applications. This enables process automation, reduces repetitive tasks, and minimizes errors. Additionally, with Azure Machine Learning service, businesses can create their own custom machine learning models and make their processes smarter.

Autonomous and intelligent services offer several advantages to businesses. They accelerate business processes, reduce errors, and increase efficiency through automation and artificial intelligence technologies.

Service models that Azure provides

Azure provides flexibility and options to businesses by offering various service models. Businesses can choose different service models based on their needs and preferences.

  • Infrastructure as a Service (IaaS): Azure offers infrastructure services by providing users with virtual machines, storage, and networking resources. Businesses can host and manage their own applications and workloads using these resources. This service model offers flexibility and scalability without the need for investing in physical infrastructure.
  • Platform as a Service (PaaS): Platform services facilitate the application development and deployment processes for users. Users can develop, test, and deploy applications using the ready-made platforms and tools provided by Azure. This service model accelerates the application development process and eliminates the need to deal with infrastructure details.
  • Software as a Service (SaaS): Azure offers various applications to users through the cloud via software services. Users can manage their business processes, communicate, and increase productivity using these applications. Examples of SaaS-based applications include Office 365. This service model eliminates the need for users to deal with software purchasing, installation, and maintenance processes.

Azure Services and Solutions

Virtual machines and virtual networks

Azure provides users with flexibility and ease of management by offering essential services such as virtual machines and virtual networks.

  • Virtual Machines: Azure enables the creation of virtual environments running on physical hardware by providing users with virtual machines. Virtual machines can be scaled and managed according to the needs of businesses. Users can run their own operating systems, host applications, and store data on virtual machines. This allows them to manage workloads flexibly without investing in physical hardware.
  • Virtual Networks: Azure allows users to create virtual networks. Virtual networks enable secure communication between Azure resources. Users can create subnets and gateways, manage IP address ranges, and control network access with firewalls on virtual networks. This enables businesses to establish and manage a secure network infrastructure among multiple resources.

Virtual machines and virtual networks offer various advantages to businesses. Virtual machines reduce physical hardware costs while providing flexibility and scalability. Businesses can easily facilitate application and data hosting processes by creating virtual machines that meet their requirements. Virtual networks provide a secure and customizable network infrastructure, enabling secure communication between resources.

Data storage and database services

Azure platform facilitates data management and secure storage by offering comprehensive data storage and database services to users.

  • Blob Storage: Azure Blob Storage provides a suitable service for storing large amounts of data. Blob Storage enables the storage of various types of data, such as images, videos, documents, and other files. Users can securely store and manage large datasets, media content, or backup files using this service.
  • File Storage: It allows users to create shared file systems. With this service, users can enable shared file sharing between multiple servers and applications. File Storage provides a convenient solution for collaborative projects or situations that require data sharing between applications.
  • SQL Database: With Azure SQL Database service, users can benefit from a relational database service. Users can create a cloud-based SQL database to manage their data securely and in a scalable manner. Azure SQL Database offers advantages such as high performance, automatic backups, disaster recovery, and security features.
  • Database Management: Azure provides management services for different types of databases. For example, Azure Cosmos DB offers a fast and scalable NoSQL database for distributed applications. Additionally, services like Azure Redis Cache enable the development of high-performance applications. These services assist users in optimizing database management and performance.

Azure’s data storage and database services offer flexibility, scalability, and security to users. Users can securely store, backup, and manage their data using these services.

Analytics and AI solutions

Azure provides users with the opportunity to analyze data, create predictions, and develop artificial intelligence applications using analytics and AI solutions.

  • Data Analytics: Users are offered a comprehensive data analytics platform to analyze data and discover valuable insights. Data analytics tools enable users to delve deep into their data through capabilities such as data visualization, reporting, querying, and data mining. This allows businesses to derive meaningful information from their data and make better decisions.
  • Machine Learning: Azure offers users the ability to build and train machine learning models. Machine learning enables algorithms to analyze data and discover patterns and relationships. With Azure Machine Learning service, users can create, train, and deploy their own machine learning models. These models can be used in various applications such as making predictions, classification, and providing recommendations.
  • Artificial Intelligence: Users are provided with the opportunity to develop artificial intelligence applications. Azure Cognitive Services allows users to easily integrate artificial intelligence features such as image recognition, text analysis, speech recognition, and sentiment analysis. These services can be used in various areas such as improving customer service, performing content analysis, and automated text translation.

Mobile and web application development

Azure offers various services that facilitate the development process of mobile and web applications, enabling users to create fast, secure, and scalable applications.

  • Azure App Service: Azure App Service provides users with the ability to quickly deploy and manage web and mobile applications. Users can develop applications compatible with different programming languages and platforms and easily deploy them. Azure App Service offers automatic scaling, high availability, and security features, providing users with an enhanced experience.
  • Azure Functions: It is a service that supports event-driven application development. Users can break down their code into small pieces and create functional applications by connecting these pieces to specific events or triggers. Azure Functions simplifies the application development process for users and allows them to create scalable applications rapidly.
  • Azure Logic Apps: It enables users to create business processes quickly and easily. Users can establish connections between different services and applications using a visual interface and create automated workflows. Azure Logic Apps facilitates tasks such as data integration, notifications, and data synchronization for users.

Azure Mobile App: It is a service that supports the development process of mobile applications. Users can create applications for different mobile platforms, establish database connections, and easily integrate features such as user management.

Security and authentication services

Azure, users with comprehensive solutions for data security through its security and authentication services.

  • Azure Active Directory (Azure AD): It enables users to manage authentication and authorization processes. Users can create user accounts on Azure AD, implement role-based access control, and enable security measures such as multi-factor authentication. This allows businesses to ensure data security by preventing unauthorized access to data.
  • Azure Security Center: It provides users with continuous monitoring and protection against security threats for their cloud infrastructure and applications. Users can create security policies, monitor security threats, and identify security vulnerabilities to take necessary measures.
  • Azure Information Protection: It allows users to protect their data and share sensitive information with the right people. Users can classify, label, and encrypt data.

Azure Firewall: It provides users with the ability to protect their cloud infrastructure with a firewall. Users can create advanced security policies, control incoming and outgoing network traffic, and provide protection against attacks.

Benefits of Microsoft Azure

Microsoft Azure offers businesses a range of benefits, including cost savings, rapid deployment, flexibility, high efficiency, performance, and global accessibility.

  • Cost Savings and Scalability: Azure enables users to save on infrastructure costs. Users can eliminate maintenance costs by setting up their own data centers. Additionally, they only pay for the resources they use, minimizing costs. With Azure’s scalable architecture, users can easily scale their workloads according to their needs, optimizing resource utilization.
  • Rapid Deployment and Flexibility: Users are provided with fast application deployment and flexibility. Users can quickly deploy their applications using ready-made templates, automated scaling, and rapid deployment tools. Azure accelerates the application development process and offers flexibility through different platforms, services, and tools.
  • High Efficiency and Performance: Azure offers a platform that delivers high efficiency and performance. Users can access fast and scalable computing power. Features such as security, database management, data analytics, and artificial intelligence enable users to work efficiently.
  • Global Accessibility: Azure provides users with global accessibility by having data centers in multiple geographic locations. This allows users to deploy their applications and data worldwide, providing their customers with a high-performance experience.

Azure Pricing and Cost Management

Azure provides users with flexible cost management options by offering various subscription models and pricing units. Additionally, users can effectively control the costs of Azure services through billing and cost management tools.

  • Azure Subscription Models: Users are offered a variety of subscription models, including Pay-As-You-Go, Subscription, Enterprise Agreement, and Free Trial options. Users can choose the subscription model that best suits their needs and business requirements.
  • Pricing Units and Constraints: Different pricing units and constraints are provided for different services and resources. For example, virtual machines are priced on an hourly basis, while storage services are priced based on storage space and data transfer volume. Azure also offers pricing models based on measurement units and service level agreements (SLAs).
  • Billing: Users are provided with a detailed billing system. Users can track their consumption, view their invoices, and analyze costs associated with different services through the Azure portal. Additionally, they can create separate billing and cost centers for different users or departments.
  • Cost Management Tools: Azure Cost Management and Billing offers users features such as detailed cost reports, budget tracking, cost forecasts, and resource optimization. These tools enable users to effectively manage Azure services and keep their costs under control.

Uses of Microsoft Azure

Microsoft Azure offers a wide range of applications for businesses with various use cases. Below, you can find a few of the common use cases of Microsoft Azure:

  • Enterprise Application Development: Azure provides a powerful platform for enterprise application development. Users can develop scalable and reliable applications using services and tools available on Azure. It enables quick deployment and management of cloud-based applications.
  • Big Data Analytics: Users can store, process, and analyze large datasets using services such as Azure Data Lake, Azure Databricks, and Azure Synapse Analytics. They can gain valuable insights through data visualization tools and integration with artificial intelligence.
  • Backup and Disaster Recovery: Azure Backup and Azure Site Recovery services help protect data and ensure business continuity. Features such as automatic backups, data replication, and fast recovery are provided.
  • Internet of Things (IoT) Projects: Azure IoT Hub, Azure IoT Central, and Azure IoT Edge enable the management, data collection, and analysis of IoT devices connected to the Internet of Things. IoT projects can be implemented in various fields such as industrial automation, smart cities, and smart homes.
  • Media and E-commerce Solutions: Users can utilize Azure Media Services for media broadcasting, publishing, and optimization of content. E-commerce companies can build secure and scalable e-commerce platforms on Azure, enhancing customer experience and optimizing business processes.

In this article, I tried to provide information about Microsoft’s cloud platform, Azure. I hope I was able to assist you in understanding what Microsoft Azure is. In the future, I will strive to write about more specific topics related to Microsoft Azure.

Thank you for reading.

Nedir bu Microsoft Azure?

Photo by Rubaitul Azad on Unsplash

Microsoft Azure, bulut bilişim hizmetleri sunan Microsoft tarafından geliştirilen bir platformdur. Bu yazıda, Microsoft Azure hakkında kapsamlı bir genel bakış sunmaya ve platformun özellikleri, hizmetleri, faydaları ve kullanım alanları hakkında bilgi vermeye çalışacağım. Umarım sizler için de faydalı bir yazı olur.

Bulut bilişim ve önemi

Bulut bilişim, internet aracılığıyla bilgisayar kaynaklarına (sunucular, depolama, ağlar, yazılımlar) erişimi sağlayan bir bilgi teknolojisi modelidir. Geleneksel bilgi işlem yöntemlerine göre daha esnek, ölçeklenebilir ve maliyet-etkin bir çözüm sunar.

İşletmeler ve bireyler, fiziksel sunuculara ve altyapılara yatırım yapmak zorunda kalmadan gereksinimlerine göre kaynakları kullanabilirler. Bu sayede maliyetler optimize edilir ve işletmeler daha esnek bir şekilde büyüyebilir.

Kullanıcılar, ihtiyaçlarına göre bilgisayar kaynaklarını talep edebilir, kullanabilir ve bunlara internet üzerinden erişebilirler. Veriler ve uygulamalar sunucular üzerinde depolanır ve işlenirken, kullanıcılar sadece internet bağlantısı olan herhangi bir cihaz üzerinden bu kaynaklara erişebilirler.

Bulut bilişimin önemi gün geçtikçe artmaktadır. İşletmeler, verilerini güvenli bir şekilde depolayabilir, uygulamalarını bulutta çalıştırabilir ve kullanıcılarına her zaman ve her yerden erişim imkanı sunabilir. Ayrıca, Bulut bilişim sayesinde işletmeler, büyümeleri ve yenilikçi projeleri için gerekli altyapıyı hızla elde edebilirler. Bulut bilişim aynı zamanda veri yedekleme, felaket senaryoları ve iş sürekliliği gibi kritik işlevleri destekleyerek işletmelerin güvenlik ve risk yönetimi konularında da avantaj sağlamasına yardımcı olur.

Bulut bilişim, sadece işletmelere değil, bireylere de birçok fayda sunar. Kişisel verileri ve dosyaları bulutta saklamak, cihazlar arasında senkronizasyon sağlamak ve işbirliği yapmak için kullanıcı dostu bir çözüm sunar. Ayrıca, bulut tabanlı hizmetler, müzik, film, oyun gibi içeriklere anında erişim imkanı sunarak eğlence hizmetlerinden de yaralanmanızı sağlar.

Bulut bilişim, sadece işletmelere değil, bireylere de birçok fayda sunar. Kişisel verileri ve dosyaları bulutta saklamak, cihazlar arasında senkronizasyon sağlamak ve işbirliği yapmak için kullanıcı dostu bir çözüm sunar. Ayrıca, bulut tabanlı hizmetler, müzik, film, oyun gibi içeriklere anında erişim imkanı sunarak eğlence hizmetlerinden de yaralanmanızı sağlar.

Bulut bilişimin yaygın kullanım alanları arasında veri depolama, uygulama geliştirme, veri tabanı yönetimi, analitik, yapay zeka, IoT (Nesnelerin İnterneti), web barındırma ve daha birçok hizmet bulunur. Kullanıcılar, ihtiyaçlarına göre bu hizmetleri seçip kullanarak işlerini daha verimli ve esnek bir şekilde yürütebilirler.

Sonuç olarak, bulut bilişim teknolojisi, işletmelerin daha esnek, verimli ve güvenli bir şekilde çalışmalarını sağlar. İnternet aracılığıyla bilgisayar kaynaklarına erişimi sağlayan esnek ve ölçeklenebilir bir bilgi teknolojisi modelidir. İşletmelerin ve bireylerin daha verimli, güvenli ve yenilikçi çözümler elde etmelerine yardımcı olur. Bu nedenle, günümüzde birçok işletme ve birey bulut bilişim çözümlerine yönelmektedir.

Microsoft Azure platformunun amacı nedir?

Microsoft Azure, bulut bilişim hizmetleri sunan bir platform olarak tasarlanmıştır. Azure, işletmelere ve geliştiricilere esneklik, ölçeklenebilirlik ve güvenlik sağlayarak bulut tabanlı çözümler sunmaktır.

Azure, kullanıcılarına uygulama geliştirme, veri depolama, analitik, yapay zeka, ağ oluşturma, güvenlik ve daha birçok alanda destek sağlar. İşletmeler, Azure üzerinde uygulamalarını ve verilerini barındırabilir, iş süreçlerini optimize edebilir ve müşterilerine hızlı ve güvenli bir şekilde hizmet sunabilir.

Esneklik ve ölçeklenebilirlik

Ölçeklenebilirlik ve esneklik, Microsoft Azure platformunun önemli özelliklerinden biridir.

Azure kullanıcılara iş yüklerini ihtiyaçlarına göre hızlı bir şekilde ölçekleme imkanı sağlar. İşletmeler, taleplerine bağlı olarak kaynak ekleyebilir veya gereksiz kaynakları kaldırabilir. Böylelikle, dönemsel talepler veya beklenmedik trafik artışları gibi durumlarda bile kaynaklar otomatik olarak ölçeklendirilebilir. Ölçeklenebilirlik, işletmelere yüksek performans, hızlı yanıt süreleri ve kullanıcı deneyimi açısından avantaj sağlar.

Esneklik, Azure kullanıcılarına çeşitli hizmetler ve konfigürasyon seçenekleri sunarak ihtiyaçlarına özelleştirilmiş çözümler oluşturma imkanı verir. Kullanıcılar, kendi iş yükleri ve gereksinimlerine en uygun hizmetleri seçebilir ve bu hizmetleri istedikleri gibi yapılandırabilirler. Azure, çeşitli iş yükleri için önceden yapılandırılmış şablonlar, hızlı başlangıç araçları ve geniş bir hizmet içeriği sunarak kullanıcılara esneklik sağlar.

Bu özellikler, işletmelere ve geliştiricilere önemli avantajlar sunar. İşletmeler, Azure sayesinde ihtiyaçlarına göre kaynakları ölçeklendirerek taleplere yanıt verebilir, maliyetleri optimize edebilir ve verimliliklerini artırabilir. Ayrıca, esneklik sayesinde işletmeler, kendi iş süreçlerine ve gereksinimlerine en uygun çözümleri seçebilir ve bu çözümleri istedikleri gibi yapılandırabilirler.

Hızlı uygulama geliştirme

Azure, geliştiricilere çeşitli platformlar, hizmetler ve araçlar sunarak uygulama geliştirme sürecini hızlandırır ve kolaylaştırır.

Azure, hazır şablonlar ve başlangıç noktaları sunar. Bu şablonlar, farklı uygulama türleri için önceden yapılandırılmış çözümler sağlar. Geliştiriciler, bu hazır şablonları kullanarak uygulama altyapısını hızlı bir şekilde oluşturabilir ve zaman kazanabilir. Örneğin, bir web uygulaması geliştirmek isteyen bir geliştirici, Azure tarafından sunulan web uygulaması şablonunu kullanarak temel altyapıyı oluşturabilir ve daha sonra iş mantığını ekleyebilir.

Azure ayrıca çeşitli veri tabanı hizmetleri sunar. Geliştiriciler, ihtiyaçlarına göre ilişkisel veya NoSQL veritabanlarını kullanabilir. Azure Cosmos DB gibi bir veri tabanı hizmeti, verilerin ölçeklenebilir ve dağıtılmış bir şekilde depolanmasını sağlar. Bu, uygulama geliştiricilerinin veri tabanı yönetimiyle uğraşmadan verilere kolayca erişebilmelerini sağlar.

Azure, geliştiricilerin farklı hizmetleri uygulamalarına entegre etmelerini de sağlar. Örneğin, kimlik doğrulama hizmetleri, veri depolama hizmetleri, mesajlaşma hizmetleri gibi çeşitli hizmetler Azure tarafından sunulur ve uygulama geliştiricileri bu hizmetleri uygulamalarına entegre ederek işlevselliği artırabilir.

Bu hizmetlerin yanı sıra, Azure ayrıca geliştiricilerin farklı programlama dilleri ve araçlarla çalışmalarını destekler. Örneğin, C#, Java, Python gibi yaygın kullanılan dillerle uygulama geliştirebilir ve popüler yazılım geliştirme ortamlarını (IDE’ler) platforma entegre edebilirsiniz.

Yüksek güvenlik ve uyumluluk

Microsoft Azure yüksek güvenlik ve uyumluluk standartlarına sahip bir bulut bilişim platformudur. Müşterilerine güvenli ve güvenilir bir bulut bilişim platformu sunmak için kapsamlı güvenlik önlemleri almaktadır.

Kullanıcıların kimlik doğrulamasını sağlamak için çeşitli kimlik yönetimi ve erişim kontrolü mekanizmaları sunar. Bu sayede sadece yetkili kullanıcıların sistemlere erişimi sağlanır ve verilere izinsiz erişim engellenir. Ayrıca, çok faktörlü kimlik doğrulama gibi ek güvenlik katmanları da sunulur.

Veri güvenliği açısından, Azure verilerin güvenli bir şekilde saklanmasını sağlamak için şifreleme teknolojilerini kullanır. Veriler hem dinamik hem de statik olarak şifrelenir, böylece izinsiz erişime karşı koruma sağlanır. Ayrıca, Azure üzerinde çalışan uygulamaların ve sistemlerin düzenli olarak güvenlik denetimlerinden geçtiği ve güncel güvenlik standartlarına uymaları sağlanır.

Azure ayrıca farklı sektörlere yönelik uyumluluk standartlarına da uygunluk gösterir. Örneğin, finans, sağlık, kamu gibi sektörlere özgü düzenlemeler ve standartlarla uyumlu hizmetler sunar. Böylece, müşteriler bu sektörlerdeki düzenlemelere uygun bir şekilde verilerini işleyebilir ve depolayabilir.

Bu güvenlik ve uyumluluk önlemleri, müşteri verilerinin güvende olmasını ve yasal gereksinimlere uymasını sağlar. İşletmelerin hassas verilerini güvende tutmalarına ve müşteri güvenini kazanmalarına yardımcı olur.

Yüksek kullanılabilirlik ve dayanıklılık

Azure, müşterilerine sürekli çalışabilir, kesintisiz bir hizmet sunmak için bir dizi mekanizma kullanır.

Yüksek kullanılabilirlik sağlamak için birden fazla veri merkezindeki sunucuları ve kaynakları kullanır. Bu sayede, bir veri merkezinde bir sorun olması durumunda bile hizmet kesintisiz bir şekilde devam eder. Veri merkezi seviyesinde yedeklemeler ve kurtarma planlarıyla, müşterilerin verileri ve uygulamaları güvende tutulur.

Ayrıca otomatik ölçeklendirme özelliği sunar. Bu özellik sayesinde, beklenmedik trafik artışları veya yoğun talepler durumunda sistem otomatik olarak kaynakları ölçeklendirir. Böylece, uygulamalar performanslarını korur ve kullanıcılara kesintisiz bir deneyim sunulur.

Dayanıklılık açısından, Azure’da depolanan veriler çoklu kopyalama teknikleriyle korunur. Veriler birden fazla disk üzerinde yedeklenir ve gerektiğinde otomatik olarak onarılmaları sağlanır. Bu sayede, veri kaybı riski minimize edilir ve müşterilerin verileri güvenli bir şekilde korunur.

Azure ayrıca felaket kurtarma mekanizmaları da sunar. Eğer bir felaket durumu meydana gelirse, veriler ve uygulamalar başka bir coğrafi bölgedeki sunuculara hızlı bir şekilde aktarılır. Böylece, müşterilere sunulan hizmet kesintisiz biri şekilde devam eder ve iş sürekliliği sağlanır.

Bu yüksek kullanılabilirlik ve dayanıklılık önlemleri, müşterilere sürekli çalışan, güvenilir bir platform sunar.

Veri analitiği ve yapay zeka

Azure, işletmelere verilerini analiz etmek, önemli bilgileri keşfetmek ve yapay zeka uygulamaları geliştirmek için kapsamlı bir platform sunar.

Büyük veri işleme yetenekleriyle işletmelerin verilerini etkili bir şekilde işleyebilmelerini sağlar. Verilerinizi depolayabilir, sorgulayabilir ve analiz edebilirsiniz. Hızlı ve ölçeklenebilir bir veri işleme altyapısı ile verilerinizi hızlı bir şekilde işleyebilir ve sonuçlara daha hızlı ulaşabilirsiniz.

Veri görselleştirme konusunda da Azure birçok araç ve hizmet sunmaktadır. Verilerinizi interaktif grafikler, tablolar ve görsel panolar aracılığıyla görselleştirebilirsiniz. Bu sayede, verilerinizin anlamlı bir şekilde sunulması ve anlaşılması kolaylaşır.

Azure, makine öğrenimi ve derin öğrenme yeteneklerini destekler. Geliştiriciler, Azure Machine Learning hizmetini kullanarak makine öğrenimi modelleri oluşturabilir ve bu modelleri eğitebilirler. Bu modelleri kullanarak verilerden tahminler, sınıflandırmalar yapabilirsiniz. Azure, aynı zamanda önceden eğitilmiş yapay zeka modellerini kullanmanızı da sağlar, böylelikle yapay zeka uygulamalarınızı hızlı bir şekilde geliştirebilirsiniz.

Azure Cognitive Services ise işletmelere hazır yapay zeka hizmetleri sunmaktadır. Metin anlama, görüntü tanıma, konuşma tanıma gibi özelliklerle yapay zeka uygulamalarınızı kolayca platforma entegre edebilirsiniz. Bu hizmetler, iş süreçlerinizi otomatikleştirmenize, müşteri hizmetlerini iyileştirmenize ve daha akıllı uygulamalar oluşturmanıza olanak tanır.

Küresel ölçekte erişim

Azure, dünya genelinde birçok coğrafi konumda veri merkezleri bulundurarak kullanıcılara geniş bir erişim imkanı sağlamaktadır.

Küresel ölçekteki veri merkezleri, kullanıcıların uygulamalarını ve verilerini uzak noktalarda depolamalarını kolaylaştırır. Veri merkezleri arasında hızlı ve güvenli bir ağ bağlantısı sağlanır, böylece kullanıcılar uygulamalarınızı yüksek performanslı bir şekilde kullanırlar.

Azure, kullanıcılara yakın konumda bulunan veri merkezlerine veri depolama ve işleme yeteneği sunar. Bu, verilerinizi müşterilere daha hızlı bir şekilde iletebilmenizi ve düşük gecikme süreleriyle hizmet sunabilmenizi sağlar. Ayrıca, kullanıcılar belirli bir bölgedeki veri merkezlerini tercih ederek, yerel veri saklama gereksinimlerini karşılayabilirler.

Azure ile sunulan geniş veri merkezi ağı sayesinde, kullanıcılar uygulamalarını ve verilerini dünya çapında yayarak müşterilere yakın olabilir ve yerel gereksinimlere uyum sağlayabilir.

Maliyet etkinlik

Kullanıcıların sadece ihtiyaç duydukları hizmetleri ve kaynakları kullanmalarını sağlayarak maliyetleri optimize etmelerine yardımcı olur.

Azure platformunun ölçeklenebilir yapısı, kullanıcıların iş yüklerini ihtiyaçlarına göre ayarlamalarına olanak tanır. İhtiyaçlar arttıkça kaynakları otomatik olarak ölçeklendirerek gereksiz maliyetleri önler. Aynı şekilde, talepler azaldığında kaynakları otomatik olarak küçültür ve gereksiz kaynak kullanımını engeller. Böylece, kullanıcılar sadece kullandıkları kaynaklar için ödeme yaparlar.

Azure ayrıca farklı ödeme seçenekleri sunar. Kullanıcılar, ihtiyaçlarına ve tercihlerine göre esnek bir şekilde ödeme yapabilirler. Örneğin, “pay-as-you-go” modeliyle kullanıcılar sadece kullandıkları kaynaklar için ödeme yaparlar, böylece bütçelerini daha etkin bir şekilde yönetebilirler.

Otonom ve akıllı hizmetler

Yapay zeka ve otomasyon teknolojilerini kullanarak otonom ve akıllı hizmetler sunar.

Örneğin, Azure Cognitive Services ile metin anlama, görüntü tanıma ve konuşma tanıma gibi özelliklerle donatılmış yapay zeka hizmetleri sunulur. Bu hizmetler, işletmelere müşteri hizmetleri, içerik analizi, duygu analizi gibi birçok alanda otomatik ve akıllı çözümler sunar.

Azure otomasyonu ise iş süreçlerini kolaylaştırmak ve verimliliği artırmak için tasarlanmıştır. Azure Logic Apps ve Azure Functions gibi hizmetler, işletmelerin farklı sistemler ve uygulamalar arasında otomatik bağlantılar oluşturmasını sağlar. Bu sayede, iş süreçlerini otomatikleştirir, tekrarlayan görevleri azaltır ve hataları en aza indirir. Ayrıca, Azure Machine Learning hizmeti sayesinde işletmeler kendi özel makine öğrenimi modellerini oluşturabilir ve iş süreçlerini daha akıllı hale getirebilir.

Otonom ve akıllı hizmetler, işletmelere birçok avantaj sağlar. Otomatikleştirme ve yapay zeka teknolojileri sayesinde iş süreçlerini hızlandırır, hataları azaltır ve verimliliği artırır.

Azure’nin sağladığı hizmet modelleri

Azure, çeşitli hizmet modelleri sunarak işletmelere esneklik ve seçenekler sağlar. İşletmeler, ihtiyaçlarına ve tercihlerine göre farklı hizmet modellerini kullanabilirler.

  • Altyapı Hizmetleri (Infrastructure as a Service — IaaS): Azure, altyapı hizmetleri sağlayarak kullanıcılara sanal makineler, depolama ve ağ gibi temel altyapı kaynaklarını sunar. İşletmeler, bu kaynakları kullanarak kendi uygulamalarını ve iş yüklerini barındırabilir ve yönetebilirler. Bu hizmet modeli, işletmelere fiziksel altyapıya yatırım yapmadan esneklik ve ölçeklenebilirlik sunar.
  • Platform Hizmetleri (Platform as a Service — PaaS): Platform hizmetleri sayesinde kullanıcıların uygulama geliştirme ve dağıtma süreçlerini kolaylaştırılır. Kullanıcılar, Azure tarafından sağlanan hazır platformları ve araçları kullanarak uygulama geliştirebilir, test edebilir ve dağıtabilirler. Bu hizmet modeli, kullanıcıların uygulama geliştirme sürecini hızlandırır ve altyapı detaylarıyla uğraşmalarını engeller.
  • Yazılım Hizmetleri (Software as a Service — SaaS): Yazılım hizmetleri sayesinde Azure kullanıcılara çeşitli uygulamaları bulut üzerinden sunabilmektedir. Kullanıcılar, bu uygulamaları kullanarak iş süreçlerini yönetebilir, iletişim sağlayabilir ve verimliliklerini artırabilir. Örnek olarak, Office 365 gibi SaaS tabanlı uygulamalar gösterilebilir. Bu hizmet modeli, kullanıcıların yazılım satın alma, kurulum ve bakım gibi süreçlerle uğraşmalarını ortadan kaldırır.

Azure Hizmetleri ve Çözümleri

Sanal makineler ve sanal ağlar

Azure, sanal makineler ve sanal ağlar gibi önemli hizmetler sunarak kullanıcılara esneklik ve yönetim kolaylığı sağlar.

  • Sanal Makineler: Kullanıcılara sanal makineler sağlayarak fiziksel donanımlar üzerinde çalışan sanal ortamlar oluşturulması sağlamaktadır. Sanal makineler, işletmelerin ihtiyaçlarına göre ölçeklendirilebilir ve yönetilebilir. Kullanıcılar, sanal makineler üzerinde kendi işletim sistemlerini çalıştırabilir, uygulamalarını barındırabilir ve verilerini depolayabilir. Bu sayede, fiziksel donanım yatırımı yapmadan esnek bir şekilde iş yüklerini yönetebilirler.
  • Sanal Ağlar: Azure, kullanıcılara sanal ağlar oluşturma imkanı sunar. Sanal ağlar, kullanıcıların Azure kaynakları arasında güvenli bir şekilde iletişim kurmalarını sağlar. Kullanıcılar, sanal ağlar üzerinde alt ağlar ve ağ geçitleri oluşturabilir, IP adres aralıklarını yönetebilir ve güvenlik duvarları ile ağ erişimini kontrol edebilir. Bu sayede, işletmelerin birden fazla kaynak arasında güvenli bir ağ altyapısı oluşturması ve yönetmesi mümkün olur.

Sanal makineler ve sanal ağlar, işletmelere çeşitli avantajlar sağlar. Sanal makineler, fiziksel donanım maliyetlerini azaltırken esneklik ve ölçeklenebilirlik sunar. İşletmeler, gereksinimlerine uygun sanal makineler oluşturarak uygulama ve veri barındırma süreçlerini kolaylaştırabilirler. Sanal ağlar ise güvenli ve özelleştirilebilir bir ağ altyapısı sağlayarak kaynaklar arasında güvenli iletişimi mümkün kılar.

Veri depolama ve veri tabanı hizmetleri

Azure platformu, kullanıcılara kapsamlı veri depolama ve veri tabanı hizmeti sunarak veri yönetimini kolaylaştırır ve güvenli bir şekilde verilerin depolanmasını sağlar.

  • Blob Depolama: Azure Blob Depolama, büyük miktardaki verilerin depolanması için uygun bir hizmet sunar. Blob Depolama, resimler, videolar, belgeler ve diğer dosyalar gibi çeşitli veri tiplerinin depolanmasını sağlar. Kullanıcılar, bu hizmeti kullanarak büyük veri setlerini, medya içeriklerini veya yedekleme dosyalarını güvenli bir şekilde saklayabilir ve yönetebilirler.
  • Dosya Depolama: Kullanıcılara paylaşılan dosya sistemleri oluşturma imkanı sağlar. Bu hizmet sayesinde kullanıcılar, birden fazla sunucu ve uygulama arasında ortak bir dosya paylaşımı sağlayabilirler. Dosya Depolama, işbirliği gerektiren projelerde veya uygulamalar arasında veri paylaşımı gerektiren durumlarda kullanışlı bir çözüm sunar.
  • SQL Veri tabanı: Azure SQL Veri tabanı hizmeti ile kullanıcılar ilişkisel veri tabanı hizmetinden yararlanabilmektedirler. Kullanıcılar, bulut tabanlı bir SQL veri tabanı oluşturarak verilerini güvenli ve ölçeklenebilir bir şekilde yönetebilirler. Azure SQL Veri tabanı, yüksek performans, otomatik yedekleme, felaket kurtarma ve güvenlik özellikleri gibi avantajlar sunar.
  • Veri tabanı Yönetimi: Azure, farklı veritabanı türleri için yönetim hizmetleri sumaktadır. Örneğin, Azure Cosmos DB, dağıtılmış uygulamalar için hızlı ve ölçeklenebilir bir NoSQL veri tabanı sunmaktadır. Ayrıca Azure Redis Cache gibi hizmetler sayesinde de performanslı uygulamalar geliştirmenize olacak sağlar. Bu hizmetler, kullanıcıların veri tabanı yönetimi ve performansını optimize etmelerine yardımcı olur.

Azure platformunun veri depolama ve veri tabanı hizmetleri, kullanıcılara esneklik, ölçeklenebilirlik ve güvenlik sağlar. Kullanıcılar, verilerini güvenli bir şekilde depolayabilir, yedekleyebilir ve yönetebilir.

Analitik ve yapay zeka çözümleri

Analitik ve yapay zeka çözümleriyle kullanıcılara verileri analiz etme, öngörüler oluşturma ve yapay zeka uygulamaları geliştirme imkanı sağlanmaktadır.

  • Veri Analitiği: Kullanıcılara verileri analiz etmek ve önemli bilgileri keşfetmek için kapsamlı bir veri analitiği platformu sunulmaktadır. Veri analitiği araçları, veri görselleştirme, raporlama, sorgulama ve veri madenciliği gibi yeteneklerle kullanıcıların verilerini derinlemesine analiz etmeleri sağlanır. Bu sayede işletmeler, verilerinden anlamlı bilgiler elde ederek daha iyi kararlar alabilirler.
  • Makine Öğrenimi: Azure, kullanıcılara makine öğrenimi modelleri oluşturma ve eğitme imkanı sunmaktadır. Makine öğrenimi, algoritmaların veri analizi yaparak örüntüler ve ilişkiler keşfetmesini sağlar. Azure Machine Learning hizmeti sayesinde kullanıcılar, kendi makine öğrenimi modellerini oluşturabilir, eğitebilir ve dağıtabilirler. Bu modeller, tahminler yapma, sınıflandırma, öneriler sunma gibi birçok uygulamada kullanılabilir.
  • Yapay Zeka: Kullanıcılara yapay zeka uygulamaları geliştirme imkanı sağlanmaktadır. Azure Cognitive Services, kullanıcıların görüntü, metin, konuşma ve duygu analizi gibi yapay zeka özelliklerini kolayca entegre etmelerini sağlar. Bu hizmetler, işletmelerin müşteri hizmetlerini iyileştirmek, içerik analizi yapmak, otomatik metin çevirisi yapmak gibi birçok alanda kullanılabilir.

Mobil ve web uygulama geliştirme

Azure, kullanıcılara mobil ve web uygulama geliştirme sürecini kolaylaştıran çeşitli hizmetler sunmaktadır. Bu hizmetler, kullanıcıların hızlı, güvenli ve ölçeklenebilir uygulamalar oluşturmasını sağlar.

  • Azure App Service: Azure App Service, kullanıcılara web ve mobil uygulamaları hızlı bir şekilde dağıtma ve yönetme imkanı sağlar. Kullanıcılar, farklı programlama dilleri ve platformlarla uyumlu olarak uygulama geliştirebilir ve kolayca dağıtabilir. Azure App Service, otomatik ölçeklendirme, yüksek kullanılabilirlik ve güvenlik özellikleri sunarak kullanıcılara daha iyi bir deneyim sağlar.
  • Azure Functions: Olaya dayalı uygulama geliştirmeyi destekleyen bir hizmettir. Kullanıcılar, kodlarını küçük parçalara bölebilir ve bu parçaları belirli olaylara veya tetikleyicilere bağlayarak işlevsel uygulamalar oluşturabilir. Azure Functions, kullanıcıların uygulama geliştirme sürecini basitleştirir ve hızlı bir şekilde ölçeklendirilebilir uygulamalar oluşturmalarını sağlar.
  • Azure Logic Apps: Kullanıcılara hızlı ve basit bir şekilde iş süreçleri oluşturma imkanı sağlar. Kullanıcılar, görsel bir arayüzle farklı hizmetler ve uygulamalar arasında bağlantılar kurabilir ve otomatik iş akışları oluşturabilir. Azure Logic Apps, kullanıcıların veri entegrasyonu, bildirimler, veri senkronizasyonu gibi işlemleri kolayca gerçekleştirmesini sağlar.
  • Azure Mobile App: Kullanıcılara mobil uygulama geliştirme sürecini destekleyen bir hizmettir. Kullanıcılar, farklı mobil platformlar için uygulamalar oluşturabilir, veri tabanı bağlantıları kurabilir ve kullanıcı yönetimi gibi özellikleri kolayca entegre edebilir.

Güvenlik ve kimlik doğrulama hizmetleri

Azure, güvenlik ve kimlik doğrulama hizmetleriyle kullanıcılara veri güvenliği konusunda kapsamlı çözümler sunmaktadır.

  • Azure Active Directory (Azure AD): Kullanıcıların kimlik doğrulama ve yetkilendirme süreçlerini yönetmelerini sağlar. Kullanıcılar, Azure AD üzerinde kullanıcı hesaplarını oluşturabilir, rol tabanlı erişim kontrolü uygulayabilir ve çok faktörlü kimlik doğrulama gibi güvenlik önlemlerini etkinleştirebilirler. Bu sayede işletmeler, verilere yetkisiz erişimi engelleyerek veri güvenliğini sağlayabilirler.
  • Azure Security Center: Kullanıcılara bulut altyapısını ve uygulamalarını sürekli olarak izleme ve güvenlik tehditlerine karşı koruma imkanı sunar. Kullanıcılar, güvenlik politikalarını oluşturabilir, güvenlik tehditlerini izleyebilir ve güvenlik açıklarını tespit ederek gerekli önlemleri alabilir.
  • Azure Information Protection: Kullanıcıların verilerini korumalarını ve hassas bilgileri doğru kişilerle paylaşmalarını sağlar. Kullanıcılar, verileri sınıflandırabilir, etiketleyebilir ve şifreleyebilirler.
  • Azure Firewall: Kullanıcıların bulut altyapısını güvenlik duvarı ile koruma imkanı sunar. Kullanıcılar, gelişmiş güvenlik politikaları oluşturabilir, gelen ve giden ağ trafiğini denetleyebilir ve saldırılara karşı koruma sağlayabilirler.

Microsoft Azure’ın Faydaları

Microsoft Azure, birçok fayda sağlayarak işletmelere maliyet tasarrufu, hızlı dağıtım, esneklik, yüksek verimlilik, performans ve küresel erişim imkanı sunar.

  • Maliyet Tasarrufu ve Ölçeklendirme: Azure, kullanıcılara altyapı maliyetlerinden tasarruf etme imkanı sunar. Kullanıcılar, kendi veri merkezlerini kurarak bakım maliyetlerini ortadan kaldırabilirler. Ayrıca, yalnızca kullandıkları kaynaklar için ödeme yaparlar, bu da maliyetleri en aza indirmektedir. Azure platformunun ölçeklenebilir yapısı sayesinde kullanıcılar iş yüklerini ihtiyaçlarına göre kolayca ölçeklendirerek kaynak kullanımını optimize ederler.
  • Hızlı Dağıtım ve Esneklik: Kullanıcılara hızlı uygulama dağıtımı ve esneklik imkanı sağlanmaktadır. Kullanıcılar, hazır şablonlar, otomatik ölçeklendirme ve hızlı dağıtım araçlarıyla uygulamalarını kolayca yayına alabilirler. Azure, farklı platformlar, hizmetler ve araçlar sunarak uygulama geliştirme sürecini hızlandırır ve kullanıcılara esneklik sağlar.
  • Yüksek Verimlilik ve Performans: Azure, yüksek verimlilik ve performans sağlayan bir platform sunar. Kullanıcılar, hızlı ve ölçeklenebilir işlem gücüne erişebilirler. Güvenlik, veri tabanı, veri analitiği ve yapay zeka gibi özellikler kullanıcıların verimli bir şekilde çalışmalarını sağlar.
  • Küresel Erişim İmkanı: Birçok coğrafi konumda veri merkezleri bulundurarak kullanıcılara küresel erişim imkanı sunulmaktadır. Bu sayede kullanıcılar uygulamalarını ve verilerini dünya çapında yayabilir ve müşterilerine yüksek performanslı bir deneyim sunabilirler.

Azure Fiyatlandırması ve Maliyet Yönetimi

Azure, çeşitli abonelik modelleri ve fiyatlandırma birimleri sunarak kullanıcılara esnek bir maliyet yönetimi imkanı sağlamaktadır. Ayrıca, faturalandırma ve maliyet yönetimi araçlarıyla kullanıcılar, Azure hizmetlerinin maliyetlerini etkin bir şekilde kontrol edebilirler.

  • Azure Abonelik Modelleri: Kullanıcılara çeşitli abonelik modelleri sunulmaktadır. Bunlar arasında Pay-As-You-Go (Kullanıldıkça Öde), Abonelik, Kurumsal Anlaşma ve Ücretsiz Deneme gibi seçenekler bulunur. Kullanıcılar, ihtiyaçlarına ve iş gereksinimlerine en uygun abonelik modelini seçebilirler.
  • Fiyatlandırma Birimleri ve Kısıtlar: Farklı hizmetler ve kaynaklar için farklı fiyatlandırma birimleri ve kısıtlar sunulmaktadır. Örneğin, sanal makineler saatlik bazda fiyatlandırılırken, depolama hizmetleri depolama alanı ve veri transferi miktarına göre fiyatlandırılır. Azure ayrıca ölçüm birimlerine ve hizmet seviyesi anlaşmalarına (SLA) dayalı fiyatlandırma modelleri sunar.
  • Faturalandırma: Kullanıcılara detaylı bir faturalandırma sistemi sunulmaktadır. Kullanıcılar, Azure portalı üzerinden tüketimlerini izleyebilir, faturalarını görüntüleyebilir ve farklı hizmetlere ait maliyetleri analiz edebilirler. Ayrıca, farklı kullanıcılar veya departmanlar için ayrı faturalandırma ve maliyet merkezleri oluşturabilirler.
  • Maliyet Yönetimi Araçları: Azure Cost Management and Billing, kullanıcılara detaylı maliyet raporları, bütçe takibi, maliyet tahminleri ve kaynak optimizasyonu gibi özellikler sağlar. Bu araçlar sayesinde kullanıcılar, Azure hizmetlerini etkin bir şekilde yönetebilir ve maliyetlerini kontrol altında tutabilirler.

Microsoft Azure’ın Kullanım Alanları

Microsoft Azure, çeşitli kullanım alanlarıyla işletmelere geniş bir uygulama yelpazesi sunar. Aşağıda, Microsoft Azure’ın yaygın kullanım alanlarından bir kaçını bulabilirsiniz:

  • Kurumsal Uygulama Geliştirme: Azure, kurumsal uygulama geliştirme için güçlü bir platform sunar. Kullanıcılar, Azure üzerindeki hizmetler ve araçlarla ölçeklenebilir ve güvenilir uygulamalar geliştirebilirler. Bulut tabanlı uygulamaların hızlı bir şekilde dağıtılması ve yönetilmesi sağlanabilmektedir.
  • Büyük Veri Analitiği: Kullanıcılar, Azure Data Lake, Azure Databricks ve Azure Synapse Analytics gibi hizmetlerle büyük veri setlerini depolayabilir, işleyebilir ve analiz edebilirler. Veri görselleştirme araçları ve yapay zeka entegrasyonu sayesinde değerli bilgiler elde edebilirler.
  • Yedekleme ve Felaket Kurtarma: Azure Backup ve Azure Site Recovery gibi hizmetlerle veriler korunur ve iş sürekliliği sağlanabilir. Otomatik yedekleme, veri replikasyonu ve hızlı geri yükleme gibi özellikler sunulmaktadır.
  • IoT (Nesnelerin İnterneti) Projeleri: Azure IoT Hub, Azure IoT Central ve Azure IoT Edge gibi hizmetlerle nesnelerin internetine bağlı cihazlar yönetilebilir, veri toplanabilir ve analiz edilebilir. Endüstriyel otomasyon, akıllı şehirler ve akıllı ev gibi birçok alanda IoT projeleri gerçekleştirilebilir.
  • Medya ve E-ticaret Çözümleri: Kullanıcılar, medya yayıncılığı için Azure Media Services’i kullanabilir, içeriklerini yayınlayabilir ve yayınlarını optimize edebilirler. E-ticaret şirketleri ise Azure üzerinde güvenli ve ölçeklenebilir e-ticaret platformları oluşturabilir, müşteri deneyimini geliştirebilir ve iş süreçlerini optimize edebilirler.

Bu yazıda Microsoft’un bulut platformu Azure hakkında bilgi vermeye çalıştım. Umarım Microsoft Azure Nedir? konusunda biraz da olsa yardımcı olabilmişimdir. İlerleyen dönemlerde de Microsoft Azure hakkında daha özel konularda yazmaya çalışacağım.

Okuduğunuz için teşekkür ederim.

.NET 7 Core Web API And Rate Limiting

Photo by Ludovic Charlet on Unsplash

Hello, in this article, we will examine the concept of rate limiting introduced with .NET Core 7.

Every application we develop and deploy consumes shared resources from the hosting machine. Applications that share CPU, memory, and I/O loads also perform database operations. In such cases, it is important to control both the resource consumption of applications and the number of operations they perform.

Intentionally or due to an error, applications can consume excessive resources. For example, a mobile application you’ve created can send more requests to your API application than intended. This will result in excessive resource consumption and performance issues. The applications we create can process a certain number of requests based on the capacity of the servers they run on. If the number of requests exceeds this limit, it can cause the database and application to slow down, and even result in errors.

You can also use the rate limit feature to protect your applications from deliberate attacks aimed at decreasing their performance. As you may know, malicious users can make continuous requests to your application, rendering it unusable. Such attacks are referred to as Denial of Service (DoS) attacks.

In a web application, rate limiting generally refers to restricting the number of requests processed by the web application. This is where the concept of rate limiting comes into play. With rate limiting, you can set limits on the number of requests within a specific time interval for your application or for a specific user. For example, you can set limits such as “5000 requests within 5 minutes” or “10 requests per user within 5 minutes.” Requests can also be limited based on a token or IP address when needed.

Rate Limiting Types

  • Concurrency limit: This type of rate limiting controls the number of requests that can concurrently access a resource. When the number of requests reaches the specified limit, further requests will be cut off and receive an error. Once the first request is completed, the limit is incremented by 1, and the pending requests within the limit will start processing.
  • Fixed window limit: This type allows a specific number of requests to be made within a defined time interval. Let’s explain it with a commonly encountered example (I’ll use the analogy of a theater): The number of people allowed to watch a play in a theater is predetermined based on the number of seats. Similarly, the duration of the theater performance is known. The audience members who are admitted to a 50-seat theater will watch the play for 1 hour. Meanwhile, new audience members queue up outside for the next session. After the play ends, the next batch of 50 people enters the theater, and the cycle continues. Fixed window limits are implemented in a similar manner.
  • Sliding window limit: This type of limit algorithm collects data within a specific window size (often time-based) in a data stream and slides this window over a certain period (usually in seconds or minutes). You can use a sliding window limit to monitor the traffic of an API application and count all requests made within a specific timeframe (e.g., the last 5 minutes).

For example, if an API user is allowed to make a maximum of 100 requests per minute, the sliding window limit is set to 1 minute. At the beginning of each minute, the number of requests made by the API user within the past 1 minute is calculated. If this number exceeds 100, the API user receives an error code, and additional requests are rejected.

Let’s illustrate this algorithm with a diagram:

Consider a 2-hour period divided into 4 segments of 30 minutes each. We treat each 30-minute segment as a separate window. We further divide these 30-minute windows into 10-minute control areas, with a total limit of 100.

The first 20 minutes have passed without any requests, and we are currently in the third 10-minute interval.

https://onurkarakus.files.wordpress.com/2023/05/a47d4-0zagz4onwo-l0mb9q.webp

Within the current 10-minute period, we receive 50 requests. The window is shifted forward by 10 minutes. Since we are still within the first 30 minutes, the count is transferred to the second 30 minutes, and as a result, there are still 50 active request allowances.

https://onurkarakus.files.wordpress.com/2023/05/67c17-0bleuabouv_slvigy.webp

The window is shifted to the next 10-minute period, and we receive 20 more requests. Since we are still within the first 30-minute segment, the total number of requests becomes 70, and the remaining limit is updated to 30.

https://onurkarakus.files.wordpress.com/2023/05/fc1c5-0m_slm2ipdgma8i2x.webp

Then the window is shifted to the side, and we are now fully within the second 30-minute segment. We have 20 recorded requests, and the second 30-minute segment starts with 20 requests. Our overall limit remains at 80.

The algorithm continues to operate in this manner.

  • Token Bucket Limit: This can be abbreviated as the ability to perform operations as long as there are tokens in a bucket. When an external request comes in, it takes one token from the bucket, and the operation is performed. At certain time intervals, new tokens are added to the bucket in a way that does not overflow it (the bucket has a limit). If the bucket is empty, incoming requests are rejected.

Now let’s examine the topic of rate limiting through code with an example project.

First, let’s create our project. We create a Web API project named “BookRateLimit.”

We can define rate limiting operations within our Program.cs class. For this, it is sufficient to register the middleware using the builder.Services.AddRateLimiter(…) method. We will make our definitions such that a maximum of 5 requests can be accepted within 1 minute.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
}
));
});


// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

Let’s examine the definitions we made before the test.

By creating a GlobalLimiter, we ensure that rate limit definitions are active for all requests to the API.

Using PartitionedRateLimiter, we can diversify the limit definitions based on our desired controls. For example, we can set different limit definitions for different types of requests.

In our example, for simplicity, we applied the FixedWindowLimiter rule.

We specify the maximum number of requests within the window using PermitLimit and the time interval using Window.

Now we can perform our test. When we run our application, Swagger will automatically be enabled, allowing us to execute our method.

With the first execution, we can receive our responses. However, if more than 5 requests are made within 1 minute, the API will interrupt the requests and return an error message.

Successful Request Response.
The error we get when we violate the 5 requests per minute rule.

With the activation of the rate limit feature, requests that violate the rules and exceed the limit will end with a 503 error. But what if we want to alert users with a specific error code?

In that case, we can make changes to our Program.cs class to determine how to behave in the OnRejected state.

Let’s update our Program.cs class as follows:

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
}
));

//İsteklerin engellenmesi durumunda verilence hata kodu ve mesaj bilgisi.
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});


// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

If we test our application again, you will see that this time we receive the error code 429 — Too Many Requests.

If we don’t want to interrupt users’ requests in situations where they exceed the limit, we can make them wait until the specified time period elapses. This way, we prevent requests from being lost. However, in cases where the incoming request volume is high, it can lead to a large request queue for our application. It is important to be cautious in this regard.

For these changes, let’s modify our Program.cs class as follows:

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1),
//Kuyruk bilgileri
QueueLimit = 2,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
}
));

options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});


// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

We have added two new definitions. “QueueLimit” determines the number of requests that will wait for the specified time period to elapse after reaching the limit, while “QueueProcessingOrder” determines which request will be processed first when a new time period starts. With “OldestFirst”, we establish a structure where the first request in will be the first one to be processed.

Requests are waiting in the queue without any errors.

When we test the latest changes we made in our application, the requests that reach the limit will be put on hold and processed after the specified time elapses.

We can differentiate the rate limit information for the methods we use in our application. This can be achieved by writing policies and making the necessary definitions. Let’s update our Program.cs class again to add policies. By commenting out the GlobalLimiter feature, we ensure that it is not active.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
//options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
//RateLimitPartition.GetFixedWindowLimiter(
// partitionKey: httpcontext.Request.Headers.Host.ToString(),
// factory: partition => new FixedWindowRateLimiterOptions
// {
// AutoReplenishment = true,
// PermitLimit = 3,
// Window = TimeSpan.FromMinutes(1),
// //Kuyruk bilgileri
// QueueLimit = 2,
// QueueProcessingOrder = QueueProcessingOrder.OldestFirst
// }
//));

//Eklenen Policy Bilgileri
options.AddPolicy("User", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
Window = TimeSpan.FromMinutes(1)
}));

options.AddPolicy("Auth", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 5,
Window = TimeSpan.FromMinutes(1)
}));

options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

Now let’s add our changes to WeatherForecastController.cs. The goal here is to make each method work with a separate policy.

//WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace BookRateLimit.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}

[EnableRateLimiting("User")]
[HttpGet]
[Route("SayUserHello")]
public string SayUserHello()
{
return "Hello User.";
}

[EnableRateLimiting("Auth")]
[HttpGet]
[Route("LoginUser")]
public string LoginUser()
{
return "User Login Completed.";
}
}
}

Here, we have added the attribute [EnableRateLimiting(“”)] to the newly added methods. This allows us to activate separate policies for each method.

In some cases, we may not want a method to be subject to the globally defined rate limit. If that’s the case, we can simply add the [DisableRateLimiting] attribute to the method, indicating that it should not be included in the rate limit rules.

[DisableRateLimiting]                
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
}

Finally, if we apply the [EnableRateLimiting(“”)] and [DisableRateLimiting] attributes to the entire Controller, all methods within that Controller will be affected by this attribute assignment.

//WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace BookRateLimit.Controllers
{
[EnableRateLimiting("User")]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}

[HttpGet]
[Route("SayUserHello")]
public string SayUserHello()
{
return "Hello User.";
}

[HttpGet]
[Route("LoginUser")]
public string LoginUser()
{
return "User Login Completed.";
}
}
}

In this article, I have tried to explain the Rate Limit feature that comes with .NET 7. I hope it has been understandable for everyone.

Goodbye for now.

You can access the application code I created from the following link:

https://github.com/onurkarakus/BookRateLimit

Resources:

https://github.com/onurkarakus/BookRateLimit

.NET 6 Web API and JWT Token

Hello, in this article, we will examine the important topic of security for API applications.

It is important for the API applications we develop to be secure, both to protect the data and to prevent unauthorized individuals from using the API applications we create for their own purposes. In general, security is one of the most important issues that comes after the proper functioning of our application.

Today, we will examine the JWT Token structure, which is one of the security methods in API applications. I will try to explain the structure, creation, and working methods of the JWT Token structure in my own words.

Authentication

Authentication is the process of verifying a user’s identity. In this process, the user gains access to an application with an identity information that authenticates them. For example, a user identifies themselves to an application with a username and password. After these credentials are verified, the user’s identity is confirmed and they are granted access to resources within the system.

There are several options for implementing authentication in .NET Core Web API structure, and the most commonly used one is the JWT (JSON Web Token) based authentication structure. JWT is a data structure that stores a user’s identity information, and the validity of this data structure is ensured by a special key contained within it. Therefore, using a JWT to perform authentication is considered quite secure.

What is JSON Web Token (JWT)?

JSON Web Token (JWT) is a secure authorization method for web applications. JWT carries data in the form of tokens containing information related to user authorization, and provides secure data flow between applications using this data.

JWT consists of three parts: a header, a payload, and a signature. These parts are separated by a period “.” symbol.

Let’s take a brief look at what these sections mean.

Header:
The header information consists of two parts: the type of token and the signing algorithm. The signing algorithm can be HMAC, SHA256, or RSA.

Payload:
It represents the information to be stored or transmitted within the JWT. This can include user information, user authorization information, or additional information.

There are three types of information that can be found in the payload:

  • Registered Claims: These are optional headers that contain information related to the token. Examples include sub (subject), iss (issuer), and so on.
  • Public Claims: These are the headers that can be added by those using the token.
  • Private Claims: These contain the information to be transmitted or shared with the token.

The information to be used in the claims should not correspond to reserved information. Therefore, specific claim naming standards have been established.

You can find the information and abbreviations used here. -> https://www.iana.org/assignments/jwt/jwt.xhtml

Signature:
To create the signature section, you need to take the Base64Url encoded header and payload information, along with a secret value, and sign them using the algorithm specified in the header.

After these steps, the resulting JWT token will look like the following.

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

EyJzdWIiOiJCeU9udXIiLCJuYW1lIjoiT251ciBLQVJBS1XFniIsImlhdCI6MTUxNjIzOTAyMn0.8rPL9B1HFX-5QGX7qHOIIIsmPyUkFHbigg06sE3U-Pk

You can check the JWT token generation and decoding debugger application at https://jwt.io/#debugger-io.

The ubiquitous JWT Token diagram that is encountered everywhere.

In short, JWT consists of three parts: a header, a payload, and a signature. The header defines the type and encoding format of the JWT. The payload contains authorization information and is usually stored in JSON format. The signature includes the hash value of the header and payload data and is used to verify the authenticity of the JWT.

JWT allows user authentication without the need for data storage on the server side. This eliminates the need for data storage on the server side while securely transferring data between applications, and also improves server performance.

Some of the advantages of JWT include the ease of reading and encoding data, no need for server-side data storage, and secure data transfer between applications.

What is Authentication and Authorization?

Authentication and Authorization are two fundamental concepts for the security of web applications.

Authentication is the process of verifying a user’s identity, as previously mentioned. This process requires the user to prove their identity, such as by using their username and password. Once the user’s identity is verified, the application generates a token that can be understood by the application later on for authorization purposes.

Authorization, on the other hand, is the process of determining what actions a user is allowed to perform. This process is performed after the user’s identity has been verified by the application. For example, a user can be authorized to perform only certain actions associated with a particular role. If a user attempts to perform actions outside of their authorized scope, they can be notified with a specific message. An administrator user with an “admin” role, for instance, can be authorized to perform specific actions associated with that role.

Advantages of token-based authentication

JWT authentication is a preferred authentication method in many web applications. Instead of using a password or similar information that users need to register or log in to your web application, this method performs authentication using a token that the user possesses.

Token-based authentication is generally a scalable, performant, and secure authentication method used in web applications.

  • Stateless: Token can be sent and processed by the user on the application server without the need for state information to be kept on the server. Therefore, it has a fast and scalable structure.
  • Trustworthy: Token information is usually encrypted and kept confidential between the user and the application. Therefore, it has a secure structure. Tokens are signed to ensure data integrity and accuracy before being processed by the application.
  • Flexible: Token-based authentication can be used independently of the platform. This provides an opportunity to work comfortably with new emerging technologies and application structures.
  • Performance: Applications using token-based authentication do not perform authentication for every user request received. Instead, the accuracy of the token information sent is checked. This allows our applications to run more efficiently and quickly.
  • Scalable: Token-based authentication is also useful in load balancing among servers. With tokens, load balancing is facilitated, and each server can perform the same authentication and authorization process.
  • Customizable: It makes it easier to give users personalized permissions and authorizations. Tokens can be given to users with specific permissions and can be customized according to the needs of the application.
  • Cost-effective: Tokens can be easily created and managed with open-source libraries. Thus, it provides a cost-effective solution for authentication and authorization processes in the application.

One important point is that the authentication process alone is not an authorization mechanism. After verifying users, the levels of using our application need to be determined within the authorization structure. This will make our application more secure.

.NET Core Web API application and JWT Token

Let’s start by discussing the structure of the API application we will be creating as an example.

There will be two different controller classes in the API. The first one is the AuthContoller, where users can perform login operations, and the other one is the BookController, where users can access book information.

We will prepare two methods in the BookController, one of which will work for logged-in users and the other for all users.

As a quick example, instead of keeping records on a database, we will keep user information and response data in the application as hard-coded (In-Memory). Of course, you can also use ASP.NET Identity or another authentication system to manage users for authentication and authorization processes.

First, let’s create our project. We create an ASP.NET Core Web API project called JwtAuthForBooks.

We can delete the Controllers/WeatherForecastController.cs and WeatherForecast.cs classes that come pre-built in our application code.

Let’s first develop the Model, Controller, Interface, and Service classes for user login.

Create a folder named Models and create two classes named UserLoginRequest.cs and UserLoginResponse.cs.

//UserLoginRequest.cs 

namespace JwtAuthForBooks.Models;

public class UserLoginRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
//UserLoginResponse.cs

namespace JwtAuthForBooks.Models;

public class UserLoginResponse
{
public bool AuthenticateResult { get; set; }
public string AuthToken { get; set; }
public DateTime AccessTokenExpireDate { get; set; }
}

We create a class named IAuthService.cs inside a folder called Interfaces. Here, we will define our service interface to be able to inject it into our Controller class.

//IAuthService.cs
using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface IAuthService
{
public Task<UserLoginResponse> LoginUserAsync(UserLoginRequest request);
}

We create a class named AuthService.cs in the Services folder. This class will be the service that performs our login operations.

//AuthService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Services;

public class AuthService : IAuthService
{
public Task<UserLoginResponse> LoginUserAsync(UserLoginRequest request)
{
UserLoginResponse response = new();

if (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
{
throw new ArgumentNullException(nameof(request));
}

if (request.Username == "onur" && request.Password == "123456")
{
response.AccessTokenExpireDate = DateTime.UtcNow;
response.AuthenticateResult = true;
response.AuthToken = string.Empty;
}

return Task.FromResult(response);
}
}

Let’s create an Api Controller named AuthController.cs in our Controllers folder and arrange it as follows. This class will be our controller for login operations.

//AuthController.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtAuthForBooks.Controllers;

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
readonly IAuthService authService;

public AuthController(IAuthService authService)
{
this.authService = authService;
}

[HttpPost("LoginUser")]
[AllowAnonymous]
public async Task<ActionResult<UserLoginResponse>> LoginUserAsync([FromBody] UserLoginRequest request)
{
var result = await authService.LoginUserAsync(request);

return result;
}
}

To activate our service, we register our service in the Program.cs class.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//The record line to be added.
builder.Services.AddTransient<IAuthService, AuthService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

When we run our application, we can perform the login process through the controller. The token and date information will initially come as empty. We will add our improvements related to JWT token and the BookController class that we will use shortly.

Let’s add the Nuget package that we will use for JWT operations first. You can find the package name below. Since we created our project with .NET 6, we need to get version 6.0.14. If you created your project with .NET 7, you can get version 7.

Microsoft.AspNetCore.Authentication.JwtBearer

Let’s develop the model, service interface, and service class we will use for token operations. Model information will be in the Models folder, interface information will be in the Interfaces folder, and the service class will be in the Services folder.

//GenerateTokenRequest.cs

namespace JwtAuthForBooks.Models;

public class GenerateTokenRequest
{
public string Username { get; set; }
}
//GenerateTokenResponse.cs

namespace JwtAuthForBooks.Models;

public class GenerateTokenResponse
{
public string Token { get; set; }
public DateTime TokenExpireDate { get; set; }
}
//ITokenService.cs

using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface ITokenService
{
public Task<GenerateTokenResponse> GenerateToken(GenerateTokenRequest request);
}
//TokenService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JwtAuthForBooks.Services;

public class TokenService : ITokenService
{
readonly IConfiguration configuration;

public TokenService(IConfiguration configuration)
{
this.configuration = configuration;
}

public Task<GenerateTokenResponse> GenerateToken(GenerateTokenRequest request)
{
SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["AppSettings:Secret"]));

var dateTimeNow = DateTime.UtcNow;

JwtSecurityToken jwt = new JwtSecurityToken(
issuer: configuration["AppSettings:ValidIssuer"],
audience: configuration["AppSettings:ValidAudience"],
claims: new List<Claim> {
new Claim("userName", request.Username)
},
notBefore: dateTimeNow,
expires: dateTimeNow.Add(TimeSpan.FromMinutes(500)),
signingCredentials: new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256)
);

return Task.FromResult(new GenerateTokenResponse
{
Token = new JwtSecurityTokenHandler().WriteToken(jwt),
TokenExpireDate = dateTimeNow.Add(TimeSpan.FromMinutes(500))
});
}
}

Finally, we need to make the following changes in the appsettings.json file.

//appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",

"AppSettings": {
"ValidAudience": "AudienceInformation",
"ValidIssuer": "IssuerInformation",
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
}
}

When we run our login method again, we will be able to see that the JWT Token information has been created.

Let’s take a moment to examine the service here. In the GenerateToken method of this service, we create a JWT token using the username entered by the user. The JwtSecurityToken class takes the necessary information for the JWT token and performs the token creation process. So, let’s try to examine what these information are.

One of the important points is the Secret value we pass as signingCredentials to the JwtSecuritToken class. With this value, we can sign our token information.

We do this by creating a SymmetricSecurityKey from the value we specified in our appsetting.json file. We can check this value from the token sent to us and understand that it is signed by us.

The important thing to note here is that token information is readable structures. Therefore, private information should not be included in the token.

issuer information is a value that identifies who the token value is distributed by, that is, who is identifying us. For example, a value like MyBookStore can be specified.

audience determines who will use the token value to be generated. It can be a token value generated for a site (www.test.com).

expires determines how long the token information will be active. After this period, the token will be inactive and authorization errors will be given during the use of Api methods.

notBefore can be used if you want the token information to come into effect after a certain time has passed since it was generated. Since we want the token information to be immediately active, we set the time it was generated as the starting value.

claims can be defined as the section where we add the information we want to store in the token information. Here, we can define a piece of information that is not private and later perform certain operations by taking these values ​​when this token is sent to us.

Now let’s develop the BookController class where we will test the authorization. Then we will activate the authentication for our application and be able to do our test.

As we mentioned before in our scenario, we will have two controller methods. One of these methods will return the book list and will work for all users. Our second method will only work for authorized users who have successfully completed the login process and sent the token information, and will return the book information.

We start by creating three classes in our Models folder called BookInformation.cs, BookTitle.cs, and GetBookInformationByIdRequest.cs.

//BookInformation.cs

namespace JwtAuthForBooks.Models;

public class BookInformation
{
public int BookId { get; set; }
public string Isbn { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AuthorName { get; set; }
}
//BookTitle.cs

namespace JwtAuthForBooks.Models;

public class BookTitle
{
public int BookId { get; set; }
public string Isbn { get; set; }
public string Title { get; set; }
}
//GetBookInformationByIdRequest.cs

namespace JwtAuthForBooks.Models;

public class GetBookInformationByIdRequest
{
public int BookId { get; set; }
}

Let’s not disrupt our service structure. We create two classes named IBookService.cs in the Interfaces folder and BookService.cs in the Services folder, and organize them as follows.

//IBookService.cs

using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface IBookService
{
public Task<List<BookTitle>> GetBookTitlesAsync();
public Task<BookInformation> GetBookInformationByIdAsync(GetBookInformationByIdRequest request);
}
//BookService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Services;

public class BookService : IBookService
{
static readonly List<BookInformation> bookInformations = new List<BookInformation> {
new BookInformation { BookId = 1, Isbn = "9752115047", Title ="22/11/63", AuthorName = "Stephen King", Description = "22 Kasım 1963’te, bütün bunları değiştirme şansınız olsaydı?" },
new BookInformation { BookId = 2, Isbn = "1476762740", Title ="Uyuyan Güzeller", AuthorName = "Stephen King * Owen King", Description = "Şimdi burada dünyanın kaderine karar verilecek." },
new BookInformation { BookId = 3, Isbn = "9752126049", Title ="Enstitü", AuthorName = "Stephen King", Description = "Enstitü..." }
};

public Task<BookInformation> GetBookInformationByIdAsync(GetBookInformationByIdRequest request)
{
var loadedBookInformation = bookInformations.FirstOrDefault(p => p.BookId == request.BookId);

return Task.FromResult(loadedBookInformation);
}

public Task<List<BookTitle>> GetBookTitlesAsync()
{
var booktitleList = bookInformations.Select(book => GenerateBookTitleForList(book)).ToList();

return Task.FromResult(booktitleList);
}

private static BookTitle GenerateBookTitleForList(BookInformation book)
{
return new BookTitle { BookId = book.BookId, Title = book.Title, Isbn = book.Isbn };
}
}

Now we add our BookController.cs class to the Controllers folder.

Here, it is important to note that we use the [AllowAnonymous] attribute for the method that all users can access without authorization, and the [Authorize] attribute for the method that can only be accessed by authorized users. As the names suggest, we have defined our methods that will work anonymously and with authorization in this way.

Now let’s register our service in our Program.cs class.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddTransient<IAuthService, AuthService>();
builder.Services.AddTransient<ITokenService, TokenService>();

// The record line to be added.
builder.Services.AddTransient<IBookService, BookService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Now that we have made all the necessary developments, let’s activate the authorization structure for our application. To do this, let’s make our definitions within the Program.cs class again.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddTransient<IAuthService, AuthService>();
builder.Services.AddTransient<ITokenService, TokenService>();
builder.Services.AddTransient<IBookService, BookService>();

// lines to be added
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = builder.Configuration["AppSettings:ValidIssuer"],
ValidAudience = builder.Configuration["AppSettings:ValidAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["AppSettings:Secret"])),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = false,
ValidateIssuerSigningKey = true
};
});

builder.Services.AddAuthorization();

//End of the lines to be added

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

// lines to be added

app.UseAuthentication();
app.UseAuthorization();

//End of the lines to be added

app.MapControllers();

app.Run();

The final state of our project will be as follows.

We defined the functionality of JWT token structure to work with AddAuthentication() method. With AddAuthorization(), we indicated that there will be authorization. We activated these definitions with UseAuthentication() and UseAuthorization().

Now, we can test our methods. Our method defined with [AllowAnonymous] will work without any issues during the test.

Users who have not successfully passed the login process and have not received the token information will receive a 401 Unauthorized error when they run the GetBookInformationById method.

Let’s log in with the username and password through the Login method to obtain the token information. Then, we will retry the GetBookInformationById method by sending this token information.

We have successfully logged in and obtained the token information.

Now, we will try the process again by sending this token information to the GetBookInformationById method. However, when sending the token, we will include it in the header information with the key value of Authorization. It should be sent in the format of Bearer <token_info>.

After that, when we run our method again with the token we received, we can see that the method returns a response with authorization since we have successfully authenticated.

In this article, I have tried to explain the JWT Token topic as much as possible. I hope it becomes a useful source of information for you.

You can access the application codes we created from the link below.

https://github.com/onurkarakus/JwtAuthForBooks

See you later.

.NET 7 Core Web API Rate Limiting

Photo by Makarios Tang on Unsplash

Merhaba, bu yazımızda .NET Core 7 ile gelen rate limiting kavramını inceleyeceğiz.

Her geliştirdiğimiz ve devreye aldığımız uygulama çalıştığı makine üzerinden ortak kaynakları tüketmektedir. CPU, bellek ve I/O yüklerini paylaşan uygulamalar bunlara ek olarak veri tabanı işlemleri de yapmaktadırlar. Böyle bir durumda uygulamaların hem kaynak tüketimleri hem de yaptıkları işlemlerin sayılarının kontrol altına alınması önemlidir.

Bilerek veya bir hata sebebiyle uygulamalar fazla kaynak tüketebilirler. Oluşturduğunuz bir mobil uygulama API uygulamanıza istenilenden fazla istek gönderebilir. Bu da fazla kaynak tüketimi ile birlikte performans sorunları oluşturacaktır. Oluşturduğumuz uygulamalar çalıştıkları sunucuların kapasitelerine göre belirli sayıda istekleri işleyebilmektedirler. Bu istek sayısının sınırın dışına çıkması veri tabanı ve uygulamanın yavaşlamasına ve hatta hatalara sebep olacaktır.

Bilinçli bir şekilde uygulamaların performanslarını düşürmek amacıyla yapılan saldırılardan korunmak için de rate limit özelliğini kullanabilirsiniz. Bildiğiniz gibi kötü niyetli kullanıcılar uygulamanıza sürekli istek yaparak uygulamanızın kullanılamaz hale gelmesini sağlayabilir. Bu tür saldırılara da DOS (Denial Of Services) adı verilmektedir.

Bir web uygulamasında hız sınırlama genellikle web uygulaması tarafından işlenen istek sayısını sınırlama anlamına gelmektedir. Rate limiting kavramı da bu noktada devreye giriyor. Rate limiting sayesinde uygulamanız için bir zaman aralığında veya bir kullanıcı için istek sayısı belirleme imkanına sahip olursunuz. Örneğin, “5 dakika içinde 5000 istek veya 5 dakika içinde kullanıcı başına 10 istek” şeklinde limitler ekleyebilirsiniz. İstenildiği zaman bir token veya IP adresi üzerinden de isteklerin sınırlandırılması yapılabilmektedir.

Rate Limiting Tipleri

Concurrency limit (Eşzamanlılık sınırı) : Bu tip sınırlamalar bir kaynağa eş zamanı olarak erişilebilecek istek sayısını kontrol etmektedir. İstek sayısı belirlenen sınıra geldiği zaman istekler kesilecek ve hata alacaktır. Gelen ilk istek tamamlandıktan sonra belirlenen sınır 1 artırılacak ve sınırda kalmış olan istek işlenmeye başlayacaktır.

Fixed window limit (Sabit sınır) : Belirlenen bir zaman aralığında, belirlenen sayıda istek yapılmasını sağlar. Her anlatımda karşılaştığımız örnek ile açıklamaya çalışayım. (Ben tiyatro olarak yazacağım 🙂 ) Bir tiyatro salonunda oyunu izleyecek kişi sayısı daha önceden koltuk sayısı ile sabitlenmiştir. Yine aynı şekilde tiyatro oyununun süresi de bilinmektedir. 50 kişilik bir tiyatro salonuna alınan izleyiciler 1 saat boyunca oyunu izleyecekler. Dışarıda ise sonraki seans için yeni izleyiciler sıraya girmekte. Oyun bittikten sonra sıradaki izleyiciler salona girerler ve oyun başlar. Daha sonra yine bir 50 kişi sıraya girer ve bu olay devamlı tekrarlanır. Sabit sınırlı limit kontrolleri de bu şekilde yapılmaktadır.

Sliding window limit (Kayan sınır) : Bu limit tipi bir veri akışındaki verileri belirli bir pencere boyutu içinde (genellikle zaman bazlı) toplayarak, bu pencerenin belirli bir süre boyunca (genellikle saniye veya dakika cinsinden) kaydırıldığı bir algoritmadır. Bir API uygulamasının trafiği izlemek için bir kaydırma penceresi limiti kullanabilirsiniz ve belirli bir süre içinde (örneğin, son 5 dakika içinde) yapılan tüm istekleri sayabilirsiniz.

Örneğin, bir API kullanıcısı her dakika içinde en fazla 100 istek yapabiliyorsa, kaydırma penceresi limiti 1 dakika olarak ayarlanır. Her dakika başında, API kullanıcısının son 1 dakika içinde yaptığı isteklerin sayısı hesaplanır. Bu sayı, 100’ün üzerine çıkarsa API kullanıcısı bir hata kodu alır ve ek istekler reddedilir.

Bu algoritmayı bir çizim ile açıklamaya çalışalım.

2 saatlik bir periyodu 4’e bölerek 30 dakikalık bölümler oluşturduğumuzu düşünelim. Her 30 dakika ayrı bir bölüm olarak değerlendireceğiz. Bu 30 dakikalık bölümleri de 10’ar dakikalık kontrol alanlarına bölüyoruz ve toplam limit olarak da 100 veriyoruz.

İlk 20 dakika hiç istek gelmemiş durumda ve şu an üçüncü 10 dakika içinde bulunuyoruz.

https://devblogs.microsoft.com/dotnet/wp-content/uploads/sites/10/2022/07/sliding_part1.png

Bulunduğumuz 10 dakikalık periyod içerisinde 50 istek geliyor. Pencere bir 10 dakika ileriye alınıyor. Şu an halen ilk 30 dakika içerisine olduğumuz için ikinci 30 dakika için aktarılıyor ve son durumda 50 istek hakkı daha aktif durumda.

https://devblogs.microsoft.com/dotnet/wp-content/uploads/sites/10/2022/07/sliding_part2.png

Sonraki 10 dakika içerisine pencere kaydırılıyor ve 20 istek daha geliyor. Halen ilk 30 dakikalık bölüm içinde olduğumuzdan dolayı toplam gelen istek sayısı 70 oluyor ve kalan limitimiz 30 olarak güncelleniyor.

https://devblogs.microsoft.com/dotnet/wp-content/uploads/sites/10/2022/07/sliding_part3.png

Ardından pencere bir yana kaydırılıyor ve artık ikinci 30 dakikanın tamamen içinde bulunuyoruz. Elimizde kayıtlı istek sayısı 20 ve ikinci 30 dakikalık bölüm 20 istek ile başlıyor. Kanal limitimiz de 80 olarak devam ediyor.

Genel olarak algoritma bu şekilde devam ederek çalışıyor.

Token Bucket Limit (Jeton Kovası Sınırı) : Bir kovanın içinde jetonlar kadar işlem yapılabilmesi şeklinde kısaltılabilir. Dışardan gelen istek kova içindeki jetonlardan birini alır ve işlem gerçekleşir. Belirli zaman aralıklarında ise kovaya taşmayacak şekilde (kovanın bir limiti mevcut) yeni jetonlar eklenir. Eğer kova boş ise gelen istek reddedilir.

Şimdi de örnek proje ile rate limit konusunu kod üzerinden inceleyelim.

Öncelikle projemizi oluşturalım. BookRateLimit isimli bir Web Api projesi oluşturuyoruz.

Program.cs sınıfımız içinde sınırlandırma işlemleri tanımlayabiliriz. Bunun için builder.Services.AddRateLimiter(…) middleware kayıt işlemini yapmamız yeterli olacaktır. Tanımlamalarımızı 1 dakika içinde en fazla 5 istek kabul edilebilir şeklinde yapacağız.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
}
));
});


// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

Test öncesinde nasıl bir tanımlama yaptık onu inceleyelim.

Bir GlobalLimiter oluşturarak API’ye gelen tüm istekler için rate limit tanımlarının aktif olmasını sağlıyoruz.

PartitionedRateLimiter ile limit tanımlanmasının istediğimiz kontrollerimizi çeşitlendirebiliyoruz. Örnek olarak farklı istek tipleri için farklı limit tanımları yapabiliyoruz.

Örneğimizde basit olması açısından FixedWindowLimiter kuralını uyguladık.

PermitLimit ile pencere içinde maksimum istek sayısını, Window ile de zaman aralığını belirtiyoruz.

Şimdi testimizi yapabiliriz. Uygulamamızı çalıştırdığımız zaman Swagger otomatik olarak devreye girecek ve metodumuzu çalıştırabileceğiz.

İlk Execute işlemi ile yanıtlarımızı alabiliyoruz. Fakat 1 dakika içinde 5’ten fazla istek gelmesi durumunda API istekleri keserek hata mesajı geri döndürecektir.

Başarılı İstek Cevabı.
Dakikada 5 istek kuralını ihlal ettiğimiz zaman alacağımız hata.

Rate limit özelliğinin aktif olması ile birlikte kurallara uymayan yani limite takılan istekler 503 hatası alarak sonlanıyorlar. Peki belirli bir hata kodu ile kullanıcıları uyarmak istersek?

O zaman yine Program.cs sınıfımız içerisinde değişiklik yaparak OnRejected durumunda nasıl davranılması gerektiğini belirleyeceğiz.

Program.cs sınıfımızı aşağıdaki şekilde güncelliyoruz.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
}
));

//İsteklerin engellenmesi durumunda verilence hata kodu ve mesaj bilgisi.
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});


// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

Uygulamamızı yeniden test edersek bu defa hata kodu olarak 429 – Too Many Request hatasını aldığımızı göreceksiniz.

Kullanıcıların isteklerini kesmek istemediğimiz durumlarda sınır için belirlediğimiz sürenin dolmasını beklemeleri sağlayabiliriz. Böylelikle isteklerin kaybolmasının önüne geçmiş oluruz. Yalnız, burada gelecek istek sayısı fazla ise uygulamamız için büyük bir request queue oluşabilecektir. Bu konuda dikkatli olmak gerekmektedir.

Bu değişiklikler için de Program.cs sınıfımızı aşağıdaki gibi düzenliyoruz.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpcontext.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1),
//Kuyruk bilgileri
QueueLimit = 2,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
}
));

options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});


// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

İki yeni tanım eklemiş olduk. QueueLimit limit sınırına ulaşıldıktan sonra kaç adet isteğin belirlenen sürenin dolmasını bekleyeceğini, QueueProcessingOrder ise yeni süre başladığı zaman öncelikle hangi isteğin işleyeceğini belirlemektedir. OldestFirst ile de ilk gelenin ilk çıkacağı bir yapı kurmuş oluyoruz.

İstekler hata almadan kuyrukta bekliyor.

Uygulamamızda yaptığımız son değişiklikleri test ettiğimiz zaman sınıra ulaşan istekler beklemeye alınacak ve süre dolduktan sonra işleneceklerdir.

Uygulamamız içinde kullandığımız metotların rate limit bilgilerini farklılaştırabiliriz. Bunun için de policy yazılarak tanımlar yapılabilmektedir. Policy eklemek için Program.cs sınıfımızı yeniden güncelleyelim. GlobalLimiter özelliğini comment ile kapatarak aktif olmamasını sağlıyoruz.

//Program.cs Sınıfımız
using System.Threading.RateLimiting;

namespace BookRateLimit
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

//Rate Limit Tanımları
builder.Services.AddRateLimiter(options =>
{
//options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpcontext =>
//RateLimitPartition.GetFixedWindowLimiter(
// partitionKey: httpcontext.Request.Headers.Host.ToString(),
// factory: partition => new FixedWindowRateLimiterOptions
// {
// AutoReplenishment = true,
// PermitLimit = 3,
// Window = TimeSpan.FromMinutes(1),
// //Kuyruk bilgileri
// QueueLimit = 2,
// QueueProcessingOrder = QueueProcessingOrder.OldestFirst
// }
//));

//Eklenen Policy Bilgileri
options.AddPolicy("User", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10,
Window = TimeSpan.FromMinutes(1)
}));

options.AddPolicy("Auth", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 5,
Window = TimeSpan.FromMinutes(1)
}));

options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;

if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
{
await context.HttpContext.Response.WriteAsync(
$"İstek sınır sayısına ulaştınız. {retryAfter.TotalMinutes} dakika sonra tekrar deneyiniz. ", cancellationToken: token);
}
else
{
await context.HttpContext.Response.WriteAsync(
"İstek sınırına ulaştınız. Daha sonra tekrar deneyin. ", cancellationToken: token);
}
};
});

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

//Tanımladığımız özellikler ile rate limit middleware aktif edilir.
app.UseRateLimiter();

app.MapControllers();

app.Run();
}
}
}

Şimdi WeatherForecastController.cs için değişikliklerimizi ekleyelim. Burada amacımız her metodun ayrı policy ile çalışmasını sağlamak olacak.

//WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace BookRateLimit.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}

[EnableRateLimiting("User")]
[HttpGet]
[Route("SayUserHello")]
public string SayUserHello()
{
return "Hello User.";
}

[EnableRateLimiting("Auth")]
[HttpGet]
[Route("LoginUser")]
public string LoginUser()
{
return "User Login Completed.";
}
}
}

Burada yeni eklediğimiz metotlara [EnableRateLimiting(“”)] özellik (attribute) atamasını yapmış olduk. Böylece tanımladığımız poliçelerin (policy) metotlar için ayrı ayrı aktif olmasını sağlayabiliyoruz.

Yazdığımız metotlarda Global olarak tanımlanmış olan bir limit bilgisinin kullanılmamasını isteyebilir. Böyle bir durumda limit kurallarına dahil olmamasını istediğimiz metodumuza [DisableRateLimiting] özelliğini (attribute) eklememiz yeterli olacaktır.

[DisableRateLimiting]                
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
}

Son olarak eğer [EnableRateLimiting(“”)] ve [DisableRateLimiting] özellik (attribute )atamalarını yazdığımız Controller için yaparsak controller içerisindeki tüm metotlarımız bu özellik atamasından etkilenecektir.

//WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace BookRateLimit.Controllers
{
[EnableRateLimiting("User")]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}

[HttpGet]
[Route("SayUserHello")]
public string SayUserHello()
{
return "Hello User.";
}

[HttpGet]
[Route("LoginUser")]
public string LoginUser()
{
return "User Login Completed.";
}
}
}

Bu yazıda genel olarak .NET 7 ile gelen Rate Limit özelliğini anlatmaya çalıştım. Umarım herkes için anlaşılır olmuştur.

Görüşmek üzere.

Oluşturduğumuz uygulama kodlarına aşağıdaki bağlantıdan ulaşabilirsiniz.

https://github.com/onurkarakus/BookRateLimit

Kaynaklar :
https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#sliding-window-limit

.NET Core Web API ve JWT Token

Part Three: Security in React and WebApi in ASP.NET Core C# with  authentication and authorization by KeyCloak | by Nicolas Barlatier | Dev  Genius

Merhaba, bu yazımızda API uygulamaları için önemli bir konu olan güvenlik konusunu inceleyeceğiz.

Geliştirdiğimiz API uygulamalarının güvenli olması hem verilerin korunması hem de istenmeyen kişilerin oluşturduğumuz API uygulamalarını kendi amaçları için kullanmasının önünce geçmektedir. Genel olarak baktığımızda uygulamamızın uygun şekilde çalışmasından sonra gelen en önemli konulardan biridir güvenlik.

Bugün, API uygulamalarında güvenlik yöntemlerinden biri olan JWT Token yapısını inceleyeceğiz. Yapıyı, oluşturulmasını ve çalışma yöntemlerini kendimce anlatmaya çalışacağım.

Kimlik Doğrulama (Authentication)

Authentication, bir kullanıcının kimlik doğrulama işlemidir. Bu işlemde kullanıcı, kendisini doğrulayan bir kimlik bilgisi ile uygulama üzerinde yetki alarak kullanımını sağlar. Örneğin bir kullanıcı adı ve şifre ile kullanıcı uygulamaya kendini tanıtır. Bu kimlik bilgileri doğrulandıktan sonra kullanıcının kimliği onaylanır ve sistemdeki kaynaklara erişmesine izin verilir.

.NET Core Web API yapısında, kimlik doğrulama için kullanılacak birçok seçenek bulunmaktadır. Bunlar arasında en yaygın kullanılanı JWT (JSON Web Token) tabanlı kimlik doğrulama yapısıdır. JWT, kullanıcının kimlik bilgilerinin tutulduğu bir veri yapısıdır ve bu veri yapısının doğruluğu, içinde yer alan özel bir anahtar tarafından sağlanır. Bu nedenle, bir JWT kullanarak kimlik doğrulama işlemi gerçekleştirmek oldukça güvenlidir.

JSON Web Token (JWT) nedir ?

JSON Web Token (JWT), web uygulamaları için güvenli bir yetkilendirme yöntemidir. JWT, kullanıcı yetkilendirmesi ile ilgili bilgileri içeren token şeklinde verileri taşır ve bu verileri kullanarak uygulamalar arasında güvenli bir şekilde veri akışı sağlar.

JWT, bir header, bir payload ve bir signature olmak üzere üç parçadan oluşur. Bu parçalar “.” işareti ile ayrılarak gösterilir.

Bu bölümlerin kısaca ne anlama geldiklerine bakalım.

Başlık (Header) :
Başlık bilgisi iki bölümden oluşur. Belirte türü ve imzalama algoritması. İmzalama algoritması HMAC, SHA256 veya RSA olabilmektedir.

Yük (Payload) :
JWT içinde saklanacak veya gönderilecek olan bilgiyi temsil etmektedir. Burada kullanıcı bilgisi, kullanıcı yetki bilgileri veya ek bilgiler yer alabilmektedir.

Payload içerisinde üç tip bilgi bulunabilmektedir. Bunlar;

  • Kayıtlı Talepler (Registered Claims) :
    Gerekli olmayan fakat token ile ilgili bilgileri içeren başlıklardır. Örnek vermek gerekirse sub (subject), iss (issuer) gibi.
  • Açık Talepler (Public Claims) :
    Token’ı kullananlar tarafından eklenebilen bilgiler.
  • Özel Talepler (Private Claims) :
    Token ile aktarılacak veya paylaşılacak bilgileri içermektedir.

Talepler içinde kullanılacak olan bilgilerin rezerve edilmiş bilgilere denk gelmemesi gerekmektedir. Bunun için belirli talep isimlendirme standartları belirlenmiştir.

Kullanılan bilgilere ve kısaltmalara buradan ulaşabilirsiniz. -> https://www.iana.org/assignments/jwt/jwt.xhtml

İmza (Signature) :
İmza bölümünü oluşturmak için Base64Url olarak kodlanmış başlık (header), yük (payload) bilgisini ve bir gizli değer (secret) alınarak başlıkta belirtilen algoritma ile imzalamanız gerekmektedir.

Bu işlemlerden sonra oluşacak olan JWT token bilgisi aşağıdaki gibi olacaktır.

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

EyJzdWIiOiJCeU9udXIiLCJuYW1lIjoiT251ciBLQVJBS1XFniIsImlhdCI6MTUxNjIzOTAyMn0.8rPL9B1HFX-5QGX7qHOIIIsmPyUkFHbigg06sE3U-Pk

https://jwt.io/#debugger-io adresinde JWT token oluşturma ve bu token bilgilerini tekrar açmak ile ilgili debugger uygulamasını kontrol edebilirsiniz.

Her yerde karşılaşılan bildik JWT Token diyagramı.

Kısaca anlatmak gerekirse JWT, bir header, bir payload ve bir signature olmak üzere üç parçadan oluşur. Header, JWT’nin tipini ve encoding formatını tanımlar. Payload, yetkilendirme bilgilerini içeren verilerdir ve bu veriler genellikle JSON formatında tutulur. Signature, header ve payload verilerinin hash değerini içerir ve JWT’nin doğruluğunu kontrol etmek için kullanılır.

JWT, sunucu tarafında veri saklama ihtiyacı olmadan kullanıcı yetkilendirmesi yapılmasına olanak tanır. Bu, uygulamalar arasında güvenli veri transferi yaparken sunucu tarafında veri saklama gereksinimini ortadan kaldırır ve aynı zamanda sunucunun performansını da arttırır.

JWT’nin avantajları arasında, verilerin kolayca okunabilir ve kodlanabilir olması, sunucu tarafında veri saklama gereksinimi olmaması, uygulamalar arasında güvenli veri akışı sağlaması gibi unsurlar bulunur.

Kimlik doğrulama (Authentication) ve yetki (Authorization) nedir?

Kimlik doğrulama ve yetkilendirme, web uygulamalarının güvenliği için iki temel kavramdır.

Kimlik doğrulama (Authentication), daha önce de belirttiğim gibi kullanıcının kimliğini doğrulama işlemidir. Bu işlem, kullanıcının kimliğini kanıtlamasını gerektirir. Örneğin, bir kullanıcının kullanıcı adı ve şifre gibi kimlik bilgileri kullanılarak doğrulanması işlemi kimlik doğrulama (authentication) olarak adlandırılır. Bu işlem sonucunda kullanıcının kimliği doğrulanır ve uygulama tarafından yetkilendirme işlemi yapılabilmesi için uygulamanın da daha sonra anlayabileceği bir token oluşturulur.

Yetkilendirme (Authorization) ise, kullanıcının yapabileceği işlemleri belirleme işlemidir. Kullanıcının uygulama tarafından kimliği doğrulandıktan sonra yapılması gereken işlemdir. Örnek olarak, kullanıcı sadece belirli bir role ait işlemlerin yapılması için yetkilendirilebilir. Yönetici (admin) rolüne sahip bir kullanıcının uygulama içerisinde bu role ait işlemleri yapabilmesi, farklı rollerde olan işlemleri gerçekleştirmek istediği zaman ise belirli bir mesaj ile uyarılması sağlanabilmektedir.

Token-based authentication’ın avantajları

JWT Authentication, birçok web uygulamasında tercih edilen bir kimlik doğrulama yöntemidir. Bu yöntem, kullanıcıların web uygulamanıza kayıt olmaları veya giriş yapmaları için gereken bir parola veya benzeri bilgileri kullanmak yerine, kullanıcının sahip olduğu bir token kullanarak kimlik doğrulama işlemini gerçekleştirir.

Genel olarak web uygulamalarında kullanılan Token-based authentication ölçeklenebilir, performanslı ve güvenli bir kimlik doğrulama yöntemidir.

  • Durumsuz (Stateless) : Sunucu tarafında durum bilgisi tutulmasına gerek kalmadan token kullanıcı tarafından uygulamaya gönderilebilir ve işlenebilir. Bu sebepten dolayı hızlı ve ölçeklenebilir bir yapıya sahiptir.
  • Güvenli (Trustworthy) : Token bilgileri kullanıcı ve uygulama arasında genellikle şifrelenir ve gizli tutulur. Bu sebeple güvenli bir yapıya sahiptir. Token’lar, uygulama tarafından işlenmeden önce, veri bütünlüğü ve doğruluğu sağlamak için imzalanır.
  • Esnek (Flexible) : Token-based authentication platform bağımsız olarak kullanılabilmektedir. Bu da yeni gelişen teknoloji ve uygulama yapıları ile rahat çalışma olanağı sağlamaktadır.
  • Performans (Performance) : Token-based authentication yapısını kullanan uygulamalar gelen her kullanıcı isteği için bir kimlik doğrulama işlemi yapmaz. Bunun yerine gönderilen token bilgisinin doğruluğu kontrol edilir. Bu da uygulamalarımızın daha performanslı ve hızlı çalışmasının sağlar.
  • Ölçeklenebilir (Scalable) : Token-based authentication, sunucular arasında yük dengeleme yaparken de kullanışlıdır. Token’lar sayesinde, yük dengeleme işlemi kolaylaşır ve her sunucu aynı kimlik doğrulama ve yetkilendirme işlemini yapabilir.
  • Özelleştirilebilir (Customizable) : Kullanıcılara özelleştirilmiş izinler ve yetkiler verme işlemini kolaylaştırır. Token’lar, kullanıcılara özel izinlerle birlikte verilebilir ve uygulamanın ihtiyacına göre özelleştirilebilir.
  • Düşük maliyetli (Cost-Effective) : Token’lar genellikle açık kaynaklı kütüphanelerle kolayca oluşturulabilir ve yönetilebilir. Böylelikle uygulama için kimlik doğrulama ve yetkilendirme işlemlerini uygun maliyetli bir şekilde çözüme kavuşturur.

Önemli noktalar bir tanesi kimlik doğrulama işleminin tek başına bir yetkilendirme mekanizması olmamasıdır. Kullanıcıların doğrulanmasının ardından yetki yapısı içerisinde uygulamamızı kullanacak seviyelerin belirlenmesi gerekmektedir. Böylelikle uygulamamız daha güvenli bir hale gelecektir.

.NET Core Web API uygulaması ve JWT Token

Örnek olarak hazırlayacağımız API uygulamamızın biraz yapısından bahsederek başlayalım.

API içerisinde iki farklı controller sınıfımız olacak. Bu sınıfların ilki kullanıcıların giriş işlemlerini gerçekleştirebilecekleri AuthContoller bir diğeri ise kullanıcıların kitap bilgilerine ulaşabilecekleri BookController.

BookController içerisinde iki metot hazırlayacağız ve bu metotların biri giriş işlemini yapmış kullanıcılar için diğeri ise tüm kullanıcılar için çalışacak.

Hızlı bir örnek olması açısından bir veri tabanı üzerinde kayıt tutmak yerine kullanıcı bilgilerini, dönecek olan cevap bilgilerini hard-coded olarak uygulama içerisinde (In-Memory) tutacağız. Tabii burada kullanıcı yönetimi için ASP.NET Identity veya başka bir kimlik doğrulama yapısı kullanarak da kullanıcıların kimlik doğrulama ve yetkilendirme işlemlerini yapabilirsiniz.

Öncelikle projemizi oluşturalım. JwtAuthForBooks isimli bir ASP.NET Core Web Api projesi oluşturuyoruz.

Uygulama kodumuzda hazır olarak gelen Contorllers/WeatherForecastController.cs ve WeatherForecast.cs sınıflarını silebiliriz.

İlk önce kullanıcıların girişlerini yapacağımız Model, Controller, Interface ve Service sınıflarını geliştirelim.

Models isimli bir klasör ile birlikte UserLoginRequest.cs ve UserLoginResponse.cs isimli iki sınıf oluşturuyoruz.

//UserLoginRequest.cs 

namespace JwtAuthForBooks.Models;

public class UserLoginRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
//UserLoginResponse.cs

namespace JwtAuthForBooks.Models;

public class UserLoginResponse
{
public bool AuthenticateResult { get; set; }
public string AuthToken { get; set; }
public DateTime AccessTokenExpireDate { get; set; }
}

Interfaces isminde bir klasör oluşturarak IAuthService.cs isminde bir sınıf oluşturuyoruz. Burada Controller sınıfımıza ekleyebilmek için (inject) servis ara yüzümüzü tanımlayacağız.

//IAuthService.cs

using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface IAuthService
{
public Task<UserLoginResponse> LoginUserAsync(UserLoginRequest request);
}

Services isminde bir klasör oluşturarak AuthService.cs isminde bir sınıf oluşturuyoruz. Bu sınıf ise bizim login işlemlerimizi yapacak olan servis olacaktır.

//AuthService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Services;

public class AuthService : IAuthService
{
public Task<UserLoginResponse> LoginUserAsync(UserLoginRequest request)
    {
        UserLoginResponse response = new();

if (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
        {
throw new ArgumentNullException(nameof(request));
        }

if (request.Username == "onur" && request.Password == "123456")
        {
            response.AccessTokenExpireDate = DateTime.UtcNow;
            response.AuthenticateResult = true;
            response.AuthToken = string.Empty;            
        }

return Task.FromResult(response);
    }
}

Controllers klasörümüz içerisine AuthController.cs isminde bir Api Controller oluşturup aşağıdaki gibi düzenliyoruz. Bu sınıf bizim login işlemlerimizi yaptığımız controller olacaktır.

//AuthController.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtAuthForBooks.Controllers;

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
readonly IAuthService authService;

public AuthController(IAuthService authService)
 {
this.authService = authService;
 }

    [HttpPost("LoginUser")]
    [AllowAnonymous]
public async Task<ActionResult<UserLoginResponse>> LoginUserAsync([FromBody] UserLoginRequest request)
    {
var result = await authService.LoginUserAsync(request);

return result;
    }
}

Servisin aktif olması için Program.cs sınıfı içerisinde servisimizi kayıt ediyoruz.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//Eklenecek olan kayıt satırı.
builder.Services.AddTransient<IAuthService, AuthService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Uygulamamızı çalıştırdığımız zaman controller üzerinden login işlemini yapabiliyoruz. Burada token ve tarih bilgileri boş olarak geliyor. Birazdan JWT token ile ilgili geliştirmelerimizi ve kullanacağımız BookController sınıfımızı ekleyeceğiz.

Öncelikle JWT işlemleri için kullanacağımız Nuget paketini ekleyelim. Aşağıdaki paket ismini bulabilirsiniz. .NET 6 ile projemizi oluşturduğumuz için 6.0.14 versiyonunu almamız gerekmektedir. Eğer siz .NET 7 ile projenizi oluşturduysanız 7 versiyonunu alabilirsiniz.

Microsoft.AspNetCore.Authentication.JwtBearer

Token işlemleri yapmak için kullanacağımız model, servis ara yüzü ve servis sınıfını geliştirelim. Model bilgileri Models , ara yüz bilgileri Interfaces, servis sınıfı ise Services klasörü içinde olacaktır.

//GenerateTokenRequest.cs

namespace JwtAuthForBooks.Models;

public class GenerateTokenRequest
{
public string Username { get; set; }
}
//GenerateTokenResponse.cs

namespace JwtAuthForBooks.Models;

public class GenerateTokenResponse
{
public string Token { get; set; }
public DateTime TokenExpireDate { get; set; }
}
//ITokenService.cs

using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface ITokenService
{
public Task<GenerateTokenResponse> GenerateToken(GenerateTokenRequest request);
}
//TokenService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JwtAuthForBooks.Services;

public class TokenService : ITokenService
{
readonly IConfiguration configuration;

public TokenService(IConfiguration configuration)
    {
this.configuration = configuration;
    }

public Task<GenerateTokenResponse> GenerateToken(GenerateTokenRequest request)
    {
        SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["AppSettings:Secret"]));

var dateTimeNow = DateTime.UtcNow;

        JwtSecurityToken jwt = new JwtSecurityToken(
                issuer: configuration["AppSettings:ValidIssuer"],
                audience: configuration["AppSettings:ValidAudience"],
                claims: new List<Claim> {
new Claim("userName", request.Username)
                },
                notBefore: dateTimeNow,
                expires: dateTimeNow.Add(TimeSpan.FromMinutes(500)),
                signingCredentials: new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256)
            );

return Task.FromResult(new GenerateTokenResponse
        {
            Token = new JwtSecurityTokenHandler().WriteToken(jwt),
            TokenExpireDate = dateTimeNow.Add(TimeSpan.FromMinutes(500))
        });
    }
}

Son olarak da appsettings.json dosyasında da aşağıdaki değişiklikleri yapmamız gerekiyor.

//appsettings.json

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",

"AppSettings": {
"ValidAudience": "AudienceInformation",
"ValidIssuer": "IssuerInformation",
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
}
}

Login metodumuzu yeniden çalıştırdığımız zaman JWT Token bilgisinin oluştuğunun görebileceğiz.

Burada durup biraz servisi incelememiz gerekiyor. Bu servis içerisinde bulunan GenerateToken metodunda kullanıcının girdiği kullanıcı adını da kullanarak bir JWT token oluşturuyoruz.

JwtSecurityToken sınıfı bizim için JWT token için gerekli bilgileri alarak token oluşturma işlemini gerçekleştiriyor. Peki, bu bilgilerin neler olduğunun incelemeye çalışalım.

Önemli noktalardan biri JwtSecuritToken sınıfına signingCredentials olarak geçtiğimiz Secret değeri. Bu değer sayesinde token bilgimizi imzalayabiliyoruz. Bu imzalama işlemini de appsetting.json dosyamız içinde belirlediğimiz değerden bir SymmetricSecurityKey oluşturarak yapıyoruz. Bu değeri bize gönderilen token üzerinden alarak kontrol edebiliriz ve bizim tarafımızdan imzalandığını anlayabiliriz.

Burada dikkat edilmesi gereken konu token bilgileri okunabilir yapılardır. Bundan dolayı özel bilgilerin token içinde olmaması gerekmektedir.

issuer bilgisi token değerinin kimin tarafından dağıtıldığını yani bizi belirten bir değerdir. Örnek olarak MyBookStore gibi bir değer belirtilebilir.

audience oluşturulacak olan token değerinin kimler tarafından kullanılacağını belirler. Bir site (www.test.com) için üretilecek olan token bilgisi olabilir.

expires Token bilgisinin ne kadar süre ile aktif olacağını belirler. Bu süre sonrasında token kullanılmaz halde olacak ve Api metotlarının kullanımları sırasında yetki hatası verilecektir.

notBefore Token bilgisi üretildikten belirli bir zaman sonra devreye girmesini istersen burada bir değer geçip bu özelliği aktif edebiliriz. Biz token bilgisinin hemen aktif olmasının istediğimiz için üretildiği zamanı başlama değer olarak belirledik.

claims Token bilgisi içinde saklamak istediğimiz bilgileri eklediğimiz bölüm olarak tanımlanabilir. Burada özel olmayacak şekilde istediğimiz bir bilgiyi token içinde tanımlayıp daha sonra bize bu token gönderildiği zaman bu değerleri alarak belirli işlemler yapabiliriz.

Şimdi de yetkilendirme testini yapacağımız BookController sınıfını geliştirelim. Ardından uygulamamız için kimlik doğrulamayı aktif edeceğiz ve testimizi yapabileceğiz.

Senaryomuzda dhaa önce belirttiğimiz gibi iki adet controller metodumuz olacak. bu metotlardan birisi kitap listesini dönecek ve tüm kullanıcılar için çalışabilecek. İkinci metodumuz ise sadece login işlemini başarılı bir şekilde yapmış ve token bilgisini gönderen yetkili kullanıcılar için çalışacak ve kitap bilgisini bize dönecek.

Models klasörümüze BookInformation.cs, BookTitle.cs ve GetBookInformationByIdRequest.cs isminde üç sınıf oluşturarak başlıyoruz.

//BookInformation.cs

namespace JwtAuthForBooks.Models;

public class BookInformation
{
public int BookId { get; set; }
public string Isbn { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AuthorName { get; set; }
}
//BookTitle.cs

namespace JwtAuthForBooks.Models;

public class BookTitle
{
public int BookId { get; set; }
public string Isbn { get; set; }
public string Title { get; set; }
}
//GetBookInformationByIdRequest.cs

namespace JwtAuthForBooks.Models;

public class GetBookInformationByIdRequest
{
public int BookId { get; set; }
}

Servis düzenimizi bozmayalım. Interfaces klasörümüze IBookService.cs ve Services klasörümüze BookService.cs isminde iki sınıf oluşturuyoruz ve aşağıdaki gibi düzenliyoruz.

//IBookService.cs

using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Interfaces;

public interface IBookService
{
public Task<List<BookTitle>> GetBookTitlesAsync();
public Task<BookInformation> GetBookInformationByIdAsync(GetBookInformationByIdRequest request);
}
//BookService.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;

namespace JwtAuthForBooks.Services;

public class BookService : IBookService
{
readonly List<BookInformation> bookInformations;

public BookService()
    {
        bookInformations = new List<BookInformation> {
new BookInformation { BookId = 1, Isbn = "9752115047", Title ="22/11/63",  AuthorName = "Stephen King",  Description = "22 Kasım 1963’te, bütün bunları değiştirme şansınız olsaydı?" },
new BookInformation { BookId = 2, Isbn = "1476762740", Title ="Uyuyan Güzeller",  AuthorName = "Stephen King *  Owen King",  Description = "Şimdi burada dünyanın kaderine karar verilecek." },
new BookInformation { BookId = 3, Isbn = "9752126049", Title ="Enstitü",  AuthorName = "Stephen King",  Description = "Enstitü..." }
        };
    }

public Task<BookInformation> GetBookInformationByIdAsync(GetBookInformationByIdRequest request)
    {
var loadedBookInformation = bookInformations.FirstOrDefault(p => p.BookId == request.BookId);

return Task.FromResult(loadedBookInformation);
    }

public Task<List<BookTitle>> GetBookTitlesAsync()
    {
var booktitleList = bookInformations.Select(book => GenerateBookTitleForList(book)).ToList();

return Task.FromResult(booktitleList);
    }

private static BookTitle GenerateBookTitleForList(BookInformation book)
    {
return new BookTitle { BookId = book.BookId, Title = book.Title, Isbn = book.Isbn };
    }
}

Şimdi de BookController.cs sınıfımızı Controllers klasörüne ekliyoruz.

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtAuthForBooks.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
readonly IBookService bookService;

public BookController(IBookService bookService)
 {
this.bookService = bookService;
 }

    [HttpPost("GetBookTitles")]
    [AllowAnonymous]
public async Task<ActionResult<List<BookTitle>>> GetBookTitles()
    {
var result = await bookService.GetBookTitlesAsync();

return result;
    }

    [HttpPost("GetBookInformationById")]
    [Authorize]
public async Task<ActionResult<BookInformation>> GetBookInformationById([FromBody] GetBookInformationByIdRequest request)
    {
var result = await bookService.GetBookInformationByIdAsync(request);

return result;
    }
}

Burada dikkat edilmesi gereken konu tüm kullanıcıların yetki almadan kullanacakları metodumuza [AllowAnonymous], yetkili olarak kullanılacak metodumuza ise [Authorize] özelliklerini tanımlıyoruz. İsimlerinden de anlaşılacağı gibi anonim ve yetkili çalışacak metotlarımızı bu şekilde belirtmiş olduk.

Şimdi de Program.cs sınıfımıza servisimizi kayıt edelim.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddTransient<IAuthService, AuthService>();
builder.Services.AddTransient<ITokenService, TokenService>();

//Eklenecek olan kayıt satırı.
builder.Services.AddTransient<IBookService, BookService>(); 

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Tüm geliştirmeleri yaptığımıza göre şimdi de uygulamamız için yetkilendirme yapısının aktif edelim. Bunun için yine Program.cs sınıfımız içinde tanımlamalarımızı yapalım.

//Program.cs

using JwtAuthForBooks.Interfaces;
using JwtAuthForBooks.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddTransient<IAuthService, AuthService>();
builder.Services.AddTransient<ITokenService, TokenService>();
builder.Services.AddTransient<IBookService, BookService>();

//Eklenecek olan satırlar
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["AppSettings:ValidIssuer"],
        ValidAudience = builder.Configuration["AppSettings:ValidAudience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["AppSettings:Secret"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});

builder.Services.AddAuthorization();

//Eklenecek olan satırların sonu

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//Eklenecek olan satırlar

app.UseAuthentication();
app.UseAuthorization();

//Eklenecek olan satırların sonu

app.MapControllers();

app.Run();

Projemizin son durumu aşağıdaki gibi olacaktır.

AddAuthentication() tanımı ile JWT token yapısının çalışmasını tanımladık. AddAuthorization() ile yetkilendirmenin olacağınız belirttik. UseAuthentication() ve UseAuthorization() ile de bu tanımları aktif ettik.

Metotlarımızı test edebiliriz. Test sırasında [AllowAnonymous] olarak tanımlı metodumuz sorunsuz bir şekilde çalışacaktır.

Yalnız, login işlemini başarılı bir şekilde geçmemiş ve token bilgisini almamış kullanıcılar GetBookInformationById metodunu çalıştırdıkları zaman 401 Unauthorized hatası alacaklardır.

Login metodu üzerinden kullanıcı adı ve şifre ile login olup token bilgisini alalım. Ardından GetBookInformationById metoduna bu token bilgisini göndererek işlemi tekrar deneyeceğiz.

Başarılı bir şekilde giriş işlemini yaptık ve token bilgisini aldık.

Şimdi bu token bilgisini GetBookInformationById metoduna göndererek işlemi yenidne deneyeceğiz. Yalnız, burada token gönderirken header bilgileri içerisinde Authorization key değeri ile bunu ileteceğiz. Bearer <token_bilgisi> şeklinde.

Ardından metodumuzu yeniden çalıştırdığımızda yetkimizi almış bir şekilde metot cevabının geldiğini görebiliriz.

Bu yazıda JWT Token konusunu temel olarak dilim döndüğünce anlatmaya çalıştım. Umarım yararlı bilgiler arasına girer sizler için.

Oluşturduğumuz uygulama kodlarına aşağıdaki bağlantıdan ulaşabilirsiniz.

https://github.com/onurkarakus/JwtAuthForBooks

Görüşmek üzere.

.NET 6 Web API, CQRS and MediatR Library

Hello, in this article, we will work on the Command Query Responsibility Segregation (CQRS) and Mediator design principles together.

The MediatR library is based on two main principles: CQRS and Mediator.

You can access the codes of the project from the following link: https://github.com/onurkarakus/CqrsMediatorPattern

First of all, what is CQRS? I will try to explain it a bit.

As the name suggests, CQRS is a principle of separating commands and queries. In the structures we usually use, we try to manage CRUD operations through a single repository. CQRS brings a design logic that is built on this separation.

Command objects are defined as objects that make changes on data (CUD), while queries are objects that perform reading operations (R) on data.

The working logic of the CQRS principle

As you can see, this principle supports the complete separation of read and command operations within the application. Here, a specific boundary or method for the separation process is not explicitly defined. The separation process can be done based on a model, application, or database if desired. Of course, these rules can change depending on the size and complexity of the application.

The short definition and basic principle of this principle can be defined as follows:

“A method should change the state of an object or return a value. It should not do both.”

Advantages:

  • The segregation of commands and queries prevents complexity in the creation of ViewModel objects used to present data to users from data models (mapping).
  • If you are designing an application that includes a large number of database operations, you must prioritize performance, which means that reading and writing operations must be separate objects, allowing for separate control, management, and performance optimization.
  • There will be fewer operation lock-ups (e.g. db lock) because reading and writing operations can be scaled separately.
  • It ensures that the application codes are clear and readable.
  • It can speed up the adaptation of new programmers to the project.

Disadvantages:

  • If you are developing a small-scale application, adopting the CQRS principle can be overwhelming.
  • Using only the CQRS principle creates a lot of code development needs, causing the development time of the project to extend.

What is Mediator?

The Mediator Design Pattern is a principle that provides us with the opportunity to reduce dependencies between objects. By starting from the idea of “the class with the least dependencies is the easiest to use”, this design pattern aims to make objects have the same interface by adapting the same interface. This principle aims to ensure that objects communicate not with each other, but through this interface.

With this pattern, the objects we use communicate with each other not through their dependencies, but through an intermediary.

A common example given when researching is the airport, tower, and plane example. Planes communicating with the tower and not with each other as long as it is not necessary can be a simple example of this principle.

MediatR library is a library that makes it easier for us to implement the Mediator Design Pattern.

Now, let’s try to examine the CQRS design pattern example on a project first. Then, let’s make our project more user-friendly with the MediatR library using the Mediator design pattern.

Create a new ASP.NET Core Web API project and name it.

Creation screen for the project named Cqrs Mediatr Pattern

When our project is first created, we should clean up the default files that come with it to keep it organized.

Our scenario is simple. We have an application containing book information and an enum value that holds the types of these books. Let’s perform CRUD operations on the application.

To do this, we create folders named Domain -> Entities, Domain -> Enum and define our object definitions.

We have three new object definitions.

The Book.cs that will store our book information.

using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.Domain.Entities;

public class Book
{
public long Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public BookGenre BookGenre { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
}

BookGenre.cs which is the enum type we will hold the book types.

namespace CqrsMediatorPattern.Domain.Enums;

public enum BookGenre
{
None = 0,
Horror = 1,
Fable = 2,
Fiction = 3,
Mystery = 4
}

For our database operations, we need to add Entity Framework Core packages to our project. For this, you can install the following packages as package manager (Nuget Packet Manager), CLI or package reference (PacketReference).

Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore
Adding packages via Nuget package manager

Now we can design our Dbcontext class. By opening a folder named Data -> Context, we design our Dbcontext class there. We are making an arrangement so that the name of our class is BookDbContext.

BookDbContext.cs
using CqrsMediatorPattern.Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace CqrsMediatorPattern.Data.Context;

public class BookDbContext : DbContext
{
public BookDbContext(DbContextOptions<BookDbContext> options) : base(options)
{


}

public DbSet<Book> Books { get; set; }
}

We need to update the ConnectionString information in our appsettings.json file. We add the information as follows.

{your_username} : Username specified for Sql Server
{your password} : Your password for Sql Server

{
"ConnectionString": {
"SqlDbConnectionString": "Data Source=.\\SQLEXPRESS; Initial Catalog=CqrsMediatr_example;Persist Security Info=True;User Id={kullanici_adiniz};Password={şifreniz};MultipleActiveResultSets=true;TrustServerCertificate=True"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Then we need to register the DbContext object that we created in our Program.cs class. For this, we add as follows in the Program.cs class.

using CqrsMediatorPattern.Data.Context;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Then, we run the following commands on the Package Manager Console, respectively, to create our database and tables.

Add-Migration Init
Update-Database
Add-Migration
Update-Database

Then, when we check it over Sql Server, we can see that the database and tables have been created.

Şimdi CQRS için geliştirmelerimizi içeriyor. İlk olarak CQRS prensibini hayata geçireceğiz. İyi ve kötü yanlarını kod olarak gördükten sonra Mediator ilkelerinin yararları ve MediatR kütüphanesinin kullanımına bakacağız.

Öncelikle CQRS isminde bir klasör açarak Commands, Handlers ve Queries bilgilerimizi burada oluşturuyoruz.

CQRS folder and its contents

Commands: It will be the folder where all the command (CUD) operations to be made within the application are defined.

Queries: It will be the folder where all the query (R) operations to be made within the application are defined.

Handler : It will be the folder where our models are defined, which will process all commands and queries at a common point and return the answer that will be suitable for users.

Now let’s design our objects in the Commands folder.

CreateBookCommandRequest.cs and CreateBookCommandResponse.cs

using CqrsMediatorPattern.Domain.Entities;
using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class CreateBookCommandRequest
{
public string? Title { get; set; }
public string? Description { get; set; }
public BookGenre? BookGenre { get; set; }
}
namespace CqrsMediatorPattern.CQRS.Commands.Response;

public class CreateBookCommandResponse
{
public bool IsSuccess { get; set; }
public int CreatedBookId { get; set; }
}

DeleteBookCommandRequest.cs and DeleteBookCommandResponse.cs

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class DeleteBookCommandRequest
{
public int BookId { get; set; }
}
namespace CqrsMediatorPattern.CQRS.Commands.Response;

public class DeleteBookCommandResponse
{
public bool IsSuccess { get; set; }
}
Final state after adding our command objects

Now let’s design our objects in the Queries folder.

GetAllBooksQueryRequest.cs and GetAllBookQueryResponse.cs

namespace CqrsMediatorPattern.CQRS.Queries.Request;

public class GetAllBooksQueryRequest
{

}
using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.CQRS.Queries.Response;

public class GetAllBooksQueryResponse
{
public long Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public BookGenre BookGenre { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
}

Now it’s time for the Handlers folder. Here we will develop structures that will process command (Command) and query (Query) requests.

CommandHandlers

CreateBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using CqrsMediatorPattern.Domain.Entities;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class CreateBookCommandHandler
{
private readonly BookDbContext _dbContext;

public CreateBookCommandHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public CreateBookCommandResponse CreateBook(CreateBookCommandRequest request)
{
_ = _dbContext.Books.Add(new Book
{
BookGenre = request.BookGenre.Value,
Created = DateTime.Now,
Description = request.Description,
Title = request.Title
});

var idInformation = _dbContext.SaveChanges();

return new CreateBookCommandResponse { IsSuccess= true, CreatedBookId = idInformation };
}
}

DeleteBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class DeleteBookCommandHandler
{
private readonly BookDbContext _dbContext;

public DeleteBookCommandHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public DeleteBookCommandResponse DeleteBook(DeleteBookCommandRequest request)
{
var findBookResult = _dbContext.Books.FirstOrDefault(p => p.Id == request.BookId);

if (findBookResult == null)
{
return new DeleteBookCommandResponse { IsSuccess = false };
}

_dbContext.Books.Remove(findBookResult);

_ = _dbContext.SaveChanges();

return new DeleteBookCommandResponse { IsSuccess = true };
}
}

QueryHandlers

GetAllBooksQueryHandler.cs

using CqrsMediatorPattern.CQRS.Queries.Request;
using CqrsMediatorPattern.CQRS.Queries.Response;
using CqrsMediatorPattern.Data.Context;

namespace CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;

public class GetAllBooksQueryHandler
{
private readonly BookDbContext _dbContext;

public GetAllBooksQueryHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public List<GetAllBooksQueryResponse> GetAllBooks(GetAllBooksQueryRequest request)
{
return _dbContext.Books.Select(x => new GetAllBooksQueryResponse()
{
BookGenre = x.BookGenre,
Created = x.Created,
Description = x.Description,
Id = x.Id,
Title = x.Title,
Updated = x.Updated
}).ToList();
}
}

Basically, we have developed our classes with the CQRS principle. Now we need to add the classes we developed as Handler to our application as services and then develop our Controller class. The final version of our Program.cs class will be as follows.

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddTransient<CreateBookCommandHandler>();
builder.Services.AddTransient<DeleteBookCommandHandler>();
builder.Services.AddTransient<GetAllBooksQueryHandler>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Now let’s develop our Controller class. We create an API Controller named BookController inside our Controllers folder.

The final version of our BookController.cs class will be as follows.

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.CQRS.Queries.Request;
using Microsoft.AspNetCore.Mvc;

namespace CqrsMediatorPattern.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
readonly CreateBookCommandHandler createBookCommandHandler;
readonly DeleteBookCommandHandler deleteBookCommandHandler;
readonly GetAllBooksQueryHandler getAllBooksQueryHandler;

public BookController(CreateBookCommandHandler createBookCommandHandler, DeleteBookCommandHandler deleteBookCommandHandler, GetAllBooksQueryHandler getAllBooksQueryHandler)
{
this.createBookCommandHandler = createBookCommandHandler;
this.deleteBookCommandHandler = deleteBookCommandHandler;
this.getAllBooksQueryHandler = getAllBooksQueryHandler;
}

[HttpGet]
public IActionResult Get([FromQuery] GetAllBooksQueryRequest request)
{
var allBooks = getAllBooksQueryHandler.GetAllBooks(request);

return Ok(allBooks);
}

[HttpPost]
public IActionResult Post([FromBody] CreateBookCommandRequest request)
{
CreateBookCommandResponse response = createBookCommandHandler.CreateBook(request);

return Ok(response);
}

[HttpDelete("{id}")]
public IActionResult Delete([FromQuery] DeleteBookCommandRequest request)
{
DeleteBookCommandResponse response = deleteBookCommandHandler.DeleteBook(request);
return Ok(response);
}
}

Then we can run our project and test it via Swagger.

We will first add a book and then take the books as a list.

Adding a New Book
New Book Added Result
Book List
Book List Result
Bookinformation in the database

As you can see, we were able to separate our Command and Query objects by sending our requests through the BookController and perform our operations with different flows.

We performed these operations for a single object and table. But as the project grows, the number of Command and Query objects will increase. It seems that the classes we add to our application as a service will increase and DI operations of feature Controller classes will begin to be a problem.

Another bad situation is that we determine the response information of the incoming request information. Jobs such as determining the Handler information that is appropriate according to the incoming request on the side of the software will also cause difficulties for us in the development stages of the project and increase the complexity.

In such a case, the Mediator principle will support us and by adding this principle with the MediatR library, we will be able to use our CQRS objects more easily and comfortably. In this way, we will be able to ensure that our Command, Query and Handler objects work and are managed in accordance with their behavior.

Let’s start preparing MediatR developments together.

First, we start by adding the MediatR library to our project.

To add the MediatR library to our project, you can install the following packages as package manager (Nuget Packet Manager), CLI or package reference (PacketReference).

MediatR
MediatR.Extensions.Microsoft.DependencyInjection
Installing MediatR with Nuger package manager

Then we perform the service registration process in our Program.cs class.

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using MediatR;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddTransient<CreateBookCommandHandler>();
builder.Services.AddTransient<DeleteBookCommandHandler>();
builder.Services.AddTransient<GetAllBooksQueryHandler>();

builder.Services.AddMediatR(typeof(Program));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

The MediatR library offers us two interfaces. Thanks to these interfaces, we can determine our Request and RequestHanler objects and enable them to take their responsibilities in accordance with the Mediator principle.

These interfaces are as follows.

IRequest :

namespace MediatR
{
//
// Summary:
// Marker interface to represent a request with a void response
public interface IRequest : IRequest<Unit>, IBaseRequest
{
}
}

IRequestHandler :

namespace MediatR
{
//
// Summary:
// Defines a handler for a request
//
// Type parameters:
// TRequest:
// The type of request being handled
//
// TResponse:
// The type of response from the handler
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse>
{
//
// Summary:
// Handles a request
//
// Parameters:
// request:
// The request
//
// cancellationToken:
// Cancellation token
//
// Returns:
// Response from the request
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}
}

Now let’s rearrange all our Command, Query and Handler classes that we created earlier for MediatR. What we will do here is to ensure that the CommandRequest objects adapt the IRequest interfaces, and our Handler objects adapt the IRequestHandler interfaces.

CreateBookCommandRequest.cs

using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Domain.Enums;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class CreateBookCommandRequest: IRequest<CreateBookCommandResponse>
{
public string? Title { get; set; }
public string? Description { get; set; }
public BookGenre? BookGenre { get; set; }
}

DeleteBookCommandRequest.cs

using CqrsMediatorPattern.CQRS.Commands.Response;
using MediatR;
namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class DeleteBookCommandRequest: IRequest<DeleteBookCommandResponse>
{
public int BookId { get; set; }
}

GetAllBooksQueryRequest.cs

using CqrsMediatorPattern.CQRS.Queries.Response;
using MediatR;
namespace CqrsMediatorPattern.CQRS.Queries.Request;

public class GetAllBooksQueryRequest:IRequest<List<GetAllBooksQueryResponse>>
{
}

Now let’s make changes to our Handler objects. We will implement an interface like IRequestHandler<request,response> for Handler objects. Then we will add the gleen Handler method with the interface. The difference here is that the newly added Handle method will work as async.

CreateBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using CqrsMediatorPattern.Domain.Entities;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class CreateBookCommandHandler: IRequestHandler<CreateBookCommandRequest, CreateBookCommandResponse>
{
private readonly BookDbContext _dbContext;

public CreateBookCommandHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public Task<CreateBookCommandResponse> Handle(CreateBookCommandRequest request, CancellationToken cancellationToken)
{
_ = _dbContext.Books.Add(new Book
{
BookGenre = request.BookGenre.Value,
Created = DateTime.Now,
Description = request.Description,
Title = request.Title
});

var idInformation = _dbContext.SaveChanges();

return Task.FromResult(new CreateBookCommandResponse { IsSuccess = true, CreatedBookId = idInformation });
}
}

DeleteBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class DeleteBookCommandHandler : IRequestHandler<DeleteBookCommandRequest, DeleteBookCommandResponse>
{
private readonly BookDbContext _dbContext;

public DeleteBookCommandHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public Task<DeleteBookCommandResponse> Handle(DeleteBookCommandRequest request, CancellationToken cancellationToken)
{
var findBookResult = _dbContext.Books.FirstOrDefault(p => p.Id == request.BookId);

if (findBookResult == null)
{
return Task.FromResult(new DeleteBookCommandResponse { IsSuccess = false });
}

_dbContext.Books.Remove(findBookResult);

_ = _dbContext.SaveChanges();

return Task.FromResult(new DeleteBookCommandResponse { IsSuccess = true });
}
}

GetAllBooksQueryHandler.cs

using CqrsMediatorPattern.CQRS.Queries.Request;
using CqrsMediatorPattern.CQRS.Queries.Response;
using CqrsMediatorPattern.Data.Context;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;

public class GetAllBooksQueryHandler: IRequestHandler<GetAllBooksQueryRequest, List<GetAllBooksQueryResponse>>
{
private readonly BookDbContext _dbContext;

public GetAllBooksQueryHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
}

public Task<List<GetAllBooksQueryResponse>> Handle(GetAllBooksQueryRequest request, CancellationToken cancellationToken)
{
return Task.FromResult(_dbContext.Books.Select(x => new GetAllBooksQueryResponse()
{
BookGenre = x.BookGenre,
Created = x.Created,
Description = x.Description,
Id = x.Id,
Title = x.Title,
Updated = x.Updated
}).ToList());
}
}

Now we are making our BookController class suitable for MediatR. The change we will make here is that MediatR directs our classes for us, by injecting itself into the Controller class and leaving the routing process to the library.

BookController.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.CQRS.Queries.Request;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace CqrsMediatorPattern.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
readonly IMediator mediator;

public BookController(IMediator mediator)
{
this.mediator = mediator;
}

[HttpGet]
public async Task<IActionResult> Get([FromQuery] GetAllBooksQueryRequest request)
{
var allBooks = await mediator.Send(request);

return Ok(allBooks);
}

[HttpPost]
public async Task<IActionResult> Post([FromBody] CreateBookCommandRequest request)
{
CreateBookCommandResponse response = await mediator.Send(request);

return Ok(response);
}

[HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromQuery] DeleteBookCommandRequest request)
{
DeleteBookCommandResponse response = await mediator.Send(request);

return Ok(response);
}
}

As you can see, we have removed the injection information of our Handler classes that we have added before. We rearranged our Controller methods to be async and left all the work to the object we added with the name of the mediator.

Finally, we can remove our Handler services that we previously registered in our Program.cs class.

Program.cs

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using MediatR;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddMediatR(typeof(Program));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

When we run our application and list the books, we will see that the books are coming without any problems.

In this article, we examined the conveniences that the CQRS principle provides us in terms of performance, management and development while creating large-scale applications. We tried to examine how we can produce easy solutions with the MediatR library for CQRS, which comes with its good sides as well as bad sides, by examining the Mediator principle.

The CQRS structure can be used alone or together with Meditor when desired, providing us with convenience in development.

See you in another review…

You can reach the Github address of the project at the following address.

https://github.com/onurkarakus/CqrsMediatorPattern

.NET 6 Web API, CQRS ve MediatR kütüphanesi

Merhaba, bu yazıda beraber CQRS (Command Query Responsibility Segregation) ve Mediator tasarım prensipleri ve ile ilgili çalışmalar yapacağız.

MediatR kütüphanesi iki temel prensip üzerinde kurulmuş bir kütüphanedir. Bunlar; CQRS ve Mediator.

Projenin kodlarına aşağıdaki bağlantıdan ulaşabilirsiniz. https://github.com/onurkarakus/CqrsMediatorPattern

Öncelikle CQRS nedir? biraz onu anlatmaya çalışacağım.

CQRS adından da anlaşıldığı gibi komut ve sorguların ayrılması prensibidir.
Genel olarak kullandığımız yapılarda CRUD işlemlerini aslında tek bir repository üzerinden yönetmeye çalışıyoruz. CQRS bu yapıyı bölme üzerine kurulu bir tasarım mantığı getiriyor.

Komut nesneleri veriler üzerinden değişiklik yapan (CUD), sorgular ise veriler üzerinde okuma işlemini (R) gerçekleştiren nesneler olarak tanımlanmaktadır.

CQRS prensibinin çalışma mantığı

Gördüğünüz gibi bu prensip uygulama içinde okuma ve komut işlemlerinin tamamen ayrılmasını destekler. Burada ayırma işlemi için belirli bir sınır veya yöntem kesin olarak belirtilmemiştir. İstenildiği zaman bir model, uygulama veya veri tabanı bazında da ayırma işlemi yapılabilir. Tabii ki bu kurallar uygulamanın boyutu ve karmaşıklığına göre değişebilmektedir.

Bu prensibin kısa tanımı ve temel prensibi aşağıdaki gibi tanımlanabilir.

“Bir metot bir nesnenin durumunu değiştirmelidir veya bir değer döndürmelidir. İkisini birlikte yapmamalıdır.”

Avantajları :

  • Uygulama içerisinde kullanıcılara verileri sunmak için kullanacağımız ViewModel nesnelerinin veri modellerinden oluşturulması (map) işlemlerinde karmaşıklığın önüne geçmektedir.
  • Çok sayıda veri tabanı işlemi içeren bir uygulama tasarlayacaksanız performansa önem vermek zorunda kalacaksınız demektir. Böyle bir durumda okuma ve yazma işlemlerinin ayrı nesneler olması bu işlemlerin ayrı ayrı kontrol altına alınması, yönetilmesi ve performans çalışmalarının yapılması anlamına gelmektedir.
  • Yazma ve okuma işlemleri ayrı şekilde ölçeklenebilir olduğundan dolayı daha az işlem kilitlenmeleri (db lock vb.) olacaktır.
  • Uygulama kodlarının açık ve okunabilir olmasını sağlar.
  • Takıma yeni başlayacak olan yazılımcıların projeye adaptasyonunu hızlandırabilmektedir.

Dezavantajları :

  • Eğer küçük ölçekli bir uygulama geliştirecekseniz CQRS prensibinin uyarlaması sizin için yorucu olacaktır.
  • Sadece CQRS prensibini kullanmak çok fazla kod geliştirme ihtiyacı oluşturacağından dolayı projenin geliştirme süreleri uzayacaktır.

Peki Mediator nedir ?

Mediator Tasarım Deseni nesneler arasındaki bağımlılıkları azaltmamız için bizlere olanak sağlayan bir prensiptir. En rahat kullanılan sınıf bağımlılıkları en az olan sınıftır mantığından yola çıkarak bu tasarım kalıbı ile kullanacağımız nesnelerin aynı ara yüzü uyarlar. Bu prensip ise nesnelerin iletişimlerinin birbirleri ile değil bu ara yüz üzerinden yapmalarını sağlamayı amaçlar.

En rahat kullanılan sınıf bağımlılıkları en az olan sınıftır mantığından yola çıkarak bu tasarım kalıbı ile kullanacağımız nesnelerin aynı ara yüzü uyarlaması ile iletişimlerinin birbirleri ile değil bu ara yüz üzerinden yapmalarını sağlamayı amaçlar.

Bu desen sayesinde kullandığımız nesneler birbirleri bağımlı olmak yerine iletişimi bir aracı ile sağlarlar.

Genel olarak araştırdığınız zaman verilen örneklerin en meşhuru havaalanı, kula ve uçak örneğidir. Uçakların zorunlu olmadıkları sürece birbirleri ile konuşmayarak kule ile konuşmaları bu prensibe basit bir örnek olabilir.

MediatR kütüphanesi ise Mediator tasarım desenini uygulamamız için bize kolaylık sağlayan bir kütüphanedir.

Şimdi bir proje üzerinden önce CQRS tasarım deseni örneğini incelemeye çalışalım. Ardından da MediatR kütüphanesi ile projemizi Mediator tasarım deseni ile daha kullanışlı hale getirelim.

Yeni bir ASP.NET Core Web API projesi oluşturarak isimlendirelim.

CqrsMediatorPattern isimli proje için oluşturma ekranı

Projemiz ilk oluştuğu zaman hazır olacak gelen dosyaları temizleyelim.

Senaryomuz basit. Bir kitap bilgilerini içeren uygulamamız ve bu kitapları türlerini tutacağımız bir enum değerimiz olsun. Uygulama üzerinden CRUD işlemlerini gerçekleştirelim.

Bunun için Domain -> Entities, Domain -> Enum isminde klasörler oluşturarak nesne tanımlarımızı oluşturalım.

Üç adet yeni nesne tanımımız mevcut.

Kitap bilgilerimizi tutacağımız Book.cs

using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.Domain.Entities;

public class Book
{
public long Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }    
public BookGenre BookGenre { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
}

Kitap türlerini tutacağımız enum tipinde olan BookGenre.cs

namespace CqrsMediatorPattern.Domain.Enums;

public enum BookGenre
{
    None = 0,
    Horror = 1,
    Fable = 2,
    Fiction = 3,
    Mystery = 4
}

Veri tabanı işlemlerimiz için projemize Entity Framework Core paketlerini eklememiz gerekmekte. Bunun için aşağıdaki paketleri paket yöneticisi (Nuget Packet Manager) , CLI veya paket referans (PacketReference) olarak yükleyebilirsiniz.

Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore
Nuget paket yöneticisi üzerinden paketlerin eklemesi

Şimdi de Dbcontext sınıfımızı tasarlayabiliriz. Data -> Context isimli bir klasör açarak Dbcontext sınıfımız orada tasarlıyoruz. Sınıfımızın ismi BookDbContext olacak şekilde bir düzenleme yapıyoruz.

BookDbContext.cs
using CqrsMediatorPattern.Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace CqrsMediatorPattern.Data.Context;

public class BookDbContext : DbContext
{
public BookDbContext(DbContextOptions<BookDbContext> options) : base(options)
    {

    }

public DbSet<Book> Books { get; set; }    
}

appsettings.json dosyamızda ConnectionString bilgilerini güncellememiz gerekiyor. Bilgileri aşağıdaki gibi ekliyoruz.

{kullanici_adiniz} : Sql Server için belirlenen kullanıcı adı
{şifreniz} : Sql Server için belirlenen şifreniz

{
"ConnectionString": {
"SqlDbConnectionString": "Data Source=.\\SQLEXPRESS; Initial Catalog=CqrsMediatr_example;Persist Security Info=True;User Id={kullanici_adiniz};Password={şifreniz};MultipleActiveResultSets=true;TrustServerCertificate=True"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Ardından Program.cs sınıfımızda oluşturmuş olduğumuz DbContext nesnesini register etmemiz gerekiyor. Bunun için Program.cs sınıfında aşağıdaki gibi eklemeyi yapıyoruz.

using CqrsMediatorPattern.Data.Context;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
    options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Dah asonra veri tabanımızı ve tabloları oluşturmak için aşağıdaki komutları sırasıyla Package Manager Console üzerinde çalıştırıyoruz.

Add-Migration Init
Update-Database
Add-Migration
Update-Database

Ardından Sql Server üzerinden kontrol ettiğimizde veri tabanı ve tabloların oluşmuş olduğu görebiliriz.

Şimdi CQRS için geliştirmelerimizi yapabiliriz. İlk olarak CQRS prensibini hayata geçireceğiz. İyi ve kötü yanlarını kod olarak gördükten sonra Mediator prensibinin sağladığı yararları ve MediatR kütüphanesinin kullanımına bakacağız.

Öncelikle CQRS isminde bir klasör açarak Commands, Handlers ve Queries bilgilerimizi burada oluşturuyoruz.

CQRS klasörü ve içeriği

Commands : Uygulama içinde yapılacak olan tüm komut (CUD) işlemlerinin tanımlandığı klasör olacaktır.

Queries : Uygulama içinde yapılacak olan tüm sorgu (R) işlemlerinin tanımlandığı klasör olacaktır.

Handler : Tüm komut ve sorguları ortak noktada işleyecek ve kullanıcılara uygun olacak cevabı dönecek olan modellerimizin tanımlandığı klasör olacaktır.

Şimdi Commands klasörü içindeki nesnelerimizi tasarlayalım.

CreateBookCommandRequest.cs ve CreateBookCommandResponse.cs

using CqrsMediatorPattern.Domain.Entities;
using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class CreateBookCommandRequest
{
public string? Title { get; set; }
public string? Description { get; set; }    
public BookGenre? BookGenre { get; set; }
}
namespace CqrsMediatorPattern.CQRS.Commands.Response;

public class CreateBookCommandResponse
{
public bool IsSuccess { get; set; }
public int CreatedBookId { get; set; }
}

DeleteBookCommandRequest.cs ve DeleteBookCommandResponse.cs

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class DeleteBookCommandRequest
{
public int BookId { get; set; }
}
namespace CqrsMediatorPattern.CQRS.Commands.Response;

public class DeleteBookCommandResponse
{
public bool IsSuccess { get; set; }
}
Command nesnelerimiz eklendikten sonra son durum

Şimdi Queries klasörü içindeki nesnelerimizi tasarlayalım.

GetAllBooksQueryRequest.cs ve GetAllBookQueryResponse.cs

namespace CqrsMediatorPattern.CQRS.Queries.Request;

public class GetAllBooksQueryRequest
{

}
using CqrsMediatorPattern.Domain.Enums;

namespace CqrsMediatorPattern.CQRS.Queries.Response;

public class GetAllBooksQueryResponse
{
public long Id { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public BookGenre BookGenre { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
}

Şimdi de sıra Handlers klasörüne geldi. Burada komut (Command) ve sorgu (Query) isteklerini işleyeceğimiz yapıları geliştireceğiz.

CommandHandlers

CreateBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using CqrsMediatorPattern.Domain.Entities;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class CreateBookCommandHandler
{
private readonly BookDbContext _dbContext;

public CreateBookCommandHandler(BookDbContext dbContext)
    {
this._dbContext = dbContext;
    }

public CreateBookCommandResponse CreateBook(CreateBookCommandRequest request)
    {
        _ = _dbContext.Books.Add(new Book
        {
            BookGenre = request.BookGenre.Value,
            Created = DateTime.Now,
            Description = request.Description,
            Title = request.Title
        });

var idInformation = _dbContext.SaveChanges();

return new CreateBookCommandResponse { IsSuccess= true, CreatedBookId = idInformation };
    }
}

DeleteBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class DeleteBookCommandHandler
{
private readonly BookDbContext _dbContext;

public DeleteBookCommandHandler(BookDbContext dbContext)
    {
this._dbContext = dbContext;
    }

public DeleteBookCommandResponse DeleteBook(DeleteBookCommandRequest request)
    {
var findBookResult = _dbContext.Books.FirstOrDefault(p => p.Id == request.BookId);

if (findBookResult == null)
        {
return new DeleteBookCommandResponse { IsSuccess = false };
        }

        _dbContext.Books.Remove(findBookResult);

        _ = _dbContext.SaveChanges();

return new DeleteBookCommandResponse { IsSuccess = true };
    }
}

QueryHandlers

GetAllBooksQueryHandler.cs

using CqrsMediatorPattern.CQRS.Queries.Request;
using CqrsMediatorPattern.CQRS.Queries.Response;
using CqrsMediatorPattern.Data.Context;

namespace CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;

public class GetAllBooksQueryHandler
{
private readonly BookDbContext _dbContext;

public GetAllBooksQueryHandler(BookDbContext dbContext)
    {
this._dbContext = dbContext;
    }

public List<GetAllBooksQueryResponse> GetAllBooks(GetAllBooksQueryRequest request)
    {
return _dbContext.Books.Select(x => new GetAllBooksQueryResponse()
        {
            BookGenre = x.BookGenre,
            Created = x.Created,
            Description = x.Description,
            Id = x.Id,
            Title = x.Title,
            Updated = x.Updated
        }).ToList();
    }
}

Temel olarak CQRS prensibi ile sınıflarımızı geliştirmiş olduk. Şimdi Handler olarak geliştirdiğimiz sınıfları servis olarak uygulamamıza eklememiz ve ardından Controller sınıfımızı geliştirmemiz gerekiyor. Program.cs sınıfımızın son hali aşağıdaki gibi olacaktır.

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
    options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddTransient<CreateBookCommandHandler>();
builder.Services.AddTransient<DeleteBookCommandHandler>();
builder.Services.AddTransient<GetAllBooksQueryHandler>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Şimdi de Controller sınıfımızı geliştirelim. Controllers klasörümüzün içine BookController isminde bir API Controller oluşturuyoruz.

BookController.cs sınıfımızın son hali de aşağıdaki gibi olacaktır.

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.CQRS.Queries.Request;
using Microsoft.AspNetCore.Mvc;

namespace CqrsMediatorPattern.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
readonly CreateBookCommandHandler createBookCommandHandler;
readonly DeleteBookCommandHandler deleteBookCommandHandler;
readonly GetAllBooksQueryHandler getAllBooksQueryHandler;

public BookController(CreateBookCommandHandler createBookCommandHandler, DeleteBookCommandHandler deleteBookCommandHandler, GetAllBooksQueryHandler getAllBooksQueryHandler)
    {
this.createBookCommandHandler = createBookCommandHandler;
this.deleteBookCommandHandler = deleteBookCommandHandler;
this.getAllBooksQueryHandler = getAllBooksQueryHandler;
    }

    [HttpGet]
public IActionResult Get([FromQuery] GetAllBooksQueryRequest request)
    {
var allBooks = getAllBooksQueryHandler.GetAllBooks(request);

return Ok(allBooks);
    }        

    [HttpPost]
public IActionResult Post([FromBody] CreateBookCommandRequest request)
    {
        CreateBookCommandResponse response = createBookCommandHandler.CreateBook(request);

return Ok(response);
    }

    [HttpDelete("{id}")]
public IActionResult Delete([FromQuery] DeleteBookCommandRequest request)
    {
        DeleteBookCommandResponse response = deleteBookCommandHandler.DeleteBook(request);
return Ok(response);
    }
}

Ardından projemizi çalıştırıp Swagger üzerinden test edebiliriz.

İlk önce bir kitap ekleyerek ardından liste olarak kitapları alacağız.

Yeni Ktap Ekleme
Yeni Kitap Ekleme Sonucu
Kitap Listesi
Kitap Listesi Sonucu
Veri tabanında bulunan kayıt bilgisi

Gördüğünüz gibi BookController üzerinden isteklerimizi göndererek Command ve Query nesnelerimizi ayırıp işlemlerimizi farklı akışlarla yapabildik.

Biz tek bir nesne ve tablo için bu işlemleri yerine getirdik. Fakat proje büyüdükçe Command ve Query nesne sayılarımız artacak. Servis olarak uygulamamıza eklediğimiz sınıfılar fazlalaşacak ve özellik Controller sınıfıların DI işlemleri problem olmaya başlayacak gibi görünüyor.

Bir kötü durum da gelen istek (Request) bilgisinin cevap (Response) bilgisini bizim belirlememiz. Gelen isteğe göre uygun olan Handler bilgisinin de yazılımcı tarafında belirlenmesi gibi işler de projenin geliştirme aşamalarında bizlere zorluk çıkaracak, karmaşıklığı artıracaktır.

Böyle bir durumda da Mediator prensibi bize destek olacak ve MediatR kütüphanesi ile bu prensibi ekleyerek daha kolay ve daha rahat bir şekilde CQRS nesnelerimizi kullanabileceğiz. Böylelik Command, Query ve Handler nesnelerimizin davranışlarına uygun şekilde çalışmalarını ve yönetilmelerini sağlayabileceğiz.

Gelin beraber MediatR geliştirmelerini hazırlamaya başlayalım.

İlk önce MediatR kütüphanesini projemize ekleyerek başlıyoruz.

MediatR kütüphanesini projemize eklemek için aşağıdaki paketleri paket yöneticisi (Nuget Packet Manager) , CLI veya paket referans (PacketReference) olarak yükleyebilirsiniz.

MediatR
MediatR.Extensions.Microsoft.DependencyInjection
Nuger paket manager ile MediatR yüklenmesi

Ardından Program.cs sınıfımıza servis kayıt işlemini gerçekleştiriyoruz.

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using MediatR;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
    options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddTransient<CreateBookCommandHandler>();
builder.Services.AddTransient<DeleteBookCommandHandler>();
builder.Services.AddTransient<GetAllBooksQueryHandler>();

builder.Services.AddMediatR(typeof(Program));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

MediatR kütüphanesi bize iki adet ara yüz sunmaktadır. Bu ara yüzler sayesinde Request ve RequestHanler nesnelerimizi belirleyerek Mediator prensibine uygun olarak sorumluluklarının almalarını sağlayabilmektedir.

Bu ara yüzler aşağıdaki gibidir.

IRequest :

namespace MediatR
{
//
// Summary:
//     Marker interface to represent a request with a void response
public interface IRequest : IRequest<Unit>, IBaseRequest
    {
    }
}

IRequestHandler :

namespace MediatR
{
//
// Summary:
//     Defines a handler for a request
//
// Type parameters:
//   TRequest:
//     The type of request being handled
//
//   TResponse:
//     The type of response from the handler
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse>
    {
//
// Summary:
//     Handles a request
//
// Parameters:
//   request:
//     The request
//
//   cancellationToken:
//     Cancellation token
//
// Returns:
//     Response from the request
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
    }
}

Şimdi de daha önce oluşturduğumuz tüm Command, Query ve Handler sınıflarımızı MediatR için yenidne düzenleyelim. Burada yapacağımız CommandRequest nesnelerinin IRequest, Handler nesnelerimizin ise IRequestHandler ara yüzlerini uyarlamalarının sağlamak olacaktır.

CreateBookCommandRequest.cs

using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Domain.Enums;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class CreateBookCommandRequest: IRequest<CreateBookCommandResponse>
{
public string? Title { get; set; }
public string? Description { get; set; }
public BookGenre? BookGenre { get; set; }
}

DeleteBookCommandRequest.cs

using CqrsMediatorPattern.CQRS.Commands.Response;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Commands.Request;

public class DeleteBookCommandRequest: IRequest<DeleteBookCommandResponse>
{
public int BookId { get; set; }
}

GetAllBooksQueryRequest.cs

using CqrsMediatorPattern.CQRS.Queries.Response;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Queries.Request;

public class GetAllBooksQueryRequest:IRequest<List<GetAllBooksQueryResponse>>
{

}

Şimdi de Handler nesnelerimizin değişikliklerini yapalım. Handler nesneleri için IRequestHandler<request,response> şeklinde bir ara yüz uyarlaması yapacağız. Ardından ara yüz ile birlikte gleen Handler metodunu ekleyeceğiz. Burada fark yeni eklenen Handle metodunun async olarak çalışacak olmasıdır.

CreateBookCommandHandler.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using CqrsMediatorPattern.Domain.Entities;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class CreateBookCommandHandler: IRequestHandler<CreateBookCommandRequest, CreateBookCommandResponse>
{
private readonly BookDbContext _dbContext;

public CreateBookCommandHandler(BookDbContext dbContext)
    {
this._dbContext = dbContext;
    }    

public Task<CreateBookCommandResponse> Handle(CreateBookCommandRequest request, CancellationToken cancellationToken)
    {
        _ = _dbContext.Books.Add(new Book
        {
            BookGenre = request.BookGenre.Value,
            Created = DateTime.Now,
            Description = request.Description,
            Title = request.Title
        });

var idInformation = _dbContext.SaveChanges();

return Task.FromResult(new CreateBookCommandResponse { IsSuccess = true, CreatedBookId = idInformation });
    }
}

DeleteBookCommandHandler.c

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.Data.Context;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;

public class DeleteBookCommandHandler : IRequestHandler<DeleteBookCommandRequest, DeleteBookCommandResponse>
{
private readonly BookDbContext _dbContext;

public DeleteBookCommandHandler(BookDbContext dbContext)
    {
this._dbContext = dbContext;
    }

public Task<DeleteBookCommandResponse> Handle(DeleteBookCommandRequest request, CancellationToken cancellationToken)
    {
var findBookResult = _dbContext.Books.FirstOrDefault(p => p.Id == request.BookId);

if (findBookResult == null)
        {
return Task.FromResult(new DeleteBookCommandResponse { IsSuccess = false });
        }

        _dbContext.Books.Remove(findBookResult);

        _ = _dbContext.SaveChanges();

return Task.FromResult(new DeleteBookCommandResponse { IsSuccess = true });
    }
}

GetAllBooksQueryHandler.cs

using CqrsMediatorPattern.CQRS.Queries.Request;
using CqrsMediatorPattern.CQRS.Queries.Response;
using CqrsMediatorPattern.Data.Context;
using MediatR;

namespace CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;

public class GetAllBooksQueryHandler: IRequestHandler<GetAllBooksQueryRequest, List<GetAllBooksQueryResponse>>
{
private readonly BookDbContext _dbContext;

public GetAllBooksQueryHandler(BookDbContext dbContext)
{
this._dbContext = dbContext;
    }    

public Task<List<GetAllBooksQueryResponse>> Handle(GetAllBooksQueryRequest request, CancellationToken cancellationToken)
    {
return Task.FromResult(_dbContext.Books.Select(x => new GetAllBooksQueryResponse()
        {
            BookGenre = x.BookGenre,
            Created = x.Created,
            Description = x.Description,
            Id = x.Id,
            Title = x.Title,
            Updated = x.Updated
        }).ToList());
    }
}

Şimdi ise BookController sınıfımızı da MediatR’a uygun hale getiriyoruz. Burada yapacağımız değişiklik MediatR’ın bizim için snıflarımızı yönlendirmesi adın Controller sınıfına kendisini inject ederek yönlendirme işlemini ona bırakacağız.

BookController.cs

using CqrsMediatorPattern.CQRS.Commands.Request;
using CqrsMediatorPattern.CQRS.Commands.Response;
using CqrsMediatorPattern.CQRS.Queries.Request;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace CqrsMediatorPattern.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
readonly IMediator mediator;

public BookController(IMediator mediator)
    {
this.mediator = mediator;
    }

    [HttpGet]
public async Task<IActionResult> Get([FromQuery] GetAllBooksQueryRequest request)
    {
var allBooks = await mediator.Send(request);

return Ok(allBooks);
    }        

    [HttpPost]
public async Task<IActionResult> Post([FromBody] CreateBookCommandRequest request)
    {
        CreateBookCommandResponse response = await mediator.Send(request);

return Ok(response);
    }

    [HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromQuery] DeleteBookCommandRequest request)
    {
        DeleteBookCommandResponse response = await mediator.Send(request);

return Ok(response);
    }
}

Görüldüğü gibi daha önceden eklemiş olduğumuz Handler sınıflarımızın injection bilgilerini kaldırdık. Controller metotlarımızı async olacak şekilde yeniden düzenledik ve tüm işi mediator ismi ile eklediğimiz nesneye bıraktık.

Son olarak daha önce Program.cs sınıfımızında kayıt ettiğimiz Handler servislerimizi de kaldırabiliriz.

Program.cs

using CqrsMediatorPattern.CQRS.Handlers.CommandHandlers;
using CqrsMediatorPattern.CQRS.Handlers.QueryHandlers;
using CqrsMediatorPattern.Data.Context;
using MediatR;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddDbContextPool<BookDbContext>(
    options => options.UseSqlServer(builder.Configuration["ConnectionString:SqlDbConnectionString"]));

builder.Services.AddMediatR(typeof(Program));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

Uygulamamızı çalıştırıp kitapları listelediğimiz zaman sorunsuz bir şekilde kitapların geldiğini göreceğiz.

Bu yazıda CQRS prensibinin büyük ölçekli uygulamaları oluştururken bize performans, yönetim ve geliştirme noktalarında sağladığı kolaylıkları inceledik. İyi yanlarının yanında kötü yanları ile de gelen CQRS için MediatR kütüphanesi ile nasıl kolay çözümler üretebileceğimize Mediator prensibini de inceleyerek incelemeye çalıştık.

CQRS yapısı istenildiği zaman tek başına istenildiğinde ise Meditor ile birlikte kullanılabilemekte ve bizlere geliştirme konusunda kolaylıklar sağlamaktadır.

Başka bir incelemede görüşmek üzere…

Projenin Github adresine aşağıdaki adresten ulaşabilirsiniz.

https://github.com/onurkarakus/CqrsMediatorPattern

Nedir bu Mikro Servis?

http://comunytek.com/en/introduction-to-microservices/

Merhaba,

Son dönem çok defa ismini duyduğumuz, neredeyse herkesin ya yeni uygulama geliştirmeye başlarken ya da mevcut uygulamalarını bu yapıya güncellemek için düşüncelere daldığı mikro servis konusunu inceleyeceğiz. Öncelikle mevcut durumun ardından mikro servis konusunu inceledikten sonra API Gateway modeli üzerinden bir örneklendirme yapacağız.

O zaman ? Buyursunlar.

Mikro Servis Yapısı Hakkında

Monolitik Mimarideki Yapılar
Mikro servislerden bahsetmeden önce biraz mikro servis öncesi durumları incelemek yararlı olacak diye düşünüyorum. Nereden ? Nereye ? Büyük ihtimalle şimdiye kadar hatta şu an bile geliştirdiğimiz uygulamalar monolit yapıdaki uygulamalar. (Benim öyle şu an) Peki nedir bu monolitik yapı?

Monolitik mimari geniş bir kitle tarafından kullanılan geleneksel bir mimari aslında. Geliştirdiğimiz uygulamanın tüm parçalarının aynı proje içinde olması diyebiliriz. Peki, biz zaten gevşek bağlanmış (loosely coupled) uygulamalar geliştiriyoruz. Monolitik mimari burada neyi değiştiriyor diyebilirsiniz. Açıklamaya çalışayım efendim.

Uygulama içerisinde kullandığımız tüm bileşenlerimiz birbirleri ile gevşek bağlı olabilir fakat uygulamamızın içerisinde bulunan her şey uygulamanın kendisi ile sıkı sıkıya bağlı durumda. Bunun en güzel örneği dağıtım (deployment) aşamasında karşımıza çıkıyor. Eğer biz uygulamamız içerisinde bir güncelleme yaparsak ve bunu üretim (production) ortamında yayınlamak istersek o zaman tüm uygulamayı baştan yayınlama aşamasına göndermemiz gerekiyor. Aynı durum üretim ortamında çalışan uygulamalarımızda oluşan sorunlar karşısında da ortaya çıkıyor.

Örnek vermek gerekirse bir bankacılık uygulaması içerisinde kullanıcıların varlıkları ile ilgili işlemler yapılıyor. Hesap bilgileri, kredi kartı bilgileri, yatırım bilgileri vb. Eğer kart işlemleri modülü ile ilgili bir hata oluşursa uygulamamız kartların bilgileri çekerken hata alacak durumda.

“Ne var? O noktada hata alırsa bir yolla hatayı fırlatmaz log olarak sisteme yazabilirim. Veya yumuşak bir geçiş ile kullanıcının mutluluğunu bozmam.” Evet olası bir durum, yapılabilir fakat ne kadar doğru? Tabii uygulamamız için yapacağımız yeni geliştirmelerde de bu sorun mevcut. Kart ekstreleri ile ilgili bir geliştirme yaptınız. Bunun üretim ortamına çıkması gerekiyor. O zaman tüm uygulamanın üretim ortamına alınması, hatta farklı yerlerin etkilenip / etkilenmediği kontrolleri vs. vs. Sadece kart modülü için yapılan bir değişiklik için sizi uzun bir yolculuk bekliyor aslında.

Monolitik Mimari

Mikro Servis Mimarisi
Bu mimaride uygulamamızı her birinin farklı bir hizmet verdiği bileşenlere ayırıyoruz. Her bileşen ayrı bir uygulama olarak yaşam döngüsünü sürdürüyor. Bankacılık uygulaması örneğimize geri dönecek olursak hesaplar, kartlar ve yatırım ürünlerinin ayrı birer küçük uygulama olduklarını düşünelim. Bu küçük uygulamalara bölmenin bize kattıkları konusunu inceleyelim.

Hesap bilgilerini dönen servisimiz gayet güzel çalışırken, kartları dönen servisimizde bir hata olması durumunda sistemin geneli etkilenmeden akışlarımız devam edebilir.

Bu küçük uygulamalardan herhangi birinde bir yük artışı olduğu zaman (küçük olmalarından dolayı) hızlı bir şekilde yatay olarak ölçeklendirilebilir. (horizontal scale). Daha sonrasında eğer yük azalırsa ölçeklendirme sonucunda oluşan yeni makinelerin kapatılması da söz konusu. (Docker ve Kubernetes)

Yine, bu küçük uygulamalardan herhangi birinde yapacağımız bir kod güncellemesini diğer uygulamalar etkilenmeden üretim ortamına alabilme yeteneğimiz de oluyor.

O zaman genel olarak baktığımız zaman mikro servis mimarisi, uygulamanın kendisinin çeşitli bileşenlere bölündüğü ve her bileşenin belirli bir amaca hizmet ettiği bir mimaridir. Bileşenler artık uygulamanın kendisine bağımlı değildir. Bu bileşenlerin her biri tam anlamıyla ve fiziksel olarak bağımsızdır. Bu şekilde kurulmuş olan bir mimari ile uygulamalarımız ayrı ayrı veri tabanlarına sahip olabildikleri gibi ayrı makinelerde de barındırılabilirler.

Mikro Servis Mimarisi

Avantajları

  • Mikro servislerin her biri ayrı uygulama olarak çalıştıklarından dolayı ayrı programlama dili ile de yazılabilir.
  • Ekibe yeni katılan bir geliştiricinin (developer) tüm yapıya hakim olması için geçecek olan zamanı azaltarak belirli mikro servisler üzerinde hızlı bir şekilde çalışmaya başlaması sağlanabilir.
  • Dağıtım (deployment) aşaması her mikro servis için ayrı ayrı yapılabilir. Bu da deploy sürelerini kısaltmış olur.
  • Mikro servislerin çalıştıkları konteynır (container) üzerinde yüke göre ölçeklendirme uygulanabilir.
  • İstenildiği zaman farklı veri tabanlarında çalışacak mikro servisler geliştirilecek yapıya dahil edilebilir. Örnek olarak MongoDb ile çalışacak ve Go ile yazılmış bir mikro servisi de yapınıza sonradan dahil edebilir ve ortak çalışma alanında bu mikro servis üzerinden faydalanabilirsiniz.

Dezavantajları

  • Transaction bütünlüğünün sağlanmasındaki zorluklar.
  • Servisler arası iletişim ve bütünlük zorlayıcıdır.
  • Integration testlerin hazırlanması zordur.
  • Genel olarak iş mantığına iyi derecede hakim olmamız gerekmektedir.
  • Dağıtım (deployment) sonrası kontrol, güvenlik ve yönetim işlemlerinin iyi bir şekilde yapılması gerekmektedir. Servis sayımız çoğaldıkça bu konularda da zorluklar ortaya çıkmaktadır.

Şimdi de ufak bir mikro servis örneği ile devam edelim. Bu çalışmada bir yazar ve bir kitap servisimiz olacak. Bu servislerimiz bize sadece kendi konuları ile ilgili bilgileri verecekler.

Burada bir soru işareti ortaya çıkıyor. Bu servisleri çağıracak olan client hepsinin adresini bilmesi mi gerekiyor? Hayır, buna gerek yok. Biz bir API Gateway ekleyerek client uygulamanın bu adrese gelmesini sağlayacağız. Client API gateway üzerinden hangi işlemi isterse gateway bizim için yönlendirme (routing) işlemini yapacak ve uygun servis cevaplarını dönecek. Bunun için Ocelot kütüphanesini kullanacağız.

İlk olarak yeni bir boş çözüm (blank solution) oluşturarak ismin MyMicroServices vererek projemize başlıyoruz.

Oluşturacağımız mikro servisleri ayırabilmek için MicroServices isimli bir klasör oluşturuyoruz.

Şimdi oluşturduğumuz klasörümüze iki adet ASP.NET Core Web API projesi oluşturacağız. İsimlerini Author.Microservice ve Book.Microservice olarak vereceğiz.

Son olarak da API Gateway olarak görevlendireceğimiz MyGateWay.WebApi isimli projemizi oluşturacağız. Bu projemizi ASP.NET Core Empty proje temasını (template) kullanarak oluşturuyoruz.

Son durumda projemin yapısı aşağıdaki gibi olacaktır.

Author.Microservice isimli projeme Entities isminde bir klasör ekleyerek AuthorInformation.cs isimli sınıfımı (class) ekliyorum.

Ardından Controllers klasörüne AuthorController isminde API Controller sınıfımızı ekleyerek bize yazar listesi döndürecek olan metodumuzu ekliyoruz.

AuthorController.cs

Postman üzerinden API metodumuzu test ettiğimiz zaman aşağıdaki şekilde bilgileri döndürdüğünü görebiliriz.

Aynı şekilde Book.Microservice uygulaması içinde bir Entities klasörü açarak BookInformation.cs sınıfını ekleyeceğiz. Ardından Controllers klasörüne BookController.cs sınıfımızı ekleyerek kitap bilgilerini dönecek metodu yazacağız.

BookInformation.cs
BookController.cs

İki farklı hizmeti verecek olan iki adet mikro servisimiz şu an hazır durumda. Yalnız, bunların ikisinin birden aktif olması durumunda client hangi durumda hangi servisi çağıracağının nasıl bilecek? Bunu bizim için API Gateway çözecek. Kendisine gelen istekten hangi servis sorumlu ise isteği ona yönlendirecek. Bu yapıyı kurabilmek için Ocelot API Gateway’den yararlanacağız.

Ocelot API Gateway hakkında biraz daha
Ocelot open source olarak dağıtılan .NET/Core platformları için geliştirilmiş bir API Gateway ürünüdür. Arkasında bulunan mikro servislerin adreslerini bir konfigürasyon dosyasına kayıt ederek gelen isteğin hangi mikro servise gitmesi gerektiğini tanımlayacağız. Böylelikle client bir istekte bulunduğu cama Ocelot bizim için yönlendirmeyi yapacak ve uygun cevabı dönecek.

Konfigürasyon dosyası json formatındadır. Burada rotalarımızı (route) tanımlarken karşımıza iki terim çıkıyor. Bunlar; UpStream ve DownStream UpStream client’ın geleceği adresi tarif ederken DownStream ise Ocelot’un çağıracağı iç servis bilgisini tanımlamaktadır.

Ocelot API Gateway olarak servislerin rotalarının oluşturulması haricinde kimlik doğrulama (Authentication), yetki (Authorization) ve yük dengeleme (Load Balance) işlemlerini de bizim için yapabilmektedir.

MyGateWay.WebApi projesine Ocelot paketini ekliyoruz. Bunun için isterseniz Install-Package Ocelot komutunu isterseniz de Nuget Packet Manager üzerinden paketin yüklemesini yapabilirsiniz.

Daha sonrasında Program.cs sınıfımız içerisinde aşağıdaki değişikliği yapıyoruz.

Buradaki amacımız uygulamamızın açılışında ocelot.json isimli dosya içerisinden Ocelot konfigürasyon bilgilerinin alınması.

Şimdi de Startup.cs isimlii sınıfın içerisinde bulunan ConfigureServices metoduna Ocelot’u ekleyelim. Ardından yine aynı sınıf içinde bulunan Configure metodunda da Ocelot kullanımı için tanım yapacağız.

Startup.cs

Şimdi de sıra Ocelot üzerinde rota bilgilerini tanımlama kısmına geldi. Öncelikle burada iki adet mikro servisin hangi portlar üzerinden çalıştığının kontrol etmemiz. Daha sonrasında ise VS üzerinden uygulamalardan birini çalıştırdığımız zaman hepsinin ayağa kalkması için bir değişiklik yapmamız gerekecek.

Önce servis portları. Bunun için Author.Microservice ve Book.Microservice projelerine tek tek sağ tuşal tıklayarak açılan Properties penceresinde sol bölümde bulunan Debug menüsünü açıyoruz. Port bilgisi sayfa üzerinde gelecektir.

Burada port bilgisini Book.Microservice için 1360 Author.Microservice için 1361 olarak değiştiriyoruz. Bu değişikliği isterseniz projelerin her birinde bulunan launchSettings.json dosyası üzerinden de yapmanız mümkün.

Uygulamaların hepsinin aynı anda çalıştırılması için Solution Explorer üzerinden Solution dosyasına sağ tuşla tıklayarak Properties menüsü üzerinden açılan sayfada değişiklikleri aşağıdaki gibi yapabilirsiniz.

Şimdi Ocelot üzerinde rota tanımlarımızı yapabiliriz. Bunun için daha önceden tanımını yaptığımız ocelot.json isimli dosyayı MyGateway.WebApi projemizin root’una tanımlıyoruz. Ardından aşağıdaki şekilde rota (route) tanımlarının yapıyoruz.

ocelot.json

Burada yaptığımız tanımlamaları biraz açıklamak gerekiyor.

Routes altında tanımladığımız;

DownstreamPathTemplate: Ocelot’un gideceği API rotası olarak açıklanabilir. 
DownstreamScheme: http olarak veya https olarak gidileceğinin tanımı. 
DownstreamHostAndPorts: Author servisi için kullanılacak olan Ocelot arkasındaki adres bilgisi.

UpstreamPathTemplate: Client uygulamanın gateway’e geleceği API rota bilgisi. 
UpstreamHttpMethod: Client uygulamanın hangi HTTP Verb için bu adrese geleceğini tanımladığımız yerdir.

Ardından tanımlarımızı ve servislerimizi test edebiliriz. Daha önceden Project Properties penceresinden tüm uygulamalar beraber çalışmaya başlasın dediğimiz için F5 dememiz servislerin hepsinin çalışması için yeterli olacaktır.

Eğer pencere üzerinden bir değişiklik yapmadıysanız hepsini tek tek çalıştırmanız gerekmektedir.

Postman ile API Gateway uygulamamıza hem kitaplar hem de yazarlar için çağrılarımızı yapalım. Bakalım nasıl bir bilgi gelecek.

http://localhost:5000/gateway/Author
http://localhost:5000/gateway/Book

Gördüğünüz gibi hem http://localhost:5000/gateway/Book hem de http://localhost:5000/gateway/Author adresleri API Gateway uygulamamız içerinden çağırılan adresler. Bu adresleri Postman uygulaması ile çağırdığımız zaman Ocelot bizim için uygun olan servise gidiyor ve o servis sonucu alarak bize geri iletiyor.

Böylelikle client’ın hangi servisi nasıl çağırması gerektiğini bilmesine gerek kalmıyor. Client sadece API Gateway adresini biliyor ve oraya geliyor.

Umarım mikro servis mimarileri ve temel çalışma prensiplerini anlama konusunda yararlı bir makale olmuştur.

Görüşmek üzere…

Kaynaklar:

http://mustafabas.me/tr/mikroservis-mimarisi-nedir-avantajlari-dezavantajlari-nelerdir–b-1006

https://codewithmukesh.com/blog/microservice-architecture-in-aspnet-core/

Nedir bu Mikro Servisler (Microservices) ?

Merhaba,

Son dönem çok defa ismini duyduğumuz, neredeyse herkesin ya yeni uygulama geliştirmeye başlarken ya da mevcut uygulamalarını bu yapıya güncellemek için düşüncelere daldığı mikro servis konusunu inceleyeceğiz. Öncelikle mevcut durumun ardından mikro servis konusunu inceledikten sonra API Gateway modeli üzerinden bir örneklendirme yapacağız.

O zaman ? Buyursunlar.

Mikro Servis Yapısı Hakkında

Monolitik Mimari Yapılar
Mikro servislerden bahsetmeden önce biraz mikro servis öncesi durumları incelemek yararlı olacak diye düşünüyorum. Nereden ? Nereye ? Büyük ihtimalle şimdiye kadar hatta şu an bile geliştirdiğimiz uygulamalar monolit yapıdaki uygulamalar. (Benim öyle şu an) Peki nedir bu monolitik yapı?

Monolitik mimari geniş bir kitle tarafından kullanılan geleneksel bir mimari aslında. Geliştirdiğimiz uygulamanın tüm parçalarının aynı proje içinde olması diyebiliriz. Peki, biz zaten gevşek bağlanmış (loosely coupled) uygulamalar geliştiriyoruz. Monolitik mimari burada neyi değiştiriyor diyebilirsiniz. Açıklamaya çalışayım efendim.

Uygulama içerisinde kullandığımız tüm bileşenlerimiz birbirleri ile gevşek bağlı olabilir fakat uygulamamızın içerisinde bulunan her şey uygulamanın kendisi ile sıkı sıkıya bağlı durumda. Bunun en güzel örneği dağıtım (deployment) aşamasında karşımıza çıkıyor. Eğer biz uygulamamız içerisinde bir güncelleme yaparsak ve bunu üretim (production) ortamında yayınlamak istersek o zaman tüm uygulamayı baştan yayınlama aşamasına göndermemiz gerekiyor. Aynı durum üretim ortamında çalışan uygulamalarımızda oluşan sorunlar karşısında da ortaya çıkıyor.

Örnek vermek gerekirse bir bankacılık uygulaması içerisinde kullanıcıların varlıkları ile ilgili işlemler yapılıyor. Hesap bilgileri, kredi kartı bilgileri, yatırım bilgileri vb. Eğer kart işlemleri modülü ile ilgili bir hata oluşursa uygulamamız kartların bilgileri çekerken hata alacak durumda.

“Ne var? O noktada hata alırsa bir yolla hatayı fırlatmaz log olarak sisteme yazabilirim. Veya yumuşak bir geçiş ile kullanıcının mutluluğunu bozmam.” Evet olası bir durum, yapılabilir fakat ne kadar doğru? Tabii uygulamamız için yapacağımız yeni geliştirmelerde de bu sorun mevcut. Kart ekstreleri ile ilgili bir geliştirme yaptınız. Bunun üretim ortamına çıkması gerekiyor. O zaman tüm uygulamanın üretim ortamına alınması, hatta farklı yerlerin etkilenip / etkilenmediği kontrolleri vs. vs. Sadece kart modülü için yapılan bir değişiklik için sizi uzun bir yolculuk bekliyor aslında.

Mikro Servis Mimarisi
Bu mimaride uygulamamızı her birinin farklı bir hizmet verdiği bileşenlere ayırıyoruz. Her bileşen ayrı bir uygulama olarak yaşam döngüsünü sürdürüyor. Bankacılık uygulaması örneğimize geri dönecek olursak hesaplar, kartlar ve yatırım ürünlerinin ayrı birer küçük uygulama olduklarını düşünelim. Bu küçük uygulamalara bölmenin bize kattıkları konusunu inceleyelim.

Hesap bilgilerini dönen servisimiz gayet güzel çalışırken, kartları dönen servisimizde bir hata olması durumunda sistemin geneli etkilenmeden akışlarımız devam edebilir.

Bu küçük uygulamalardan herhangi birinde bir yük artışı olduğu zaman (küçük olmalarından dolayı) hızlı bir şekilde yatay olarak ölçeklendirilebilir. (horizontal scale). Daha sonrasında eğer yük azalırsa ölçeklendirme sonucunda oluşan yeni makinelerin kapatılması da söz konusu. (Docker ve Kubernetes)

Yine, bu küçük uygulamalardan herhangi birinde yapacağımız bir kod güncellemesini diğer uygulamalar etkilenmeden üretim ortamına alabilme yeteneğimiz de oluyor.

O zaman genel olarak baktığımız zaman mikro servis mimarisi, uygulamanın kendisinin çeşitli bileşenlere bölündüğü ve her bileşenin belirli bir amaca hizmet ettiği bir mimaridir. Bileşenler artık uygulamanın kendisine bağımlı değildir. Bu bileşenlerin her biri tam anlamıyla ve fiziksel olarak bağımsızdır. Bu şekilde kurulmuş olan bir mimari ile uygulamalarımız ayrı ayrı veri tabanlarına sahip olabildikleri gibi ayrı makinelerde de barındırılabilirler.

Avantajları

  • Mikro servislerin her biri ayrı uygulama olarak çalıştıklarından dolayı ayrı programlama dili ile de yazılabilir.
  • Ekibe yeni katılan bir geliştiricinin (developer) tüm yapıya hakim olması için geçecek olan zamanı azaltarak belirli mikro servisler üzerinde hızlı bir şekilde çalışmaya başlaması sağlanabilir.
  • Dağıtım (deployment) aşaması her mikro servis için ayrı ayrı yapılabilir. Bu da deploy sürelerini kısaltmış olur.
  • Mikro servislerin çalıştıkları konteynır (container) üzerinde yüke göre ölçeklendirme uygulanabilir.
  • İstenildiği zaman farklı veri tabanlarında çalışacak mikro servisler geliştirilecek yapıya dahil edilebilir. Örnek olarak MongoDb ile çalışacak ve Go ile yazılmış bir mikro servisi de yapınıza sonradan dahil edebilir ve ortak çalışma alanında bu mikro servis üzerinden faydalanabilirsiniz.

Dezavantajları

  • Transaction bütünlüğünün sağlanmasındaki zorluklar.
  • Servisler arası iletişim ve bütünlük zorlayıcıdır.
  • Integration testlerin hazırlanması zordur.
  • Genel olarak iş mantığına iyi derecede hakim olmamız gerekmektedir.
  • Dağıtım (deployment) sonrası kontrol, güvenlik ve yönetim işlemlerinin iyi bir şekilde yapılması gerekmektedir. Servis sayımız çoğaldıkça bu konularda da zorluklar ortaya çıkmaktadır.

Şimdi de ufak bir mikro servis örneği ile devam edelim. Bu çalışmada bir yazar ve bir kitap servisimiz olacak. Bu servislerimiz bize sadece kendi konuları ile ilgili bilgileri verecekler.

Burada bir soru işareti ortaya çıkıyor. Bu servisleri çağıracak olan client hepsinin adresini bilmesi mi gerekiyor? Hayır, buna gerek yok. Biz bir API Gateway ekleyerek client uygulamanın bu adrese gelmesini sağlayacağız. Client API gateway üzerinden hangi işlemi isterse gateway bizim için yönlendirme (routing) işlemini yapacak ve uygun servis cevaplarını dönecek. Bunun için Ocelot kütüphanesini kullanacağız.

İlk olarak yeni bir boş çözüm (blank solution) oluşturarak ismin MyMicroServices vererek projemize başlıyoruz.

Oluşturacağımız mikro servisleri ayırabilmek için MicroServices isimli bir klasör oluşturuyoruz.

Şimdi oluşturduğumuz klasörümüze iki adet ASP.NET Core Web API projesi oluşturacağız. İsimlerini Author.Microservice ve Book.Microservice olarak vereceğiz.

Son olarak da API Gateway olarak görevlendireceğimiz MyGateWay.WebApi isimli projemizi oluşturacağız. Bu projemizi ASP.NET Core Empty proje temasını (template) kullanarak oluşturuyoruz.

Son durumda projemin yapısı aşağıdaki gibi olacaktır.

Author.Microservice isimli projeme Entities isminde bir klasör ekleyerek AuthorInformation.cs isimli sınıfımı (class) ekliyorum.

Ardından Controllers klasörüne AuthorController isminde API Controller sınıfımızı ekleyerek bize yazar listesi döndürecek olan metodumuzu ekliyoruz.

Postman üzerinden API metodumuzu test ettiğimiz zaman aşağıdaki şekilde bilgileri döndürdüğünü görebiliriz.

Aynı şekilde Book.Microservice uygulaması içinde bir Entities klasörü açarak BookInformation.cs sınıfını ekleyeceğiz. Ardından Controllers klasörüne BookController.cs sınıfımızı ekleyerek kitap bilgilerini dönecek metodu yazacağız.

Ocelot API Gateway hakkında biraz daha
Ocelot open source olarak dağıtılan .NET/Core platformları için geliştirilmiş bir API Gateway ürünüdür. Arkasında bulunan mikro servislerin adreslerini bir konfigürasyon dosyasına kayıt ederek gelen isteğin hangi mikro servise gitmesi gerektiğini tanımlayacağız. Böylelikle client bir istekte bulunduğu cama Ocelot bizim için yönlendirmeyi yapacak ve uygun cevabı dönecek.

Konfigürasyon dosyası json formatındadır. Burada rotalarımızı (route) tanımlarken karşımıza iki terim çıkıyor. Bunlar; UpStream ve DownStream UpStream client’ın geleceği adresi tarif ederken DownStream ise Ocelot’un çağıracağı iç servis bilgisini tanımlamaktadır.

Ocelot API Gateway olarak servislerin rotalarının oluşturulması haricinde kimlik doğrulama (Authentication), yetki (Authorization) ve yük dengeleme (Load Balance) işlemlerini de bizim için yapabilmektedir.

MyGateWay.WebApi projesine Ocelot paketini ekliyoruz. Bunun için isterseniz Install-Package Ocelot komutunu isterseniz de Nuget Packet Manager üzerinden paketin yüklemesini yapabilirsiniz.

Daha sonrasında Program.cs sınıfımız içerisinde aşağıdaki değişikliği yapıyoruz.

Buradaki amacımız uygulamamızın açılışında ocelot.json isimli dosya içerisinden Ocelot konfigürasyon bilgilerinin alınması.

Şimdi de Startup.cs isimlii sınıfın içerisinde bulunan ConfigureServices metoduna Ocelot’u ekleyelim. Ardından yine aynı sınıf içinde bulunan Configure metodunda da Ocelot kullanımı için tanım yapacağız.

Şimdi de sıra Ocelot üzerinde rota bilgilerini tanımlama kısmına geldi. Öncelikle burada iki adet mikro servisin hangi portlar üzerinden çalıştığının kontrol etmemiz. Daha sonrasında ise VS üzerinden uygulamalardan birini çalıştırdığımız zaman hepsinin ayağa kalkması için bir değişiklik yapmamız gerekecek.

Önce servis portları. Bunun için Author.Microservice ve Book.Microservice projelerine tek tek sağ tuşal tıklayarak açılan Properties penceresinde sol bölümde bulunan Debug menüsünü açıyoruz. Port bilgisi sayfa üzerinde gelecektir.

Burada port bilgisini Book.Microservice için 1360 Author.Microservice için 1361 olarak değiştiriyoruz. Bu değişikliği isterseniz projelerin her birinde bulunan launchSettings.json dosyası üzerinden de yapmanız mümkün.

Uygulamaların hepsinin aynı anda çalıştırılması için Solution Explorer üzerinden Solution dosyasına sağ tuşla tıklayarak Properties menüsü üzerinden açılan sayfada değişiklikleri aşağıdaki gibi yapabilirsiniz.

Şimdi Ocelot üzerinde rota tanımlarımızı yapabiliriz. Bunun için daha önceden tanımını yaptığımız ocelot.json isimli dosyayı MyGateway.WebApi projemizin root’una tanımlıyoruz. Ardından aşağıdaki şekilde rota (route) tanımlarının yapıyoruz.

Burada yaptığımız tanımlamaları biraz açıklamak gerekiyor.

Routes altında tanımladığımız;

DownstreamPathTemplate: Ocelot’un gideceği API rotası olarak açıklanabilir. 
DownstreamScheme: http olarak veya https olarak gidileceğinin tanımı. 
DownstreamHostAndPorts: Author servisi için kullanılacak olan Ocelot arkasındaki adres bilgisi.

UpstreamPathTemplate: Client uygulamanın gateway’e geleceği API rota bilgisi. 
UpstreamHttpMethod: Client uygulamanın hangi HTTP Verb için bu adrese geleceğini tanımladığımız yerdir.

Ardından tanımlarımızı ve servislerimizi test edebiliriz. Daha önceden Project Properties penceresinden tüm uygulamalar beraber çalışmaya başlasın dediğimiz için F5 dememiz servislerin hepsinin çalışması için yeterli olacaktır.

Eğer pencere üzerinden bir değişiklik yapmadıysanız hepsini tek tek çalıştırmanız gerekmektedir.

Postman ile API Gateway uygulamamıza hem kitaplar hem de yazarlar için çağrılarımızı yapalım. Bakalım nasıl bir bilgi gelecek.

Gördüğünüz gibi hem http://localhost:5000/gateway/Book hem de http://localhost:5000/gateway/Author adresleri API Gateway uygulamamız içerinden çağırılan adresler. Bu adresleri Postman uygulaması ile çağırdığımız zaman Ocelot bizim için uygun olan servise gidiyor ve o servis sonucu alarak bize geri iletiyor.

Böylelikle client’ın hangi servisi nasıl çağırması gerektiğini bilmesine gerek kalmıyor. Client sadece API Gateway adresini biliyor ve oraya geliyor.

Umarım mikro servis mimarileri ve temel çalışma prensiplerini anlama konusunda yararlı bir makale olmuştur.

Görüşmek üzere…

Kaynaklar:

http://mustafabas.me/tr/mikroservis-mimarisi-nedir-avantajlari-dezavantajlari-nelerdir–b-1006

Microservice Architecture in ASP.NET Core with API Gateway