Sunday, February 3, 2019

Web API

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.

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 Get() 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" };
        }

use below line for solution:
[Route("api/students/{id}/courses")]
public IEnumerable<string> GetStudentCourses(int id)

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. 

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 


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
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'
[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

Asp.net Core Continued

65 66 67 68 69 70 71 65 ASP.NET Core Identity It is a membership system Step 1 : Inherit from IdentityDbContext class instead of ...