39,59 €
Web API applications have become increasingly significant in recent years, fueled by the ever-accelerating pace of technological advancements. However, with this rapid evolution comes a pressing challenge: the need to create web API applications that are not only functional but also adaptable, maintainable, and scalable to meet the demands of users and businesses alike. This book will help you address this challenge head-on, equipping you with the knowledge and skills required to develop web API applications from scratch.
By providing a deeper understanding of the various protocols implemented by ASP.NET Core, including RESTful, SignalR (WebSocket), gRPC, and GraphQL, supplemented by practical examples and optimization techniques, such as using middleware, testing, caching, and logging, this book offers invaluable insights for both newcomers as well as seasoned developers to meet modern web development requirements. Additionally, you’ll discover how to use cloud platforms such as Azure and Azure DevOps to enhance the development and operational aspects of your application.
By the end of the book, you’ll be fully prepared to undertake enterprise-grade web API projects with confidence, harnessing the latest advancements in ASP.NET Core 8 to drive innovation.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 939
Web API Development with ASP.NET Core 8
Learn techniques, patterns, and tools for building high-performance, robust, and scalable web APIs
Xiaodi Yan
Copyright © 2024 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
Group Product Manager: Rohit Rajkumar
Senior Editor: Nathanya Dias
Technical Editor: K Bimala Singha
Copy Editor: Safis Editing
Project Coordinator: Sonam Pandey
Indexer: Hemangini Bari
Production Designer: Aparna Bhagat
Marketing Coordinators: Anamika Singh and Nivedita Pandey
First published: March 2024
Production reference: 1010324
Published by Packt Publishing Ltd.
Grosvenor House
11 St Paul's Square
Birmingham
B3 1RB, UK.
ISBN 978-1-80461-095-4
www.packtpub.com
When I signed the contract with the publisher, I never anticipated that authoring a book would be so challenging. I owe immense gratitude to my family for their unwavering support and understanding. As I dedicated most of my weekends and holidays to writing this book, I deeply appreciate their patience and encouragement despite the limited time we shared.
My heartfelt appreciation also extends to my friends and colleagues for their constant support and motivation. There were moments when I felt overwhelmed and on the verge of giving up, but their enduring patience and willingness to listen to my frustrations kept me going. Their encouragement was invaluable, and I am truly fortunate to have them in my life.
I also want to extend my heartfelt thanks to my editors, whose patience and understanding surpassed my expectations. My tendency to procrastinate undoubtedly presented challenges and I am sincerely grateful for their guidance and persistence throughout the editing process.
Additionally, I express my gratitude to the reviewers for their invaluable feedback. Their constructive suggestions have significantly enriched the content and contributed to its overall improvement.
As I reflect on this journey, I am immensely proud to have completed this book. It has been a labor of love, fueled by my passion for .NET and the incredible possibilities it offers. I hope you will find as much joy in exploring the world of .NET through these pages as I have experienced in writing about it.
Xiaodi Yan is a seasoned software engineer with a proven track record of success in the IT industry. Since 2015, he has been awarded Microsoft MVP, showcasing his dedication to and expertise in .NET, AI, DevOps, and cloud computing. He is also a Microsoft Certified Trainer (MCT), Azure Solutions Architect Expert, and LinkedIn Learning instructor. Xiaodi often presents at conferences and user groups, leveraging his extensive experience to engage and inspire audiences. Based in Wellington, New Zealand, he spearheads the Wellington .NET User Group, fostering a vibrant community of like-minded professionals.
Connect with Xiaodi on LinkedIn at https://www.linkedin.com/in/xiaodi-yan/ to stay updated on his latest insights.
To my family, whose unwavering support and understanding made this journey possible. Your constant support got me through those late nights and tough writing sessions.
And to you, the reader: it is my pleasure to share what I’ve learned with you. May this book inspire and empower you on your own journey in the world of ASP.NET Core.
Rupenkumar Anjaria has more than 20 years of experience in architecting, designing, developing, and maintaining complex enterprise web applications. His primary technical experience includes working on .NET, C#, Angular, and SQL Server technologies, with several years of experience in ServiceNow and React, MongoDB, MVC, TFS, and Jenkins.
Aditya Oberai is a developer advocate at Appwrite and a Microsoft MVP. Having worked with various technologies, such as ASP.NET web APIs, .NET MAUI, Svelte, Node.js, and Microsoft Azure, he has spent the last five years empowering tech communities and hackathons across India and beyond. He can often be found interacting with and educating the community about APIs, serverless, cross-platform apps, community building, indie hacking, and open-source.
Ifeoluwa Osungade is a senior software engineer at Agoda, where he channels his deep expertise in C# and web development to craft software solutions that elevate the travel experience for countless customers worldwide. With an impressive career spanning over eight years in the software engineering domain, Ifeoluwa has made significant contributions across diverse industries such as technology, construction, and logistics. This breadth of experience underscores his ability to not only identify and define challenges but also create and implement innovative software solutions that streamline business operations, enhance efficiency, and boost profitability. His other areas of expertise are Scala, Python, SQL, and Azure.
In today’s world, web APIs are the backbone of the web. Millions of people use web APIs every day to purchase commodities, book a flight, get weather information, and more. In this chapter, we will learn about the fundamentals of web APIs. You might be wondering why we will start with the fundamental concepts. The answer is simple – we need to understand the basic concepts of web APIs before we build one.
This chapter introduces a couple of different web API styles, such as a REST-based API, a remote procedure call (RPC)-based API, a GraphQL API, and a real-time API. We will also learn about how to design them. If you would like to start developing a web API, feel free to jump to the next chapter.
In this chapter, we’ll be covering the following topics:
What is a web API?What is a REST API?Designing a REST-based APIWhat are RPC and GraphQL APIs?What is a real-time API?After reading this chapter, you will have a basic understanding of web APIs and be able to pick the right style for your project. Let’s get started!
API stands for application programming interface. A web API, as the name suggests, is a set of programming interfaces for the web. For example, when you book a flight on a website, the browser makes a request to the airline’s server through a web API to access the airline’s database. The airline’s server then returns the information about the flight to the browser, allowing you to book your flight in it.
APIs have been delivered by organizations for decades. With the appearance of the World Wide Web, people needed a way to communicate between the server and the client.
We can build web APIs using different technologies, such as Java, Python, Ruby, PHP, .NET, and so on. Also, they have various styles. You might have heard of terms such as SOAP, Web Service, and REST. They are all based on the HTTP protocol but communicate in different ways.
In this book, we consider web APIs as a wider concept than REST. In the digital world, the way machines communicate with each other changes as either the demands or the infrastructure evolves. In the 1990s, people focused on how to improve the internal networks that used the same platforms. TCP/IP came to be the standard for this kind of communication. After a few years, people needed to find a way to optimize communication across multiple platforms. Web Services appeared, and they used the Simple Object Access Protocol (SOAP), which was defined for enterprises, and it ensured that programs built on different platforms could easily exchange data.
However, SOAP XML is quite heavy, which means it requires more bandwidth for its usage. In the early 2000s, Windows Communication Foundation (WCF) was released by Microsoft. This helped developers manage the complexities of working with SOAP. WCF is RPC-based but still uses SOAP as the underlying protocol. Over time, some old standards, such as SOAP, have been transitioned to REST APIs, which will be discussed in the next section. We will start with REST APIs and then move on to the other styles of web-based APIs, such as gRPC APIs, GraphQL APIs, and SignalR APIs.
REST, also known as Representational State Transfer, is an architectural style of web APIs that was created by Roy Fielding in his Ph.D. dissertation Architectural Styles and the Design of Network-based Software Architectures in 2000. Today, generally speaking, REST APIs are based on HTTP, but actually, Roy Fielding’s paper just outlines the core concepts and constraints for understanding an architectural style, and it does not require any specific protocol for REST-based architecture, such as HTTP. However, since HTTP is the most widely used protocol for web APIs, we will use HTTP as the protocol for REST APIs.
Just keep in mind that REST is just a style, not a rule. When you build a web API, you do not have to follow the REST style. You can use any other style you like. You can build a web API that works well, but it might not be REST enough. REST is the recommended style because it helps us establish constraints, which contribute to the design of web APIs. It also helps developers easily integrate with other REST APIs if they follow the same style.
The core concept of REST is the term representational state transfer. Think about a web system, which is a collection of resources. For example, it might have a resource called books. The collection of books is a resource. A book is a resource too. When you request the list of the books, you select a link (for example, http://www.example.com/books), which will return a JSON string that contains all the books, resulting in the next resource’s representation, such as the link of a specific book (for example, http://www.example.com/books/1). You can continue to request the book with this link. In this process, the representation state is transferred to the client and rendered for the user.
There are loads of resources that explain REST. If you would like to know more about REST, you can read the following article on Wikipedia: REST: The Web Framework for Representational State Transfer (https://en.wikipedia.org/wiki/Representational_state_transfer).
Let’s take a look at the constraints of REST, following which we will show you a simple example of REST APIs.
Roy Fielding’s paper defines the following six constraints for REST APIs:
Client-server: This pattern enforces the principle of separation of concerns. The server and the client act independently. The client sends the request and the server responds, following which the client receives and interprets the response. The client does not need to know how the server works, and vice versa.Statelessness: The server does not maintain any state of the client. The client should provide the necessary information in the request. This stateless protocol is important to scale out the capacity of the server because it does not need to remember the session state of the clients.Cacheability: The response of the server must implicitly or explicitly contain information about whether the response is cacheable, allowing the client and intermediaries to cache the response. The cache can be performed on the client machine in memory, in browser cache storage, or in a content delivery network (CDN). It is also important to improve the scalability and performance of web APIs.The layered system: The client does not know how it is connected to the server. There may be multiple layers between the client and the server. For example, a security layer, a proxy, or a load balancer can be placed between the client and the server without impacting the client or server code.Code on demand (optional): The client can request code from the server for client-side use. For example, the web browser can request JavaScript files to perform some tasks.Uniform interface: This one is essential for a RESTful system. It contains resource identification, resource manipulation through representations, self-descriptive messages, and hypermedia as the engine of the application state. It simplifies and decouples the architecture of the system, which enables each part to evolve independently.If you feel these principles are a little bit distant or theoretical, let’s look at an example.
The website https://jsonplaceholder.typicode.com/ is a fake REST API that generates fake JSON data. Open the following link in your browser: https://jsonplaceholder.typicode.com/posts. You will see a JSON string returned:
[ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" }, { "userId": 1, "id": 2, "title": "qui est esse", "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" }, ... ]From the preceding request, we can get the resource for the collection of the posts.
Now, we can request a specific post by its ID. For example, we can request the post with ID 1 using the following URL: https://jsonplaceholder.typicode.com/posts/1. The response is as follows:
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" }And that’s it! The URLs we used in the preceding examples are the identifiers of the resources. The responses (the JSON strings) are the representations of the resources. A resource is manipulated through hypertext representations that are transferred in messages between clients and servers.
Important note
Some documents use URI. A Uniform Resource Identifier (URI) is a unique sequence of characters that identifies a logical or physical resource used by web technologies. It might use a location, name, or both. A Uniform Resource Locator (URL) is a type of URI that points to a resource over a network. It defines its protocol, such as http, https or ftp. Nowadays, the term URL remains widely used, so, we will use that in this book. However, we should know they have different scopes. URI is the superset of URL.
To get a post resource, we send a GET request. There are some other methods for manipulating resources, such as POST, PUT, PATCH, and DELETE, as shown here:
HTTP method
URL
Operation
Description
GET
/posts
Read
Read the collection of the posts
GET
/posts/1
Read
Read a post by its ID
GET
/posts/1/comments
Read
Read the comments of the post
POST
/posts
Create
Create a new post
PUT
/posts/1
Update
Update a post by its ID
PATCH
/posts/1
Update (partial)
Update part of a post by its ID
DELETE
/posts/1
Delete
Delete a post by its ID
Table 1.1 – HTTP methods and URLs for manipulating resources
There are other methods that are less frequently used, such as HEAD, OPTIONS, and TRACE.
As we can see, the HTTP methods are mapped to the create, update, read, and delete (CURD) operations. But was it always this way?
As already mentioned, REST is not a rule or a specification. There is no official standard for REST APIs. Contrary to popular opinion, it does not require JSON. Furthermore, it does not require the use of CRUD patterns. But REST implementation does make use of standards, such as HTTP, URL, JSON, XML, and so on. People apply HTTP methods and JSON to implement REST, but they may not intentionally apply the constraints as originally described in Fielding’s paper. This leads people to disagree on whether their APIs are RESTful or not. Many developers describe their APIs as RESTful, even though these APIs do not satisfy all of the constraints described in Fielding’s paper.
Frankly, it is not beneficial to argue whether a web API is REST enough or not. The goal is to make something work, rather than wasting time on a discussion of this kind of problem. Not everyone has read the original paper. Technology also evolves rapidly. There is a Chinese saying: It doesn’t matter whether it is a white cat or a black cat; as long as it catches mice, it is a good cat.
However, it would be ideal if we follow conventions when we start a greenfield project. Generally, a REST-based API is defined with the following aspects:
A base URL, which is the root of the API, such as http://api.example.com.Semantics of HTTP methods, such as GET, POST, PUT, DELETE, and so on.A media type, which defines state transition data elements, such as application/json, application/xml, and so on.In this book, we will try to follow these conventions when we develop the REST APIs with ASP.NET Core.
Now that we have had an overview of a REST API, let’s see how to design one following the conventions.
To build a REST-based API, there are many steps to take before we write code. The development team needs to communicate with stakeholders and analyze the requirements. Then, they need to write user stories (or job stories) to define the desired outcomes. This requires the insights of domain experts or subject matter experts. We will not cover this part in this book. Instead, next, we will focus on the API design, which is closer to what developers do.
In the past few years, the concept of API-first has gained more traction. The API-first approach means that the APIs are treated as first-class citizens for your project. This creates a contract for how the API is supposed to behave before any code is written. In this way, the development teams can work in parallel because the contract will be established first. Developers do not have to wait for the API to be released before integrating with frontend or mobile apps. They can mock and test the APIs based on the contract. Using tools such as Swagger, the process of building APIs can be automated, such as API documentation, mock APIs, SDKs, and so on. Automation can significantly speed up the development of APIs and applications, which helps to increase the speed to market.
Here are some steps we can follow to design a REST-based API:
Identify the resources.Define the relationships between resources.Identify operation events.Design the URL paths for resources.Map API operations to HTTP methods.Assign response codes.Document the API.If you are familiar with the preceding steps, you can skip them. However, if you are not, read the following sections.
A popular API description format is OpenAPI Specification (OAS). We can use it to describe API modeling and other details of an API. We do not need to include the implementation details at this stage, because we just want to make a contract. SwaggerHub (https://app.swaggerhub.com/home) is a tool we can use to design an API.
REST-based APIs center on resources. A resource is a collection of data, such as a collection of posts or a collection of users. A resource is identified by a URL. The client requests a resource using the URL, and the server responds with a representation of the resource. The representation of the resource is sent in the hypertext format, which can be interpreted by the widest possible range of clients.
It is important to identify the scope of the domain and the relationships between the resources. For example, if you are building a blog system, you may have a collection of posts, and each post has a collection of comments. The scope of an API may evolve as time goes by. More resources may be added to the current domain, or some resources will be removed. Also, relationships may change.
Let’s start small. We can use the blog system as an example. After the requirement analysis, we can identify the following resources:
PostsCategoriesCommentsUsersTagsYou may want to include some properties of each resource in this step. For example, a post has a title, a body, and a published datetime. A comment has a body, a publish datetime. A user has a name and an email. You may find more properties during development.
Once the resources are identified, we can define the relationships between them. For example, a post has a collection of comments. A comment has a post. A user has a collection of posts.
The relationship is defined by how these resources relate to each other. Sometimes, these relationships exist in the database as well, but sometimes, they are specific to the REST resources only.
There are some terms we can use to describe the relationships:
Independent resource: This resource can exist independently. It does not require another resource to exist. An independent resource can reference other independent or dependent resources. For example, a post is an independent resource, and it can reference its author. The authors resource is also an independent resource.Dependent resource: This resource requires another resource to exist. It can still reference other independent or dependent resources, but it cannot exist without the existence of the parent resource. For example, a comment requires a post as its parent resource; otherwise, it cannot exist. A comment can reference its author, which is an independent resource.Principal key (or primary key): This resource has a unique identifier. It may be the primary key of the resource in the database. For example, a post has a Id property, which can uniquely identify itself.Foreign key: This dependent resource has a property to reference the principal resource, which stores the principal key of the principal resource. For example, a comment has a PostId property, which references a post.There are three relationship types that these resources can have:
One-to-many: This is when a resource has many related resources. For example, a user has many posts, but a post has only one author. This is also called the parent-child (children) relationship, which is the most common pattern for relationships we can see in the REST-based API world.One-to-one: This is when a resource has one related resource. For example, a post has a single author, and one house has only one address. The one-to-one relationship is a special case of the one-to-many relationship.Many-to-many: This is when a resource has many related resources and vice versa. For example, a blog has many tags, and a tag has many blogs. A movie can have many genres, and a genre can have many movies. In many systems, a user can have many roles, and a role can have many users.Next, we can think about what operations are needed for each resource. These operations may come from user stories that are defined beforehand. Generally, each resource has its CRUD operations. Note that the operations may include more beyond CRUD. For example, a post can be published, or it can be unpublished. A comment can be approved, or it can be rejected. During this process, we may need to create a new resource or property to reflect the operation.
It is important to consider the scope of the domain. CRUD operations are easy to understand, but for some complicated relationships, we may need help from domain experts.
When we work on these operations, we need to include important input and output details. We will use them in the next steps. However, it is not necessary to include all the details of each resource. We have enough time later to capture the complete design.
For the example of the blog system, we can identify these operations for the Post resource (including but not limited to the following):
Operation name
Resource(s)
Input
Output
Description
createPost()
Post, category, user, tag
Post detail
Create a new post
listPosts()
Post
A list of posts
List all posts
listPostsByCategory()
Post and category
Category ID
A list of posts
List posts by category
listPostsByTag()
Post and tag
Tag or Tag ID
A list of posts
List posts by tag
searchPosts()
Post
Search keyword
A list of posts
Search for Posts by title, author, and content
viewPost()
Post, category, and user
Post ID
Post detail
View a post detail
deletePost()
Post
Post ID
Delete a post
updatePost()
Post and category
Post detail
Update a post
publishPost()
Post
Post ID
Publish a post
unpublishPost()
Post
Post ID
Unpublish a post
Table 1.2 – Operations for the Post resource
For some operations, such as createPost and deletePost, the output is the results of the operation. This can be represented with the HTTP status code. We will discuss this later.
We can list more operations for other resources as well.
The next step is to design the URL paths for each resource. The clients use URLs to access the resources. Even though REST is not a standard, there are some guidelines or conventions for designing URL paths.
Using nouns instead of verbs
The operation events we identified in the previous step are some actions, such as Create, List, View, Delete, and so on. However, the URL paths are not usually presented by verbs. Because HTTP methods such as GET, POST, PUT, and DELETE are already verbs, it is not necessary to include verbs in the URL paths. Instead, we should use nouns to represent the resources – for example, /posts.
If a resource is a collection, we should use plural nouns to represent the resource. For example, /posts is the URL path for the collection of posts. To get a single post by its ID, we can use /posts/{postId}.
For the resources that have a relationship, normally, the child resource (i.e., the dependent resource) should be nested under the parent resource, and the path should include the parent identifier. However, this does not reflect the database structure. For example, a post can have a collection of comments; the URL looks like /posts/{postId}/comments. It clearly shows that the comments are related to the post.
However, if the relationships are too deep or complicated, the nesting URL path can be too long. In this case, we can rethink how to better represent those resources. For example, if we want to retrieve an author’s information from one comment, we could use /posts/{postId}/comments/{commentId}/author. But this goes too far. Instead, if we know the UserId of the author, we can use /users/{userId}. Avoid using deep nesting in URL paths because it makes an API more complicated and not readable.
Returning all records simultaneously is not a good idea. We can use filtering, sorting, and pagination to return a subset of the records that a client needs. These operations can improve the performance of the APIs and provide a better user experience.
For example, if we want to search a list of posts for a specific keyword, we can use a query parameter, such as /posts?search=keyword. If we want to sort posts by the date, we can use /posts?sort=date. To get the second page of the posts, we can use /posts?page=2. These query parameters can be combined witheach other.
Generally, HTTP methods can represent CRUD operations. However, in the real world, there are many more complexities! For example, besides the basic CRUD operations, there are other operations, such as publishing or unpublishing a post. So, what HTTP methods should we use?
This is where things can get tricky. This subject is open to debate, but remember that we are not arguing whether an API is RESTful enough. We just want to make it work.
There are different approaches for these scenarios:
One possible solution could be treating such operations like a sub-resource. So, you can use /posts/{postId}/publish to publish a post. GitHub uses the following URL to star a gist: /gists/{gist_id}/star. For more information, check out https://docs.github.com/en/rest/gists/gists#star-a-gist.The post should have an IsPublished field to indicate whether it is published. So, actually, the publish action is an update action, which updates the IsPublished field only. Then, you can treat it the same as the updatePost() operation.Here are some resource URLs for the blog system:
Operation name
URL
Input
Output
Description
createPost()
/posts
Post detail
Create a new post
listPosts()
/posts
A list of posts
List all posts
listPostsByCategory()
/posts?categoryId={categoryId}
Category ID
A list of posts
List posts by category
listPostsByTag()
/posts?tag={tagId}
Tag or Tag ID
A list of posts
List posts by tag
searchPosts()
/posts?search={keyword}
Search keyword
A list of posts
Search for posts by title, author, and content
viewPost()
/posts/{postId}
Post ID
Post detail
View a post detail
deletePost()
/posts/{postId}
Post ID
Delete a post
updatePost()
/posts/{postId}
Post detail
Update a post
publishPost()
/posts/{postId}/publish
Post ID
Publish a post
unpublishPost()
/posts/{postId}/unpublish
Post ID
Unpublish a post
Table 1.3 – URLs for the Post resource
Some URLs are identical, such as deletePost() and updatePost(), because we will use HTTP methods to differentiate those operations.
Next, we need to identify which HTTP method is appropriate for each operation. As we mentioned before, there are some common HTTP methods for CRUD operations. For example, when we request a resource, we should use the GET method. When we create a new resource, we should use the POST method.
When we map the API operations to HTTP methods, we also need to consider the safety of the operations and HTTP methods. There are three types of safety for HTTP operations:
Safe: The operation is safe. It does not change any state of the resource. For example, if we send a GET request to /posts/{postId}, it will return the same result, no matter how many times the same request is sent. For some cases, the resource might be updated by a third party, and the next GET request will return the updated result. But this was not caused by the client, so it is important to understand whether the state change is caused by the client who sent the request.Idempotent: The operation is idempotent, which means it makes state changes to the target resource, but it will produce the same result if the same input is provided. Note that idempotency does not mean that the server must return the same response. For example, if we want to delete a post, we can send a DELETE request to /posts/{postId} to delete it. If the request is a success, we will get a 200 OK or a 204 No Content response. If we send the same request to /posts/{postId} again, it may return a 404 Not found response because the resource was already deleted, but it will not cause any other side effects. If an operation is idempotent and the client knows whether the previous request failed, it is safe to reissue the request without any side effects.Unsafe: The operation is unsafe. It makes state changes to the target resource. If the request is sent again, it cannot guarantee the same result. For example, we can send a POST request to /posts to create a new post. If we send the same POST request again, it will create another new post with the same title and content, and so on.All safe methods are also idempotent, but not all idempotent methods are safe. The following table lists the safety of each HTTP method:
HTTP method
Safe
Idempotent
Common operations
GET
Yes
Yes
Read, list, view, search, show, and retrieve
HEAD
Yes
Yes
HEAD is used to check the availability of a resource without actually downloading it.
OPTIONS
Yes
Yes
OPTIONS is used to retrieve the available HTTP methods for a given resource.
TRACE
Yes
Yes
TRACE is used to get diagnostic information about a request/response cycle.
PUT
No
Yes
Update and replace
DELETE
No
Yes
Delete, remove, and clear
POST
No
No
Create, add, and update
PATCH
No
No
Update
Table 1.4 – Safety of HTTP methods
The following table shows how operations are mapped to HTTP methods:
Operation name
URL
HTTP method
Description
createPost()
/posts
POST
Create a new post
listPosts()
/posts
GET
List all posts
listPostsByCategory()
/posts?categoryId={categoryId}
GET
List posts by category
listPostsByTag()
/posts?tag={tagId}
GET
List posts by tag
searchPosts()
/posts?search={keyword}
GET
Search for posts by title, author, and content
viewPost()
/posts/{postId}
GET
View a post detail
deletePost()
/posts/{postId}
DELETE
Delete a post
updatePost()
/posts/{postId}
PUT
Update a post
publishPost()
/posts/{postId}/publish
PUT
Publish a post
unpublishPost()
/posts/{postId}/unpublish
PUT
Unpublish a post
Table 1.5 – Mapping HTTP methods for the Post resource
You may have seen some other cases, such as using POST to update a resource. That works, but it does not follow the HTTP standard. Generally speaking, we can state the following:
GET is used to read resources.POST is used to create child resources with a server-defined URL, such as /posts.PUT is used to create or replace the resource with a client-defined URL, such as /posts/{postId}.. In many cases, PUT can also be used to update a resource.PATCH is used to update parts of the resource with a client-defined URL, such as /posts/{postId}.It is time to assign the HTTP response codes for the operations. There are some main response code categories:
2xx codes – success: The action requested by the client was received, understood, and accepted.3xx codes – redirection: The client must take additional action to complete the request. It is often used to indicate that the client should be redirected to a new location.4xx code – client errors: The operation was not successful, but the client can try again.5xx codes – server errors: The server has encountered an error or is incapable of performing the request. The client can retry in the future.A common issue is that some developers invent their own response codes. For example, if we create a new post, we expect the server to return a 201 Created response code. Some developers may use 200 OK and include a status code in the response body. This is not a good idea. There are many layers between the server and the client. Using your own codes will probably cause problems for these middleware components. Make sure to use the right code for the right reason. Here are some common response codes:
HTTP response code
Description
200 OK
The standard response for successful HTTP requests.
201 Created
The request has been fulfilled, resulting in a new resource being created.
202 Accepted
The request has been accepted for processing, but processing has not been completed.
204 No Content
The server has successfully processed the request but does not return any content. This is common for delete operations.
400 Bad Request
The server cannot understand or process the request due to a client error, such as malformed syntax, a request size too large, or invalid input. The client should not repeat the request without modifications.
401 Unauthorized
The request requires user authentication.
403 Forbidden
The server understood the request but is refusing action. This may be due to the fact that the client does not have the necessary permissions or is attempting a prohibited action.
404 Not Found
The requested resource could not be found.
500 Internal Server Error
A generic error message. The server encountered an unexpected condition, so it cannot process the request, and no more specific messages are suitable at this time.
503 Service Unavailable
The server is currently unable to handle the request due to temporary overloading or maintenance of the server. The response should contain a Retry-After header if possible so that the client can retry after the estimated time,
Table 1.6 – Common HTTP response codes
Here is the table that shows the response codes for each operation:
Operation name
URL
HTTP method
Response
Description
createPost()
/posts
POST
Post, 201
Create a new post
listPosts()
/posts
GET
Post[], 200
List all posts
listPostsByCategory()
/posts?categoryId={categoryId}
GET
Post[], 200
List posts by category
listPostsByTag()
/posts?tag={tagId}
GET
Post[], 200
List posts by tag
searchPosts()
/posts?search={keyword}
GET
Post[], 200
Search for posts by title, author, and content
viewPost()
/posts/{postId}
GET
Post, 200
View a post detail
deletePost()
/posts/{postId}
DELETE
204, 404
Delete a post
updatePost()
/posts/{postId}
PUT
200
Update a post
publishPost()
/posts/{postId}/publish
PUT
200
Publish a post
unpublishPost()
/posts/{postId}/unpublish
PUT
200
Unpublish a post
Table 1.7 – Response codes for the Post resource
It is essential to utilize the correct response code in order to prevent any misunderstandings. This will ensure that all communication is clear and concise, thus avoiding any potential confusion.
Technically, you can create your own status codes, but in practice, please stick as closely to the standards as possible. If you invent your own status codes, that would be risky. Your users might be in trouble consuming your APIs because they do not know your status codes. You should think about what the benefits are to have your own status codes. The convention is to respect the HTTP status codes defined in RFC. Before you create your own status codes, make sure you check the list of HTTP status codes first. Do not create your own status code unless you have strong reasons. You can find more information here: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.
However, there might be some special situations where you want to indicate a more specific status in the response. For example, you might have an API that can process a task, but it might fail for different reasons. You might want to indicate a more detailed message in the response to let your users know what happened, rather than returning a common 4xx code. You should think about the business logic carefully and differentiate between HTTP status codes and business status codes. If you cannot find a proper code in the HTTP status codes, and you do want to show a business-related status in the response, you can choose the HTTP status code to indicate the category of the response, and then attach a response body that contains your business status code. For example, you can return a response as shown here:
400 Bad Request{ "error_code": 1, "message": "The post is locked and cannot be updated."}So, the HTTP status code represents the common status of the operation, and in the response body, you can include some information that is specific to your system. We will discuss how to handle errors using the Problem Details object in Chapter 16.
OpenAPI is a popular REST API specification. It is a programming language-agnostic interface description for REST APIs, allowing both humans and computers to discover and understand the capabilities of a service without access to source code. Similar to an interface, it describes the inputs and outputs of an API, as well as how they should be transmitted. It is also known as the Swagger specification.
Swagger versus OpenAPI
Sometimes, Swagger and OpenAPI are used interchangeably. The Swagger project was developed in early 2010s to define a simple contract for an API that contains everything needed to produce or consume an API. It was donated to the OpenAPI initiative in 2015. So, OpenAPI refers to the API specification, and Swagger refers to the open-source and commercial projects from SmartBear, which work with the OpenAPI specification. In short, OpenAPI is a specification, and Swagger is tooling that uses the OpenAPI specification. Swagger UI is also one of the Swagger tools. At the time of writing, the latest version of OpenAPI was 3.1.0.
We can use SwaggerHub to design an API based on the previous steps. Here is an example, which defines a simple API for a blog system:
openapi: 3.0.0servers: - description: SwaggerHub API Auto Mocking url: https://virtserver.swaggerhub.com/yanxiaodi/MyBlog/1.0.0 info: description: This is a simple API version: '1.0.0' title: Sample Blog System API contact: email: [email protected] license: name: Apache 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0.html' tags: - name: admins description: Secured Admin-only calls - name: developers description: Operations available to regular developers paths: /posts: get: tags: - developers summary: searches posts operationId: searchPost description: | By passing in the appropriate options, you can search for available blogs in the system parameters: - in: query name: searchString description: pass an optional search string for looking up post required: false schema: type: string - in: query name: skip description: number of records to skip for pagination schema: type: integer format: int32 minimum: 0 - in: query name: limit description: maximum number of records to return schema: type: integer format: int32 minimum: 0 maximum: 50 responses: '200': description: search results matching criteria content: application/json: schema: type: array items: $ref: '#/components/schemas/Post' '400': description: bad input parameterThe other file of this file has been omitted.
The preceding API documentation is a YAML file, which defines two models (resources) – Post and Category – and two operations – GET for searching posts and POST for creating a new post. For each operation, there are details about the input and output, including the expected response codes.
After the API design is done, we can share the API documentation with other developers for integrations, as shown here:
Figure 1.1 – The SwaggerHub UI
Note that you might need to add more properties, based on your user stories and domains, before you share the API documentation with other teams. The API contract should be quite stable; otherwise, it will impact the consumers.
We have explained how to design a REST API. If you would like to learn how to start developing with ASP.NET Core, you can move on to Chapter 2.
REST API is one of the most popular API styles. In the next section, we will introduce other API styles, such as RPC APIs, GraphQL APIs, and real-time APIs.
While REST-based APIs are widely used in many scenarios today, it is not the only style of web API. For some scenarios, RPC-based APIs or GraphQL APIs may be better suited. It is important to understand the advantages and disadvantages of each style of API so that you can choose the right styles for your scenarios.
RPC has