web APIs with ASP.NET Core Flashcards
Web api Controllers
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.
Controller class vs ControllerBase
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.
ControllerBase.CreatedAtAction returns a 201 status code:
[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); }
examples of methods that ControllerBase provides
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
Content Negotiation
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.
Produces Attribute
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/
Consumes Attribute
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.
Attributes in Web api
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
ApiController attribute
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
Attribute on multiple controllers
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
Attribute on an assembly
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 { ... } }
Conventional routing - Does not work in asp.net core web api method accessing
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.
MvcApplicationBuilderExtensions.UseMvc Method
UseMvc(IApplicationBuilder)-This method only supports attribute routing. To add conventional routes use UseMvc(IApplicationBuilder, Action).
Routing
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.
MvcApplicationBuilderExtensions.UseMvcWithDefaultRoute(IApplicationBuilder) Method
Adds MVC to the IApplicationBuilder request execution pipeline with a default route named ‘default’ and the following template: ‘{controller=Home}/{action=Index}/{id?}’.
Attribute routing with Http[Verb] attributes
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.
Why Routing?
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.
- Creating Default Routes
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.)
- Extending Default Routes - for creating routing
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.
- Using Attributes - for creating routing
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.
- Building RESTful Routes
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
- Using Constraints on Routing
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:
Path parameters in swagger are always needed
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
Automatic HTTP 400 responses
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.
Default BadRequest response
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”;
});