finish lecture 5

main
borb 4 weeks ago
parent 56278a2788
commit 4241f06b0d

File diff suppressed because one or more lines are too long

@ -1,150 +1,154 @@
# Model validation & API Design ---
marp: true
# Model Validation paginate: true
math: mathjax
ASP.NET has a built-in system for validating whether the data sent in requests fits the model set in the code theme: buutti
title: 5. Model Validation & API Design
Additional requirements can be set with attributes ---
public class Contact # Model Validation & API Design
{ <!-- headingDivider: 5 -->
<!-- class: invert -->
public int Id { get; set; }
## Model validation in ASP.NET
[Required]
* ASP.NET has a built-in system for validating whether the data sent in requests fits the model set in the code
[MaxLength(50)] * Additional requirements can be set with attributes
```csharp
public string Name { get; set; } public class Contact
{
[MaxLength(100)] public int Id { get; set; }
public string Email { get; set; } [Required]
[MaxLength(50)]
} public string Name { get; set; }
# Model Validation (continued) [MaxLength(100)]
public string Email { get; set; }
An object can be validated at any time with TryValidateModel method: }
```
[HttpPost]
### `TryValidateModel`
public IActionResult Post([FromBody] Contact contact)
* An object can be validated at any time with `TryValidateModel` method:
{ ```csharp
[HttpPost]
if (!TryValidateModel(contact)) public IActionResult Post([FromBody] Contact contact)
{
{ if (!TryValidateModel(contact))
{
return BadRequest(ModelState); return BadRequest(ModelState);
}
} Contacts.Add(contact);
return Created(Request.Path, contact);
Contacts.Add(contact); }
```
return Created(Request.Path, contact);
---
}
* The attributes set a corresponding error message and information to the `ModelState` which is sent with the bad request result
The attributes set a corresponding error message and information to the ModelState which is sent with the bad request result
<div class='columns23' markdown='1'>
![](imgs/6-model-validation-and-designing-apis_0.png) <div markdown='1'>
![](imgs/6-model-validation-and-designing-apis_1.png) ![](imgs/6-model-validation-and-designing-apis_1.png)
Sometimes you might have custom requirements and the action should, according to the standard, return a bad request if the action does not fit those requirements </div>
<div markdown='1'>
For example, the simplest possible way to validate the format of an email is to check whether it contains the '@' symbol and with AddModelError add an error if it does not:
if (!contact.Email.Contains('@'))
{
ModelState.AddModelError("Description", "The email is not in valid form.");
}
Then check the model state: ![](imgs/6-model-validation-and-designing-apis_0.png)
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
# Exercise 1: </div>
</div>
---
* Sometimes you might have custom requirements and the action should, according to the standard, return a bad request if the action does not fit those requirements
* For example, the simplest possible way to validate the format of an email is to check whether it contains the `@` symbol
* Add an error with `AddModelError` if it does not contain the symbol:
```csharp
if (!contact.Email.Contains('@'))
{
ModelState.AddModelError("Description", "The email is not in valid form.");
}
```
* Then check the model state:
```csharp
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
```
## Exercise 1: Add validation
<!--_class: "exercise invert" -->
Continue working on CourseAPI. Continue working on CourseAPI.
Mark all the properties of the Course class with the [Required] attribute 1) Mark all the properties of the Course class with the `[Required]` attribute
2) Create an endpoint for `POST` requests with the URI `api/courses`
Create an endpoint for POST requests with the URI api/courses 3) All the contents of the new course should come from the request body and the new course should be added to the `Courses` list
4) The maximum number of credits should be 20 and minimum 1 (Tip: Use `Range`)
All the contents of the new course should come from the request body and the new course should be added to the Courses list 5) Return an appropriate response
The maximum number of credits should be 20 and minimun 1 (Tip: Range) ## Limiting `PATCH` with validation
Return an appropriate response * Suppose we only want to allow the `PATCH` operation to affect `Name` and `Email` properties of the `Contact` class
* Begin by declaring a class with only those fields:
# Model Validation - Limiting PATCH ```csharp
// Models/ContactPatch.cs
* If we only want to allow the PATCH operation to affect Name and Email properties of Contact class public class ContactPatch
* Begin by declaring a class with only those fields: {
* public class ContactPatch public string Name { get; set; }
* { public string Email { get; set; }
* public string Name { get; set; } }
* public string Email { get; set; } ```
* }
---
# Model Validation - Limiting PATCH (continued)
* Use the `ContactPatch` class instead of the actual class for patching:
Use the ContactPatch class instead of the actual class for patching: ```csharp
// Controllers/ContactsController.cs
[HttpPatch("{id}")] [HttpPatch("{id}")]
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<ContactPatch> patchDocument)
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<ContactPatch> patchDocument) {
Contact initialContact = _contactRepository.GetContact(id);
{ ContactPatch patchContact = new ContactPatch
{ Name = initialContact.Name, Email = initialContact.Email };
Contact initialContact = _contactRepository.GetContact(id);
patchDocument.ApplyTo(patchContact, ModelState);
ContactPatch patchContact = new ContactPatch
if (!ModelState.IsValid)
{ Name = initialContact.Name, Email = initialContact.Email }; {
return BadRequest();
patchDocument.ApplyTo(patchContact, ModelState); }
if (!ModelState.IsValid) initialContact.Name = patchContact.Name;
initialContact.Email = patchContact.Email;
{
_contactRepository.UpdateContact(id, initialContact);
return BadRequest(); return Ok(initialContact);
}
} ```
initialContact.Name = patchContact.Name; ## Designing APIs
initialContact.Email = patchContact.Email; * It can be useful to sketch an overview of the API before starting to implement it.
* Write a table with all the endpoints, writing out the
_contactRepository.UpdateContact(id, initialContact); * HTTP method
* Description of the method
return Ok(initialContact); * HTTP code on success
* HTTP codes on different failure states
} * See the following example!
# Designing APIs ---
It's useful to get a good overview of the API for yourself and others before getting to work | Method | Endpoint | Description | Success | Failure |
|:---------|:---------------------|:---------------------------|:-----------------|:-----------------------------------|
| __Method__ | __Endpoint__ | __Description__ | __Success__ | __Failure__ | | `GET` | `/api/contacts` | Return all contacts | `200 OK` | `400 Bad Request`, `404 Not Found` |
| :-: | :-: | :-: | :-: | :-: | | `POST` | `/api/contacts` | Add a new contact | `201 Created` | `400 Bad Request` |
| __GET__ | __/api/contacts__ | __Return all contacts__ | __200 Ok__ | __400 Bad request__ __404 Not found__ | | `PUT` | `/api/contacts/{id}` | Update a contact | `204 No Content` | `400 Bad Request`, `404 Not Found` |
| __POST__ | __/api/contacts__ | __Add a new contact__ | __201 Created__ | __400 Bad request__ | | `PATCH` | `/api/contacts/{id}` | Partially update a contact | `204 No Content` | `400 Bad Request`, `404 Not Found` |
| __PUT__ | __/api/contacts/{id}__ | __Update a contact__ | __204 No content__ | __400 Bad request__ __404 Not found__ | | `DELETE` | `/api/contacts/{id}` | Remove a contact | `200 OK` | `404 Not Found` |
| __PATCH__ | __/api/contacts/{id}__ | __Partially update a contact__ | __204 No content__ | __400 Bad request__ __404 Not found__ |
| __DELETE__ | __/api/contacts/{id}__ | __Remove a contact__ | __200 OK__ | __404 Not found__ |

Loading…
Cancel
Save