web APIs with ASP.NET Core Flashcards

1
Q

Web api Controllers

A

ASP.NET Core supports creating RESTful services, also known as web APIs, using C#. To handle requests, a web API uses controllers. Controllers in a web API are classes that derive from ControllerBase. This article shows how to use controllers for handling web API requests.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Controller class vs ControllerBase

A

Controller Class-A base class for an MVC controller with view support.(Inheritance
Object–
ControllerBase–
Controller)
ControllerBase- web api controllers are dervied from here

Don’t create a web API controller by deriving from the Controller class. Controller derives from ControllerBase and adds support for views, so it’s for handling web pages, not web API requests. There’s an exception to this rule: if you plan to use the same controller for both views and web APIs, derive it from Controller.

ControllerBase class provides many properties and methods that are useful for handling HTTP requests.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

ControllerBase.CreatedAtAction returns a 201 status code:

A

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult Create(Pet pet)
{
pet.Id = _petsInMemoryStore.Any() ?
_petsInMemoryStore.Max(p => p.Id) + 1 : 1;
_petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

examples of methods that ControllerBase provides

A

Method Notes
BadRequest Returns 400 status code.
NotFound Returns 404 status code.
PhysicalFile Returns a file.
TryUpdateModelAsync Invokes model binding.
TryValidateModel Invokes model validation.

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase?view=aspnetcore-3.1

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Content Negotiation

A

Produces and Consumes Attributes are newly introduced in MVC 6 (ASP.NET Core) to control the content negotiation.

What is Content Negotiation?

The process of picking the correct output format is known as Content Negotiation.

Generally, Frameworks will accept two types of input/output formats those are JSON and XML. Nowadays, every framework by default accepts and returns in JSON format because of its advantages. If the user wants to control this default behavior, he should send an Accept Header with an appropriate format in GET or POST request from a client such that Framework will make sure to use the given format.

If a user doesn’t provide any Accept-Header, then the framework will decide which format it should use based on its settings. Some of the most popular browsers such as Chrome and Firefox will by default add Accept Header as application/xml and that is the reason when we call the web API from the browser will display the output in XML format if we don’t explicitly set the output format as JSON.

Below are the sample requests generated from Chrome and IE where can observe the Accept Headers for these two browsers.

Chrome

GET / HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8

IE

GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, /

If you want to see the output in JSON format, it’s preferred to use any tools like fiddler, browser dev tools etc.

To control this and let the user be given full control of how to receive and sent the data format, Produces and Consumes Attribute is introduced in new ASP.NET MVC Core.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Produces Attribute

A

This attribute helps the user to inform the framework to generate and send the output result always in given content type as follows.
[Produces(“application/json”)]
The above line is saying to the framework to use JSON as output format. This attribute can be decorated at controller level as well as Action level. Action level will be taken as the first priority if it is declared at both controller level as well as Action level.

If you want to control it globally, you can set this while configuring MVC in ConfigureServices method as follows.
Configure(options =>
Filters.Add(newProducesAttribute(“application/json”))
);

https://www.c-sharpcorner.com/article/understanding-produces-and-consumes-attribute-in-mvc-6/

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Consumes Attribute

A

An Attribute helps the user to inform the framework to accept the input always in given content type as follows.
[Consumes(“application/json”)]
The above line is saying to the framework to use JSON as input format. This attribute can be decorated at controller level as well as Action level. Action level will be taken as the first priority if it is declared at both controller level as well as Action level.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Attributes in Web api

A

Attribute Notes
[Route] Specifies URL pattern for a controller or action.
[Bind] Specifies prefix and properties to include for model binding.
[HttpGet] Identifies an action that supports the HTTP GET action verb.
[Consumes] Specifies data types that an action accepts.
[Produces] Specifies data types that an action returns.

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc?view=aspnetcore-3.1

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

ApiController attribute

A

The [ApiController] attribute can be applied to a controller class to enable the following opinionated, API-specific behaviors:

Attribute routing requirement
Automatic HTTP 400 responses
Binding source parameter inference
Multipart/form-data request inference
Problem details for error status codes
The Problem details for error status codes feature requires a compatibility version of 2.2 or later. The other features require a compatibility version of 2.1 or later.

The [ApiController] attribute can be applied to specific controllers, as in the following example from the project template:

C#

Copy
[ApiController]
[Route(“[controller]”)]
public class WeatherForecastController : ControllerBase

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Attribute on multiple controllers

A

One approach to using the attribute on more than one controller is to create a custom base controller class annotated with the [ApiController] attribute. The following example shows a custom base class and a controller that derives from it:

C#

Copy
[ApiController]
public class MyControllerBase : ControllerBase
{
}
C#

Copy
[Produces(MediaTypeNames.Application.Json)]
[Route(“[controller]”)]
public class PetsController : MyControllerBase

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Attribute on an assembly

A

If compatibility version is set to 2.2 or later, the [ApiController] attribute can be applied to an assembly. Annotation in this manner applies web API behavior to all controllers in the assembly. There’s no way to opt out for individual controllers. Apply the assembly-level attribute to the namespace declaration surrounding the Startup class:

C#

Copy
[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Conventional routing - Does not work in asp.net core web api method accessing

A

The default route:

C#

Copy
routes.MapRoute(“default”, “{controller=Home}/{action=Index}/{id?}”);
is an example of a conventional routing. We call this style conventional routing because it establishes a convention for URL paths:

the first path segment maps to the controller name

the second maps to the action name.

the third segment is used for an optional id used to map to a model entity

Using this default route, the URL path /Products/List maps to the ProductsController.List action, and /Blog/Article/17 maps to BlogController.Article. This mapping is based on the controller and action names only and isn’t based on namespaces, source file locations, or method parameters.

Tip

Using conventional routing with the default route allows you to build the application quickly without having to come up with a new URL pattern for each action you define. For an application with CRUD style actions, having consistency for the URLs across your controllers can help simplify your code and make your UI more predictable.

Warning

The id is defined as optional by the route template, meaning that your actions can execute without the ID provided as part of the URL. Usually what will happen if id is omitted from the URL is that it will be set to 0 by model binding, and as a result no entity will be found in the database matching id == 0. Attribute routing can give you fine-grained control to make the ID required for some actions and not for others. By convention the documentation will include optional parameters like id when they’re likely to appear in correct usage.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

MvcApplicationBuilderExtensions.UseMvc Method

A

UseMvc(IApplicationBuilder)-This method only supports attribute routing. To add conventional routes use UseMvc(IApplicationBuilder, Action).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Routing

A

The [ApiController] attribute makes attribute routing a requirement. For example:

C#

Copy
[ApiController]
[Route(“[controller]”)]
public class WeatherForecastController : ControllerBase
Actions are inaccessible via conventional routes defined by UseEndpoints, UseMvc, or UseMvcWithDefaultRoute in Startup.Configure.

ASP.NET Core MVC uses the Routing middleware to match the URLs of incoming requests and map them to actions. Routes are defined in startup code or attributes. Routes describe how URL paths should be matched to actions. Routes are also used to generate URLs (for links) sent out in responses.

Actions are either conventionally routed or attribute routed. Placing a route on the controller or the action makes it attribute routed. See Mixed routing for more information.

This document will explain the interactions between MVC and routing, and how typical MVC apps make use of routing features. See Routing for details on advanced routing.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

MvcApplicationBuilderExtensions.UseMvcWithDefaultRoute(IApplicationBuilder) Method

A

Adds MVC to the IApplicationBuilder request execution pipeline with a default route named ‘default’ and the following template: ‘{controller=Home}/{action=Index}/{id?}’.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Attribute routing with Http[Verb] attributes

A

Attribute routing can also make use of the Http[Verb] attributes such as HttpPostAttribute. All of these attributes can accept a route template. This example shows two actions that match the same route template:

C#

Copy
[HttpGet("/products")]
public IActionResult ListProducts()
{
   // ...
}
[HttpPost("/products")]
public IActionResult CreateProduct(...)
{
   // ...
}
For a URL path like /products the ProductsApi.ListProducts action will be executed when the HTTP verb is GET and ProductsApi.CreateProduct will be executed when the HTTP verb is POST. Attribute routing first matches the URL against the set of route templates defined by route attributes. Once a route template matches, IActionConstraint constraints are applied to determine which actions can be executed.

Tip

When building a REST API, it’s rare that you will want to use [Route(…)] on an action method as the action will accept all HTTP methods. It’s better to use the more specific HttpVerbAttributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.

Since an attribute route applies to a specific action, it’s easy to make parameters required as part of the route template definition. In this example, id is required as part of the URL path.

C#

Copy
public class ProductsApiController : Controller
{
   [HttpGet("/products/{id}", Name = "Products_List")]
   public IActionResult GetProduct(int id) { ... }
}
The ProductsApi.GetProduct(int) action will be executed for a URL path like /products/3 but not for a URL path like /products. See Routing for a full description of route templates and related options.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Why Routing?

A

SEO friendly
RESTfully configured routing facilitates the Search Engine Optimization (SEO) of your content. A site’s URL is one of the top criteria that impacts site ranking. By converting www.yourwebsite.com/articles/show/123 to www.yourwebsite.com/how-to-peel-potatoes you encourage search engines to rank it higher for keyphrases related to “how to peel potatoes.”

Also, when you have a URL that is more descriptive, it is easier for users to correctly anticipate the content, leading to increased time on page, which also impacts SEO and your overall page authority.

URLs do not need to map a file
Without routing, an incoming request would be mapped to a physical file. With routing we have full control of the request, allowing us to decide what action and controller we execute when a certain HTTP request comes in.

Long URLs and file extensions can be eliminated
Routing helps to shorten the URL in instances where many parameters and filters are in play. By eliminating the file extension, we can hide what kind of environment we are working in.

So, how do we take advantage of these benefits? Let’s look at five ways you can build routing in your ASP.NET Core application.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q
  1. Creating Default Routes
A

You can define the default route by convention in your project’s Startup class.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
} } With the above, we assure the essential configuration exists in our project for the standard MVC pattern of Controller + Action + ID (Optional) route. You can also declare the routing pattern like this:

routes.MapRoute(
name: “default_route”,
template: “{controller}/{action}/{id?}”,
defaults: new { controller = “Home”, action = “Index” }
);

routes.MapRoute(
name: “default_route”,
template: “{controller}/{action}/{id?}”,
defaults: new { controller = “Home”, action = “Index” }
);
(This is how we used to do routing in ASP.NET Core.)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q
  1. Extending Default Routes - for creating routing
A

Once we have the default route configured, we might want to extend it by adding customized routes based on specific needs. For this, we can add configurations using the MapRoute() method.

app.UseMvc(routes =>
{
    //New Route
    routes.MapRoute(
       name: "about-route",
       template: "about",
       defaults: new { controller = "Home", action = "About" }
    );

routes.MapRoute(
name: “default”,
template: “{controller=Home}/{action=Index}/{id?}”);
});
We added an extra route which grants access to the About action on the Home controller with an /about route. Because the default pattern route is still present, we can also access the About page with the conventional /home/about route.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q
  1. Using Attributes - for creating routing
A

You can also configure routes using attributes in your controller and actions.

[Route("[controller]")]
public class AnalyticsController : Controller
{
    [Route("Dashboard")]
    public IActionResult Index()
    {
        return View();
    }
    [Route("[action]")]
    public IActionResult Charts()
    {
        return View();
    }
}
In this sample we can access to the controller actions with the following routes:

/Analytics/Dashboard
/Analytics/Charts
You can see the two tokens [controller] and [action] indicate that we have to refer to the controller and action name that has been declared. In this case, “Analytics” is the name of the controller, and “Charts” the name of the action, therefore it the name of the route.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q
  1. Building RESTful Routes
A

In order to declare a RESTful controller, we need to use the following route configuration:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable Get()
    {
        return new string[] {"hello", "world!"};
    }
    // POST api/values
    [HttpPost]
    public void PostCreate([FromBody] string value)
    {
    }
}
Here we are telling to our RESTful service to accept calls under the /api/values route. Note that we no longer use the Route attribute for actions. Instead we decorate it with HttpGet, HttpPost, HttpPut, HttpDelete attributes.
Or, we can take a look at a different scenario:
// POST api/values/5
[HttpPost("{id}")]
public void PostUpdate(int id, [FromBody] string value)
{
}
Here we have the following routes for the controller Values

HTTP Post of /values route will invoke Post() action
HTTP Post of /values/PostName route will invoke Post([FromBody]string value) action

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q
  1. Using Constraints on Routing
A

We can restrict the type of value that we pass to actions using constraints. For example, if we expect an argument that is a number we have to restrict it to an integer type. Declare constraints in attributes using curly brackets {id:int}.

[HttpGet("{id:int}")]
public string GetById(int id)
{
    return "item " + id;
}
Here, we are telling the action GetByID to accept only an integer argument. Adding a question mark to the constraints {id:int?} indicates that the parameter is optional. Therefore with a question mark we can call /GetByID/123 or /GetByID without additional parameters. We can also define constraints in default routes declared in the Startup class this way:

routes.MapRoute(
name: “getProductById”,
template: “Products/{id:int}”,
defaults: new { controller = “Products”, action = “GetById” });
There are several available constraints like bool, datetime, decimal, min, max, regex, etc.

If you’re ready to learn more about RESTful application design in ASP.NET Core, check out these resources:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Path parameters in swagger are always needed

A

By default, OpenAPI treats all request parameters as optional. You can add required: true to mark a parameter as required. Note that path parameters must have required: true, because they are always required.

https://swagger.io/docs/specification/describing-parameters/
Though you specify as optional in httpverb [HttpGet(“active/{user?}”)]. it is required in swagger

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Automatic HTTP 400 responses

A

The [ApiController] attribute makes model validation errors automatically trigger an HTTP 400 response. Consequently, the following code is unnecessary in an action method:

C#

Copy
if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}
ASP.NET Core MVC uses the ModelStateInvalidFilter action filter to do the preceding check.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Default BadRequest response

A

With a compatibility version of 2.1, the default response type for an HTTP 400 response is SerializableError. The following request body is an example of the serialized type:

JSON

Copy
{
“”: [
“A non-empty request body is required.”
]
}
With a compatibility version of 2.2 or later, the default response type for an HTTP 400 response is ValidationProblemDetails. The following request body is an example of the serialized type:

JSON

Copy
{
“type”: “https://tools.ietf.org/html/rfc7231#section-6.5.1”,
“title”: “One or more validation errors occurred.”,
“status”: 400,
“traceId”: “|7fb5e16a-4c8f23bbfc974667.”,
“errors”: {
“”: [
“A non-empty request body is required.”
]
}
}
The ValidationProblemDetails type:

Provides a machine-readable format for specifying errors in web API responses.
Complies with the RFC 7807 specification.
Log automatic 400 responses
See How to log automatic 400 responses on model validation errors (aspnet/AspNetCore.Docs #12157).

Disable automatic 400 response
To disable the automatic 400 behavior, set the SuppressModelStateInvalidFilter property to true. Add the following highlighted code in Startup.ConfigureServices:

C#

Copy
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
options.ClientErrorMapping[404].Link =
“https://httpstatuses.com/404”;
});

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

Binding source parameter inference

A

A binding source attribute defines the location at which an action parameter’s value is found. The following binding source attributes exist:

Attribute Binding source
[FromBody] Request body
[FromForm] Form data in the request body
[FromHeader] Request header
[FromQuery] Request query string parameter
[FromRoute] Route data from the current request
[FromServices] The request service injected as an action parameter
Warning

Don’t use [FromRoute] when values might contain %2f (that is /). %2f won’t be unescaped to /. Use [FromQuery] if the value might contain %2f.

Without the [ApiController] attribute or binding source attributes like [FromQuery], the ASP.NET Core runtime attempts to use the complex object model binder. The complex object model binder pulls data from value providers in a defined order.

The [ApiController] attribute applies inference rules for the default data sources of action parameters. These rules save you from having to identify binding sources manually by applying attributes to the action parameters. The binding source inference rules behave as follows:

[FromBody] is inferred for complex type parameters. An exception to the [FromBody] inference rule is any complex, built-in type with a special meaning, such as IFormCollection and CancellationToken. The binding source inference code ignores those special types.
[FromForm] is inferred for action parameters of type IFormFile and IFormFileCollection. It’s not inferred for any simple or user-defined types.
[FromRoute] is inferred for any action parameter name matching a parameter in the route template. When more than one route matches an action parameter, any route value is considered [FromRoute].
[FromQuery] is inferred for any other action parameters.

27
Q

[FromQuery] attribute

A

In the following example, the [FromQuery] attribute indicates that the discontinuedOnly parameter value is provided in the request URL’s query string:

C#

Copy
[HttpGet]
public ActionResult> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List products = null;
    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }
return products; }
28
Q

FromBody inference notes

A

[FromBody] isn’t inferred for simple types such as string or int. Therefore, the [FromBody] attribute should be used for simple types when that functionality is needed.

When an action has more than one parameter bound from the request body, an exception is thrown. For example, all of the following action method signatures cause an exception:

[FromBody] inferred on both because they’re complex types.

C#

Copy
[HttpPost]
public IActionResult Action1(Product product, Order order)
[FromBody] attribute on one, inferred on the other because it’s a complex type.

C#

Copy
[HttpPost]
public IActionResult Action2(Product product, [FromBody] Order order)
[FromBody] attribute on both.

C#

Copy
[HttpPost]
public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
Disable inference rules
To disable binding source inference, set SuppressInferBindingSourcesForParameters to true. Add the following code in Startup.ConfigureServices:

C#

Copy
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
options.ClientErrorMapping[404].Link =
“https://httpstatuses.com/404”;
});

29
Q

Multipart/form-data request inference

A

The [ApiController] attribute applies an inference rule when an action parameter is annotated with the [FromForm] attribute. The multipart/form-data request content type is inferred.

To disable the default behavior, set the SuppressConsumesConstraintForFormFileParameters property to true in Startup.ConfigureServices:

C#

Copy
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
options.ClientErrorMapping[404].Link =
“https://httpstatuses.com/404”;
});

30
Q

Problem details for error status codes

A

When the compatibility version is 2.2 or later, MVC transforms an error result (a result with status code 400 or higher) to a result with ProblemDetails. The ProblemDetails type is based on the RFC 7807 specification for providing machine-readable error details in an HTTP response.

Consider the following code in a controller action:

C#

Copy
if (pet == null)
{
    return NotFound();
}
The NotFound method produces an HTTP 404 status code with a ProblemDetails body. For example:

JSON

Copy
{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Disable ProblemDetails response
The automatic creation of a ProblemDetails for error status codes is disabled when the SuppressMapClientErrors property is set to true. Add the following code in Startup.ConfigureServices:

C#

Copy
services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = true;
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
options.SuppressMapClientErrors = true;
options.ClientErrorMapping[404].Link =
“https://httpstatuses.com/404”;
});

31
Q

Define supported request content types with the [Consumes] attribute

A

By default, an action supports all available request content types. For example, if an app is configured to support both JSON and XML input formatters, an action supports multiple content types, including application/json and application/xml.

The [Consumes] attribute allows an action to limit the supported request content types. Apply the [Consumes] attribute to an action or controller, specifying one or more content types:

C#

Copy
[HttpPost]
[Consumes(“application/xml”)]
public IActionResult CreateProduct(Product product)
In the preceding code, the CreateProduct action specifies the content type application/xml. Requests routed to this action must specify a Content-Type header of application/xml. Requests that don’t specify a Content-Type header of application/xml result in a 415 Unsupported Media Type response.

The [Consumes] attribute also allows an action to influence its selection based on an incoming request’s content type by applying a type constraint. Consider the following example:

C#

Copy
[ApiController]
[Route(“api/[controller]”)]
public class ConsumesController : ControllerBase
{
[HttpPost]
[Consumes(“application/json”)]
public IActionResult PostJson(IEnumerable values) =>
Ok(new { Consumes = “application/json”, Values = values });

[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult PostForm([FromForm] IEnumerable values) =>
    Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values }); } In the preceding code, ConsumesController is configured to handle requests sent to the https://localhost:5001/api/Consumes URL. Both of the controller's actions, PostJson and PostForm, handle POST requests with the same URL. Without the [Consumes] attribute applying a type constraint, an ambiguous match exception is thrown.

The [Consumes] attribute is applied to both actions. The PostJson action handles requests sent with a Content-Type header of application/json. The PostForm action handles requests sent with a Content-Type header of application/x-www-form-urlencoded.

32
Q

Path Variable

A

Issuing a Get request:
https://localhost:44302/api/Alerts/active/3

Controller Method defination:
[HttpGet(“active/{user}”)]
public async Task ListActiveAlerts(string user)
{

defining the parameter in HttpGet makes it path parameter; and makes in mandatory; you could see it as mandatory in swagger

33
Q

Query Parameter

A

Issuing a GET request:
https://localhost:44302/api/Alerts/active?user=3

Controller Method defination:
[HttpGet("active")]
  public async Task ListActiveAlerts(string user)
        {
}
34
Q

Path Variable vs Query Parameter

A

If you want to identify a resource, you should use Path Variable. But if you want to sort or filter items, then you should use query parameter. So, for example you can define like this:
/users # Fetch a list of users
/users?occupation=programer # Fetch a list of programer user
/users/123 # Fetch a user who has id 123

Swagger wants path parameters to be mandatory; Query string parameters are optional

Here’s an example. Suppose you are implementing RESTful API endpoints for an entity called Car. You would structure your endpoints like this:

GET /cars
GET /cars/:id
POST /cars
PUT /cars/:id
DELETE /cars/:id

This way you are only using path parameters when you are specifying which resource to fetch, but this does not sort/filter the resources in any way.

Now suppose you wanted to add the capability to filter the cars by color in your GET requests. Because color is not a resource (it is a property of a resource), you could add a query parameter that does this. You would add that query parameter to your GET /cars request like this:

GET /cars?color=blue

This endpoint would be implemented so that only blue cars would be returned.

As far as syntax is concerned, your URL names should be all lowercase. If you have an entity name that is generally two words in English, you would use a hyphen to separate the words, not camel case.

Ex. /two-words

35
Q

Optional Path parameters

A

[HttpGet(“active/{user?}”)]
public async Task ListActiveAlerts(string user)
{

Postman allows to have optional parameters but Swagger wants the path parameters to be required/mandatory

36
Q

CORS issue when working with Web api and Angular app

A

https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1

37
Q

passing object in the query string http get

A

[HttpGet]

    [SwaggerResponse(200, type: typeof(List))]
    [SwaggerResponse(400, type: typeof(string))]
    [SwaggerResponse(500, type: typeof(string))]
    public async Task ListActivePatient([FromQuery]PatientsQueryStringParametersViewModel patientsQueryStringViewModel)
    {
        patientsQueryStringViewModel.userId = _DEFAULT_USER;
        try
        {

            var data = await _patient.ListActivePatient(patientsQueryStringViewModel);

            var result = _mapper.Map>(data);
                // return the result in a "200 OK" response
                return Ok(result);
            }
            catch (Exception ex)
            {
                // log any exceptions that happen and return the error to the user
                _logger.LogError(ex, "An error occurred");
                return StatusCode(500, "An error occurred");
            }
        }
the values can be passed through swagger or postman
as these are query string parameters, they are not mandatory
38
Q

Optional parameters should be after all the required parameters; assigning value to a parameter makes it as optional

A

[HttpGet]

    [SwaggerResponse(200, type: typeof(List))]
    [SwaggerResponse(400, type: typeof(string))]
    [SwaggerResponse(500, type: typeof(string))]
    public async Task ListActivePatient(int? staffID, int? popmeasureID, bool? watch, bool? chronic, string conditionIDs, string namesearch, string sortcolumn, string sortdirection, int? pagenumber, int? rowsPerPage, int userId=3)
    {
39
Q

General URL description

A

When designing API endpoints, there’s always the need to specify what http method to use for CRUD (Create, Read/Retrieve, Update, Delete) operations. Commonly, this is nailed down as:
Create — POST
Read/Retrieve — GET
Update — PUT/PATCH
Delete — DELETE
Given the mapping above, I won’t be surprised if you think PUT and PATCH do the same thing and are simply aliases but you couldn’t be further from the truth. It’s true that both methods update the resource at a location, but they do so differently. Note that I said “… update resource at a LOCATION”. Why?

Everything on the internet is considered a resource, has a location (URL) and a corresponding identifier (URI). We’ll be silent about the latter 🙂.
Let’s imagine the internet was a street, the houses on this street were prefabricated(that’s a thing in the architectural world now) and the house addresses were URLs. Also, let’s assume the street was divided into numbered plots with a house per plot such that house-1 was on plot-1 and so on. Remember, the houses are prefabricated so they just get dropped at their location. No onsite building required.

40
Q

HttpPUT

A

When you make a PUT request to ‘https://internet-street.com/plot-1’ with a prefabricated house, you’re saying “Please PUT this house in the location marked as plot-1”. That instruction searches through our street for the specified location and replaces the content at that location. If nothing is found at the location, it’ll simply PUT the resource at the location. In this case, a full prefabricated house. Thus, a PUT request always contains a full resource. This is necessary because, a necessary quality of PUT requests is idempotence — the quality of producing the same result even if the same request is made multiple times.
If our intent is to update the house at a location, for example, say we want to add a new window, we’d make a PUT request with a full prefab house that’s identical to the former only with the number of windows changed. That seems like an awful waste of bandwidth and it is. Here’s what the house and PUT request payload would look like.

original:
{
  address: 'plot 1',
  owner: 'segun',
  type: 'duplex',
  color: 'green',
  rooms: '5',
  kitchens: '1',
  windows: 20
}
PUT payload:
{
  address: 'plot 1',
  owner: 'segun',
  type: 'duplex',
  color: 'green',
  rooms: '5',
  kitchens: '1',
  windows: 21
}

We could simply choose to send the data we need and have our server code update resources appropriately, but then, we’d loose the idempotence and its benefits such as reliable caching of responses on the network and reliable updates of resources from retries when the original request fails. PUT requests are particularly useful for major updates. So, how do we make minor updates to our resources (houses) whilst being good citizens of the web. Meet PATCH, the after-thought of REST architecture.
EDIT: Responses to PUT requests are not cacheable. If a PUT request finds a response in a cache infrastructure, that response (cache entry) should be treated as stale.

41
Q

HTTP PATCH

A

https://medium.com/backticks-tildes/restful-api-design-put-vs-patch-4a061aa3ed0b

Several applications extending the Hypertext Transfer Protocol (HTTP)
require a feature to do partial resource modification. The existing
HTTP PUT method only allows a complete replacement of a document.
This proposal adds a new HTTP method, PATCH, to modify an existing
HTTP resource.
– RFC 5789
A PATCH request on the other hand, is used to make changes to part of the resource at a location. That is, it PATCHES the resource — changing its properties. It is used to make minor updates to resources and it’s not required to be idempotent.
If we continue with our example above, we could easily add a new window to the house on plot 1 without having to ship a whole new house. All we have to do is ship the window and PATCH up the old house with a new window. Below is an example of the payload we’d have to send.

ORIGINAL:
{
  address: 'plot 1',
  owner: 'segun',
  type: 'duplex',
  color: 'green',
  rooms: '5',
  kitchens: '1',
  windows: 20
}

PATCH PAYLOAD:
{
windows: 21
}

Since PATCH is not idempotent, failed requests are not automatically re-attempted on the network. Also, if a PATCH request is made to a non-existent url e.g attempting to replace the front door of a non-existent building, it should simply fail without creating a new resource unlike PUT, which would create a new one using the payload. Come to think of it, it’ll be odd having a lone door at a house address 😄.
That about sums up the differences and use cases of both HTTP methods.

42
Q

Idempotency is a property of HTTP methods.

A

A request method is considered idempotent if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. And it’s worthwhile to mention that idempotency is about the effect produced on the state of the resource on the server and not about the response status code received by the client.

To illustrate this, consider the DELETE method, which is defined as idempotent. Now consider a client performs a DELETE request to delete a resource from the server. The server processes the request, the resource gets deleted and the server returns 204. Then the client repeats the same DELETE request and, as the resource has already been deleted, the server returns 404.

Despite the different status code received by the client, the effect produced by a single DELETE request is the same effect of multiple DELETE requests to the same URI.

Finally, requests with idempotent methods can be repeated automatically if a communication failure occurs before the client is able to read the server’s response. The client knows that repeating the request will have the same intended effect, even if the original request succeeded, though the response might be different.

43
Q

Safe Methods

A

RFC 7231
Let’s have a look at the RFC 7231, the document defines the semantics and the content of the HTTP/1.1 protocol. See the quotes below (highlights are mine).

HTTP methods can be safe:

4.2.1. Safe Methods

Request methods are considered “safe” if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource. […]

This definition of safe methods does not prevent an implementation from including behavior that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method. What is important, however, is that the client did not request that additional behavior and cannot be held accountable for it. […]

Of the request methods defined by this specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe. […]

And/or idempotent:

44
Q

Idempotent Methods

A

A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent. […]

Like the definition of safe, the idempotent property only applies to what has been requested by the user; a server is free to log each request separately, retain a revision control history, or implement other non-idempotent side effects for each idempotent request. […]

Summarizing, the HTTP methods are classified as following:

\+---------+------+------------+
| Method  | Safe | Idempotent |
\+---------+------+------------+
| CONNECT | no   | no         |
| DELETE  | no   | yes        |
| GET     | yes  | yes        |
| HEAD    | yes  | yes        |
| OPTIONS | yes  | yes        |
| POST    | no   | no         |
| PUT     | no   | yes        |
| TRACE   | yes  | yes        |
\+---------+------+------------+
45
Q

neither safe nor idempotent

A

https://stackoverflow.com/questions/45016234/what-is-idempotency-in-http-methods
RFC 5789
The RFC 5789 defines the PATCH method, which is neither safe nor idempotent. However, to prevent collisions, PATCH requests can be issued such a way as to be idempotent, as quoted below:

A PATCH request can be issued in such a way as to be idempotent, which also helps prevent bad outcomes from collisions between two PATCH requests on the same resource in a similar time frame. Collisions from multiple PATCH requests may be more dangerous than PUT collisions because some patch formats need to operate from a known base-point or else they will corrupt the resource. Clients using this kind of patch application SHOULD use a conditional request such that the request will fail if the resource has been updated since the client last accessed the resource. For example, the client can use a strong ETag in an If-Match header on the PATCH request.

46
Q

Swagger

A

When consuming a web API, understanding its various methods can be challenging for a developer. Swagger, also known as OpenAPI, solves the problem of generating useful documentation and help pages for web APIs. It provides benefits such as interactive documentation, client SDK generation, and API discoverability.

In this article, the Swashbuckle.AspNetCore and NSwag .NET Swagger implementations are showcased:

Swashbuckle.AspNetCore is an open source project for generating Swagger documents for ASP.NET Core Web APIs.

NSwag is another open source project for generating Swagger documents and integrating Swagger UI or ReDoc into ASP.NET Core web APIs. Additionally, NSwag offers approaches to generate C# and TypeScript client code for your API.

47
Q

Swashbuckle and ASP.NET Core For Swagger implementation

A

Install-Package Swashbuckle.AspNetCore -Version 5.5.0

Add and configure Swagger middleware
Add the Swagger generator to the services collection in the Startup.ConfigureServices method:

C#

Copy
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext(opt =>
opt.UseInMemoryDatabase(“TodoList”));
services.AddControllers();

    // Register the Swagger generator, defining 1 or more Swagger documents
    services.AddSwaggerGen();
}
In the Startup.Configure method, enable the middleware for serving the generated JSON document and the Swagger UI:

C#

Copy
public void Configure(IApplicationBuilder app)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();

    // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
    // specifying the Swagger JSON endpoint.
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
}); }
48
Q

Swashbuckle uses MVC routing; we could add details to swagger,

A

API info and description
The configuration action passed to the AddSwaggerGen method adds information such as the author, license, and description:

In the Startup class, import the following namespace to use the OpenApiInfo class:

C#

Copy
using Microsoft.OpenApi.Models;
Using the OpenApiInfo class, modify the information displayed in the UI:

C#

Copy
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Version = "v1",
        Title = "ToDo API",
        Description = "A simple example ASP.NET Core Web API",
        TermsOfService = new Uri("https://example.com/terms"),
        Contact = new OpenApiContact
        {
            Name = "Shayne Boyer",
            Email = string.Empty,
            Url = new Uri("https://twitter.com/spboyer"),
        },
        License = new OpenApiLicense
        {
            Name = "Use under LICX",
            Url = new Uri("https://example.com/license"),
        }
    });
});
49
Q

Swagger for xml comment in the application

A

true

$(NoWarn);1591

we could suppress the warnings

    // Register the Swagger generator, defining 1 or more Swagger documents
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo
        {
            Version = "v1",
            Title = "ToDo API",
            Description = "A simple example ASP.NET Core Web API",
            TermsOfService = new Uri("https://example.com/terms"),
            Contact = new OpenApiContact
            {
                Name = "Shayne Boyer",
                Email = string.Empty,
                Url = new Uri("https://twitter.com/spboyer"),
            },
            License = new OpenApiLicense
            {
                Name = "Use under LICX",
                Url = new Uri("https://example.com/license"),
            }
        });
        // Set the comments path for the Swagger JSON and UI.
        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
        c.IncludeXmlComments(xmlPath);
    });
}
/// 
/// Deletes a specific TodoItem.
/// 
///         
[HttpDelete("{id}")]
public IActionResult Delete(long id)
{
    var todo = _context.TodoItems.Find(id);
    if (todo == null)
    {
        return NotFound();
    }
_context.TodoItems.Remove(todo);
_context.SaveChanges();
    return NoContent();
}
50
Q

swashbuckle generates the json file, which is used by the swagger UI;
The UI is driven by the generated JSON schema:

A

https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-
with-swashbuckle?view=aspnetcore-3.1&tabs=visual-studio

"delete": {
    "tags": [
        "Todo"
    ],
    "summary": "Deletes a specific TodoItem.",
    "operationId": "ApiTodoByIdDelete",
    "consumes": [],
    "produces": [],
    "parameters": [
        {
            "name": "id",
            "in": "path",
            "description": "",
            "required": true,
            "type": "integer",
            "format": "int64"
        }
    ],
    "responses": {
        "200": {
            "description": "Success"
        }
    }
}
51
Q

swashbuckle - we could have remarks, output response types, Data annotations;use static files for styling

A

https://docs.microsoft.com/en-us/aspnet/core
/tutorials/getting-started-with-swashbuckle?
view=aspnetcore-3.1&tabs=visual-studio

/// 
/// Creates a TodoItem.
/// 
/// 
/// Sample request:
///
///     POST /Todo
///     {
///        "id": 1,
///        "name": "Item1",
///        "isComplete": true
///     }
///
/// 
/// 
/// A newly created TodoItem
/// Returns the newly created item
/// If the item is null            
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces("application/json")]
public ActionResult Create(TodoItem item)
{
    _context.TodoItems.Add(item);
    _context.SaveChanges();
    return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}
----       
 [Required]
        public string Name { get; set; }
----
public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
    // Enable middleware to serve generated Swagger as a JSON endpoint.
    app.UseSwagger();
    // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
    // specifying the Swagger JSON endpoint.
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
}); } To inject additional CSS stylesheets, add them to the project's wwwroot folder and specify the relative path in the middleware options:

C#

Copy
app.UseSwaggerUI(c =>
{
     c.InjectStylesheet("/swagger-ui/custom.css");
}
52
Q

NSwag is used for code generation and swagger UI; code generation with NSwagStudio desktop app

A

https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-nswag?view=aspnetcore-3.1&tabs=visual-studio

53
Q

Handling different return types with swagger

A
[HttpGet]
        [SwaggerResponse(200,type:typeof
(List))]
        [SwaggerResponse(400,type:typeof(string))]
        [SwaggerResponse(500,type:typeof(string))]
        public async Task  ListActiveMeasures(int divisionId)
        {
            try
            {
                var data = await _measure.ListActiveMeasures(divisionId);
                var result = _mapper.Map>(data);
                if(result.Count==0)
                {
                    return BadRequest("Measures does not exist for the specified division");
                }
                return Ok(result);
            }
            catch(Exception exception)
            {
                //TODO: include logging
                return StatusCode(500, "An error occurred");
            }
        }
}

the return type should be Task, and not viewModel type

54
Q

what is CORS

A

Browser security prevents a web page from making requests to a different domain than the one that served the web page. This restriction is called the same-origin policy. The same-origin policy prevents a malicious site from reading sensitive data from another site. Sometimes, you might want to allow other sites to make cross-origin requests to your app. For more information, see the Mozilla CORS article.

Cross Origin Resource Sharing (CORS):

Is a W3C standard that allows a server to relax the same-origin policy.
Is not a security feature, CORS relaxes security. An API is not safer by allowing CORS. For more information, see How CORS works.
Allows a server to explicitly allow some cross-origin requests while rejecting others.
Is safer and more flexible than earlier techniques, such as JSONP.
View or download sample code (how to download)

55
Q

When do url have same origin

A

Two URLs have the same origin if they have identical schemes, hosts, and ports (RFC 6454).

These two URLs have the same origin:

https://example.com/foo.html
https://example.com/bar.html
These URLs have different origins than the previous two URLs:

https: //example.net: Different domain
https: //www.example.com/foo.html: Different subdomain
http: //example.com/foo.html: Different scheme
https: //example.com:9000/foo.html: Different port

56
Q

Enable CORS

A

https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1

There are three ways to enable CORS:

In middleware using a named policy or default policy.
Using endpoint routing.
With the [EnableCors] attribute.
Using the [EnableCors] attribute with a named policy provides the finest control in limiting endpoints that support CORS.

Warning

UseCors must be called before UseResponseCaching when using UseResponseCaching.

57
Q

CORS with named policy and middleware

A

CORS Middleware handles cross-origin requests. The following code applies a CORS policy to all the app’s endpoints with the specified origins:

C#

Copy
public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com");
                          });
    });
        // services.AddResponseCaching();
        services.AddControllers();
    }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app. UseHttpsRedirection();
    app. UseStaticFiles();
    app. UseRouting();

    app.UseCors(MyAllowSpecificOrigins);

    // app.UseResponseCaching();

    app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}
The preceding code:

Sets the policy name to _myAllowSpecificOrigins. The policy name is arbitrary.
Calls the UseCors extension method and specifies the _myAllowSpecificOrigins CORS policy. UseCors adds the CORS middleware. The call to UseCors must be placed after UseRouting, but before UseAuthorization. For more information, see Middleware order.
Calls AddCors with a lambda expression. The lambda takes a CorsPolicyBuilder object. Configuration options, such as WithOrigins, are described later in this article.
Enables the _myAllowSpecificOrigins CORS policy for all controller endpoints. See endpoint routing to apply a CORS policy to specific endpoints.
When using Response Caching Middleware, call UseCors before UseResponseCaching.
With endpoint routing, the CORS middleware must be configured to execute between the calls to UseRouting and UseEndpoints.

See Test CORS for instructions on testing code similar to the preceding code.

The AddCors method call adds CORS services to the app’s service container:

C#

Copy
public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com");
                          });
    });
        // services.AddResponseCaching();
        services.AddControllers();
    }
For more information, see CORS policy options in this document.

The CorsPolicyBuilder methods can be chained, as shown in the following code:

C#

Copy
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins(“http://example.com”,
“http://www.contoso.com”)
.AllowAnyHeader()
.AllowAnyMethod();
});
});

services.AddControllers(); } Note: The specified URL must not contain a trailing slash (/). If the URL terminates with /, the comparison returns false and no header is returned.
58
Q

CORS with default policy and middleware

A

The following highlighted code enables the default CORS policy:

C#

Copy
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app. UseHttpsRedirection();
    app. UseStaticFiles();
    app. UseRouting();

    app. UseCors();
    app. UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}
The preceding code applies the default CORS policy to all controller endpoints.
59
Q

405 error with http put on IIS

A

you remove the WebDAV module line and WebDAV handler in applicationHost.Config found in C:\Windows\system32\inetsrv\config

or add below lines to your application web.config file

https: //www.youtube.com/watch?v=jX6QankwsxU
https: //docs.microsoft.com/en-us/aspnet/web-api/overview/testing-and-debugging/troubleshooting-http-405-errors-after-publishing-web-api-applications

60
Q

HttpPatch implementation

A

https: //www.infoworld.com/article/3206264/how-to-perform-partial-updates-to-rest-web-api-resources.html
https: //gavilan.blog/2019/04/05/asp-net-core-2-2-partial-updates-with-http-patch-json-patch/
https: //www.roundthecode.com/dotnet/asp-net-core-web-api/asp-net-core-api-how-to-perform-partial-update-using-http-patch
https: //dotnetcoretutorials.com/2017/11/29/json-patch-asp-net-core/

61
Q

Json patch data for HttpPatch

A

https: //docs.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-5.0
http: //jsonpatch.com/

62
Q

httpPatch - Not idempotent

A

https://stackoverflow.com/questions/28459418/use-of-put-vs-patch-methods-in-rest-api-real-life-scenarios

63
Q

swagger - describing request body

A

https: //swagger.io/docs/specification/describing-request-body/
https: //docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio