You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
9.2 KiB
Markdown
273 lines
9.2 KiB
Markdown
---
|
|
marp: true
|
|
paginate: true
|
|
math: mathjax
|
|
theme: buutti
|
|
title: 4. RESTful HTTP Methods
|
|
---
|
|
|
|
# RESTful HTTP Methods
|
|
|
|
<!-- headingDivider: 5 -->
|
|
<!-- class: invert -->
|
|
|
|
## Contents
|
|
|
|
- [HTTP methods for RESTful APIs](#http-methods-for-restful-apis)
|
|
- [HTTP `POST` method](#http-post-method)
|
|
- [HTTP `PUT` method](#http-put-method)
|
|
- [HTTP `DELETE` method](#http-delete-method)
|
|
- [HTTP `PATCH` method](#http-patch-method)
|
|
|
|
## HTTP methods for RESTful APIs
|
|
|
|
### RESTful API
|
|
|
|
* We'll be extending our Web API into a full-blown ***RESTful API***
|
|
* More about REST in [Frontend Basics Lecture 3: REST Architecture](https://gitea.buutti.com/education/frontend-basics/src/branch/main/3-rest-architecture.md)
|
|
* A good idea to also check out [Frontend Basics Lecture 2: HTTP methods](https://gitea.buutti.com/education/frontend-basics/src/branch/main/2-http.md)
|
|
* We have so far implemented `GET` and `POST` methods for reading resources from the API and creating new ones to it, respectively
|
|
* All the primary methods for following the uniform interface requirement are `GET`, `POST`, `PUT`, `PATCH` and `DELETE`
|
|
* Others exist, but these are by far most commonly used
|
|
* These methods correspond to ***CRUD*** operations *__Create__*, *__Read__*, *__Update__* and *__Delete__*
|
|
* CRUD describes what is done to a resource after a request is sent
|
|
|
|
### Primary HTTP request methods
|
|
|
|
The primary HTTP request methods with descriptions:
|
|
|
|
| Method | Attribute | Description |
|
|
|:--------|:---------------|:------------------------------------------|
|
|
| `GET` | `[HttpGet]` | ***Read*** a representation of a resource |
|
|
| `POST` | `[HttpPost]` | ***Create*** new resources |
|
|
| `PUT` | `[HttpPut]` | Fully ***update*** an existing resource |
|
|
| `PATCH` | `[HttpPatch]` | Partially ***update*** an existing resource |
|
|
| `DELETE` | `[HttpDelete]` | ***Delete*** a resource |
|
|
|
|
[https://www.restapitutorial.com/lessons/httpmethods.html](https://www.restapitutorial.com/lessons/httpmethods.html)
|
|
|
|
## HTTP `POST` method
|
|
|
|
### Handling `HttpPost` Requests
|
|
|
|
* Remember that HTTP requests can include a content body
|
|
* In ASP.NET, this content is assigned to a variable with the `[FromBody]` attribute:
|
|
```csharp
|
|
[HttpPost]
|
|
public string[] Post([FromBody] string someContent)
|
|
{
|
|
// someContent holds the content of the request body
|
|
return new string[] { text };
|
|
}
|
|
```
|
|
* Note that the `[FromBody]` attribute can only be used on one parameter
|
|
* However, nothing prevents you from using a custom type variable
|
|
|
|
---
|
|
|
|
* ASP.NET deserializes the request content body into an object:
|
|
```csharp
|
|
// Models/Contact.cs
|
|
public class Contact
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; }
|
|
}
|
|
```
|
|
* ```csharp
|
|
// Controllers/ContactsController.cs
|
|
[HttpPost]
|
|
public Contact Put(int id, [FromBody] Contact contact)
|
|
{
|
|
// Contacts is a list of Contact objects, fetched from some repository
|
|
Contacts.Add(contact);
|
|
return contact;
|
|
}
|
|
```
|
|
|
|
### Creating a POST Request with Postman
|
|
|
|
#### Request headers
|
|
|
|
* ASP.NET knows to deserialize the content if the content type is set to JSON in the HTTP requests *__headers__*
|
|
* Headers are optional parameters that can be included in every HTTP request
|
|
* Headers are set in a Key-Value format
|
|
* When creating a request in Postman, to inform the server what type of content was just sent, add a new key `Content-Type` and set its value to `application/json` in the _Headers_ tab:
|
|
|
|
<div class='centered'>
|
|
|
|

|
|
|
|
</div>
|
|
|
|
#### Request body
|
|
|
|
After setting the header,
|
|
1) select the Body tab,
|
|
2) change the content type to raw,
|
|
3) select JSON from the dropdown menu, and
|
|
4) insert the content in JSON format
|
|
|
|
<div class='centered'>
|
|
|
|

|
|
|
|
</div>
|
|
|
|
|
|
#### Sending the request
|
|
|
|
* If the `POST` method is routed at `http://localhost:54106/api/contacts`:
|
|
|
|
<div class='centered'>
|
|
|
|

|
|
|
|
</div>
|
|
|
|
### Exercise 1: Creating a `POST` Endpoint
|
|
<!--_class: "exercise invert" -->
|
|
|
|
Continue working on the CourseAPI.
|
|
|
|
1) Create a `POST` method and endpoint which adds a new course to the list in the repository with a running ID. Content and Author values are obtained from the request body:
|
|

|
|
|
|
## HTTP `PUT` method
|
|
|
|
* Use `PUT` to replace an existing resource, e.g. an element in a list, with a new one
|
|
* The ID of the resource to be replaced should be in the request URI
|
|
* The information about the new resource should be in the request body like in `POST` requests
|
|
|
|
<div class='centered'>
|
|
|
|

|
|
|
|
</div>
|
|
|
|
### Handling `HttpPut` requests
|
|
|
|
* The ID is fetched from the URI and the contents from the request body
|
|
* Filtering is used to copy all objects from the original list except for the new `contact` object, which comes from body
|
|
```csharp
|
|
// Controllers/ContactsController.cs
|
|
[HttpPut("{id}")]
|
|
public List<Contact> Put(int id, [FromBody] Contact contact)
|
|
{
|
|
// Contacts is a list of Contact objects, fetched from some repository
|
|
List<Contact> updatedList = Contacts.Select(c => c.Id != id ? c : contact).ToList();
|
|
Contacts = updatedList;
|
|
return updatedList;
|
|
}
|
|
```
|
|
|
|
### Exercise 2: Creating a `PUT` Endpoint
|
|
|
|
* In CoursesController class, create a method for PUT requests with the URI api/courses/{id}
|
|
* The ID of the course to be replaced with should be in the request URI and the contents of the new course should be in the request body.
|
|
* The method should replace the corresponding course from the Courses list in the repository with the new course.
|
|
* The method should return the updated Courses list (for testing purposes)
|
|
* Return a 404 status code if a course with the corresponding ID does not exist
|
|
* Test with Swagger/Postman
|
|
|
|
## HTTP `DELETE` method
|
|
|
|
* Use `DELETE` to delete an existing resource, e.g. an element in a list
|
|
* The ID of the resource to be deleted should be in the request URI
|
|
* As with the `GET` method, a body is not needed
|
|

|
|
|
|
### Handling `HttpDelete` Requests
|
|
|
|
* The ID is fetched from the URI
|
|
```csharp
|
|
// Controllers/ContactsController.cs
|
|
[HttpDelete("{id}")]
|
|
public List<Contact> Delete(int id)
|
|
{
|
|
// Contacts = list of contact objects, fetched from some repository
|
|
List<Contact> updatedList = Contacts.Where(c => c.Id != id).ToList();
|
|
Contacts = updatedList;
|
|
return Contacts;
|
|
}
|
|
```
|
|
|
|
### Exercise 3: Creating a `DELETE` Endpoint
|
|
<!--_class: "exercise invert" -->
|
|
|
|
Continue working on CourseAPI.
|
|
|
|
1) Create an endpoint for `DELETE` requests with the URI `api/courses/{id}`
|
|
a) The ID of the course should be fetched from the URI
|
|
b) The corresponding course should be removed from the list of courses in the repository
|
|
c) The method should return the updated `Courses` list (for testing purposes)
|
|
d) Return a `404` status code if a course with the corresponding ID does not exist
|
|
2) Test with Postman.
|
|
|
|
## HTTP `PATCH` method
|
|
|
|
* Use `PATCH` to *partially* update a resource
|
|
* I.e., update some value inside of a resource
|
|
* This saves some resources as only a part of a resource has to be sent instead of an entire document (as opposed to `PUT` requests)
|
|
* Sending and handling `PATCH` requests with ASP.NET requires some extra work and the use of a third party package [JSON Patch](http://jsonpatch.com/)
|
|
|
|
---
|
|
|
|
* To handle `PATCH` requests in a standardized way, install and add the following NuGet packages to your project:
|
|
* `Microsoft.AspNetCore.JsonPatch`
|
|
* `Microsoft.AspNetCore.Mvc.NewtonsoftJson`
|
|
* Then, change the following line in `Program.cs`:
|
|
```csharp
|
|
builder.Services.AddControllers();
|
|
```
|
|
* to this:
|
|
```csharp
|
|
builder.Services.AddControllers().AddNewtonsoftJson();
|
|
```
|
|
|
|
---
|
|
|
|
* The body of the `PATCH` request needs to be in the following form:
|
|
```json
|
|
[{ "op": "replace", "path": "/propertyName", "value": "newValue"}]
|
|
```
|
|
* Use the URI to specify the ID of the resource to update
|
|
|
|
<div class='centered'>
|
|
|
|

|
|
|
|
</div>
|
|
|
|
### Handling `HttpPatch` Requests
|
|
|
|
* The ID is fetched from the URI
|
|
* The property and its new value are specified in the body, which is retrieved as a `JsonPatchDocument`
|
|
* `PATCH` is operated on the targeted object:
|
|
```csharp
|
|
// Controllers/ContactsController.cs
|
|
[HttpPatch("{id}")]
|
|
public List<Contact> Patch(int id, [FromBody] JsonPatchDocument<Contact> patchDocument)
|
|
{
|
|
// Contacts is a list of contact objects, fetched from some repository
|
|
var contact = Contacts.FirstOrDefault(c => c.Id == id);
|
|
if(contact != null)
|
|
{
|
|
patchDocument.ApplyTo(contact);
|
|
}
|
|
return Contacts;
|
|
}
|
|
```
|
|
|
|
### Exercise 4: Creating a `PATCH` Endpoint
|
|
<!--_class: "exercise invert" -->
|
|
|
|
Continue working on CourseAPI.
|
|
|
|
1) Create an endpoint for PATCH requests with the URI `api/courses/{id}`
|
|
a) The ID of the course should be fetched from the URI
|
|
b) The corresponding course should be updated according to the JSON PATCH document in the request body
|
|
c) The method should return the updated `Courses` list (again, for testing purposes)
|
|
d) Return a `404` status code if a course with the corresponding ID does not exist
|
|
2) Test with Swagger/Postman. Try to change the number of credits for some course!
|