1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
5. ASP.NET Web API Content Negotiation
REST says that the client should have the ability to decide in which format they want the response - XML, JSON etc. A request that is sent to the server includes an Accept header. Using the Accept header the client can specify the format for the response. For example
Accept: application/xml returns XML
Accept: application/json returns JSON
Depending on the Accept header value in the request, the server sends the response. This is called Content Negotiation.
So what does the Web API do when we request for data in a specific format
The Web API controller generates the data that we want to send to the client. The controller generates the data, and hands the data to the Web API pipeline which then looks at the Accept header and depending on the format that the client has requested, Web API will choose the appropriate formatter. For example, if the client has requested for XML data, Web API uses XML formatter. If the client has requested for JSON data, Web API uses JSON formatter. These formatters are called Media type formatters.
ASP.NET Web API is greatly extensible. This means we can also plugin our own formatters, for custom formatting the data.
If you don't specify the Accept header, by default the Web API returns JSON data.
When the response is being sent to the client in the requested format, notice that the Content-Type header of the response is set to the appropriate value. For example, if the client has requested application/xml, the server send the data in XML format and also sets the Content-Type=application/xml.
The formatters are used by the server for both request and response messages. When the client sends a request to the server, we set the Content-Type header to the appropriate value to let the server know the format of the data that we are sending. For example, if the client is sending JSON data, the Content-Type header is set to application/json. The server knows it is dealing with JSON data, so it uses JSON formatter to convert JSON data to .NET Type. Similarly when a response is being sent from the server to the client, depending on the Accept header value, the appropriate formatter is used to convert .NET type to JSON, XML etc.
It's also very easy to change the serialization settings of these formatters. For example, if you want the JSON data to be properly indented and use camel case instead of pascal case for property names, all you have to do is modify the serialization settings of JSON formatters as shown below. With our example this code goes in WebApiConfig.cs file in App_Start folder.
6
ASP.NET Web API MediaTypeFormatter
10. Custom method names in ASP.NET Web API
12. FromBody and FromUri in Web API
Now let us understand the default convention used by Web API for binding parameters.
30 Attribute routing in ASP.NET Web API
Why do we need attribute based routing? If in an API, we have 2 get methods accepting an input int parameter, GET on that controller will result in error since api would not know what method to call. This can be avoided using attribute based routing.
use below line for solution:
What is Attribute Routing
Using the [Route] attribute to define routes is called Attribute Routing
What are the advantages of using Attribute Routing
Attribute routing gives us more control over the URIs than convention-based routing. Creating URI patterns like hierarchies of resources (For example, students have courses, Departments have employees) is very difficult with convention-based routing.
31 Route Prefix attribute in Web API
32. Web API attribute routing constraints
5. ASP.NET Web API Content Negotiation
REST says that the client should have the ability to decide in which format they want the response - XML, JSON etc. A request that is sent to the server includes an Accept header. Using the Accept header the client can specify the format for the response. For example
Accept: application/xml returns XML
Accept: application/json returns JSON
Depending on the Accept header value in the request, the server sends the response. This is called Content Negotiation.
So what does the Web API do when we request for data in a specific format
The Web API controller generates the data that we want to send to the client. The controller generates the data, and hands the data to the Web API pipeline which then looks at the Accept header and depending on the format that the client has requested, Web API will choose the appropriate formatter. For example, if the client has requested for XML data, Web API uses XML formatter. If the client has requested for JSON data, Web API uses JSON formatter. These formatters are called Media type formatters.
ASP.NET Web API is greatly extensible. This means we can also plugin our own formatters, for custom formatting the data.
If you don't specify the Accept header, by default the Web API returns JSON data.
When the response is being sent to the client in the requested format, notice that the Content-Type header of the response is set to the appropriate value. For example, if the client has requested application/xml, the server send the data in XML format and also sets the Content-Type=application/xml.
The formatters are used by the server for both request and response messages. When the client sends a request to the server, we set the Content-Type header to the appropriate value to let the server know the format of the data that we are sending. For example, if the client is sending JSON data, the Content-Type header is set to application/json. The server knows it is dealing with JSON data, so it uses JSON formatter to convert JSON data to .NET Type. Similarly when a response is being sent from the server to the client, depending on the Accept header value, the appropriate formatter is used to convert .NET type to JSON, XML etc.
It's also very easy to change the serialization settings of these formatters. For example, if you want the JSON data to be properly indented and use camel case instead of pascal case for property names, all you have to do is modify the serialization settings of JSON formatters as shown below. With our example this code goes in WebApiConfig.cs file in App_Start folder.
config.Formatters.JsonFormatter.SerializerSettings.Formatting =
Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
6
ASP.NET Web API MediaTypeFormatter
10. Custom method names in ASP.NET Web API
By default, the HTTP verb GET is mapped to a
method in a controller that has the name Ge t() or starts with the word Get. In the following EmployeesController, the method is named Get() so by convention this is mapped to the HTTP verb GET. Even if you rename it to GetEmployees() or GetSomething() it will still be mapped to the HTTP verb GET as long as the name of the method is prefixed with the word Get. The word Get is case-insensitive. It can be lowercase, uppercase or a mix of both.
To instruct Web API to map HTTP verb GET to
LoadEmployees() method, decorate the method with [HttpGet] attribute.
[HttpGet]
public IEnumerable<Employee>
LoadEmployees()
{
using (EmployeeDBEntities entities
= new EmployeeDBEntities())
{
return entities.Employees.ToList();
}
}
|
12. FromBody and FromUri in Web API
When a GET/PUT request is issued, Web API maps the
data in the request to the GET/PUT method parameters in the EmployeesController.
This process is called Parameter Binding.
Now let us understand the default convention used by Web API for binding parameters.
1.
If the parameter is a simple type like int, bool, double, etc., Web API
tries to get the value from the URI (Either from route data or Query String)
2.
If the parameter is a complex type like Customer, Employee etc., Web API
tries to get the value from the request body
So in our case, the id parameter is a simple type,
so Web API tries to get the value from the request URI. The employee parameter
is a complex type, so Web API gets the value from the request body.
We can change this default parameter
binding process by using [FromBody] and [FromUri]
attributes. Notice in the example below
public HttpResponseMessage Put([FromBody]int id,
[FromUri]Employee employee)
{
try
{
//rest code remains same
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest,
ex);
}
}
|
30 Attribute routing in ASP.NET Web API
Why do we need attribute based routing? If in an API, we have 2 get methods accepting an input int parameter, GET on that controller will result in error since api would not know what method to call. This can be avoided using attribute based routing.
public Student Get(int id)//api
gets confused since both methods start with ‘get’
{
return students.FirstOrDefault(s
=> s.Id == id);
}
public IEnumerable<string>
GetStudentCourses(int id)
{
if (id
== 1)
return new List<string>()
{ "C#", "ASP.NET", "SQL
Server" };
else if (id
== 2)
return new List<string>()
{ "ASP.NET Web API", "C#", "SQL
Server" };
else
return new List<string>()
{ "Bootstrap", "jQuery", "AngularJs" };
}
|
[Route("api/students/{id}/courses")]
public IEnumerable<string>
GetStudentCourses(int id)
Using the [Route] attribute to define routes is called Attribute Routing
What are the advantages of using Attribute Routing
Attribute routing gives us more control over the URIs than convention-based routing. Creating URI patterns like hierarchies of resources (For example, students have courses, Departments have employees) is very difficult with convention-based routing.
How to enable attribute routing(enabled by default in web api 2)
config.MapHttpAttributeRoutes();
we use both Attribute Routing and Convention-based routing in a single Web API project
31 Route Prefix attribute in Web API
The common prefix "api/students" can be specified for the entire controller using the [RoutePrefix] attribute as shown below. This eliminates the
need to repeat the common prefix "api/students" on every controller action method.
Before Route Prefix
|
After Route Prefix
|
public class StudentsController : ApiController
{
[Route("api/students")]
public IEnumerable<Student>
Get()
[Route("api/students/{id}")]
public Student Get(int id)
[Route("api/students/{id}/courses")]
public IEnumerable<string>
GetStudentCourses(int id)
}
|
[RoutePrefix("api/students")]
public class StudentsController : ApiController
{
[Route("")]
public IEnumerable<Student>
Get()
[Route("{id}")]
public Student Get(int id)
[Route("{id}/courses")]
public IEnumerable<string>
GetStudentCourses(int id)
}
|
However,
sometimes you may want to override the route prefix. If we
add this in student controller:
[Route("api/teachers")]
public IEnumerable<Teacher>
GetTeachers()
{
List<Teacher>
teachers = new List<Teacher>()
{
new Teacher()
{ Id = 1, Name = "Rob" },
new Teacher()
{ Id = 2, Name = "Mike" },
new Teacher()
{ Id = 3, Name = "Mary" }
};
return teachers;
}
|
We can
reach GetTeachers only by ’/api/students/api/teachers’, but we want to reach
there by /api/teachers’. This is because it is using the route prefix
/api/students. We can override the setting by using [Route("~/api/teachers")]
What is the use of RoutePrefix attribute
RoutePrefix attribute is used to specify the common route prefix at the controller level to eliminate the need to repeat that common route prefix on every controller action method
How to override the route prefix
Use ~ character to override the route prefix
RoutePrefix attribute is used to specify the common route prefix at the controller level to eliminate the need to repeat that common route prefix on every controller action method
How to override the route prefix
Use ~ character to override the route prefix
32. Web API attribute routing constraints
If we
have something like this:
[Route("{id}")]
public Student Get(int id)
{
return students.FirstOrDefault(s
=> s.Id == id);
}
[Route("{name}")]
public Student Get(string name)
{
return students.FirstOrDefault(s
=> s.Name.ToLower() == name.ToLower());
}
|
if you
navigate to either of the following URI's you get an error stating "Multiple
actions were found that match the request"
/api/students/1
/api/students/Sam
/api/students/1
/api/students/Sam
What we
want is :
-> an integer is specified in the URI (/api/students/1), then we want the Get(int id) method that has
integer parameter invoked
-->If a string is specified in the URI (/api/students/Sam), then we want the
Get(string name) method that
has string parameter invoked
[Route("{id:int}")]
public Student Get(int id)
{
return students.FirstOrDefault(s
=> s.Id == id);
}
[Route("{name:alpha}")]
public Student Get(string name)
{
return students.FirstOrDefault(s
=> s.Name.ToLower() == name.ToLower());
}
|
Eg2:
[Route("{id:int:min(1)}")]
public Student Get(int id)
{
return students.FirstOrDefault(s
=> s.Id == id);
}
|
With
the above change, if you specify a positive number like 1 in the URI, then it
will be mapped to Get(int id) method as expected
/api/students/1
However, if you specify 0 or a negative number less than ZERO, you will get an error. For example if you specify 0 as the value for id in the URI, you will get
No HTTP resource was found that matches the request URI 'http://localhost:65116/api/students/0'
/api/students/1
However, if you specify 0 or a negative number less than ZERO, you will get an error. For example if you specify 0 as the value for id in the URI, you will get
No HTTP resource was found that matches the request URI 'http://localhost:65116/api/students/0'
[Route("{id:int:min(1):max(3)}")]
public Student Get(int id)
{
return students.FirstOrDefault(s
=> s.Id == id);
} //1 and 3 are inclusive
|
[Route("{id:int:range(1,3)}")]
public Student Get(int id)
{
return students.FirstOrDefault(s
=> s.Id == id);
}
|
No comments:
Post a Comment