Web API

What is .net Web API?
.Net’s Web API is an easy way to implement a RESTful web service using all of the goodness that the .net framework provides. Once you understand the basic principles of REST, then a .net Web API will be very easy to implement.

What is REST?
REST stands for ‘Representational State Transfer’ and it is an architectural pattern for creating an API that uses HTTP as its underlying communication method.

HTTP is a request and response system; a calling client sends a request to an endpoint and the endpoint responds. The client and endpoint can be anything but a typical example is a browser accessing a web server or an app accessing and API.

There are several key implementation details with HTTP that you should be aware of:

Resources – REST uses addressable resources to define the structure of the API. These are the URLs you use to get to pages on the web, for example ‘http://www.microsoft.com/Surface-Pro-3’ is a resource

Request Verbs – These describe what you want to do with the resource. A browser typically issues a GET verb to instruct the endpoint it wants to get data, however there are many other verbs available including things like POST, PUT and DELETE.

Request Headers – These are additional instructions that are sent with the request. These might define what type of response is required or authorisation details.

Request Body – Data that is sent with the request. For example a POST (creation of a new item) will required some data which is typically sent as the request body in the format of JSON or XML.

Response Body – This is the main body of the response. If the request was to a web server, this might be a full HTML page, if it was to an API, this might be a JSON or XML document.

Response Status codes – These codes are issues with the response and give the client details on the status of the request.

Scaffolding

ASP.NET Scaffolding is a code generation framework for ASP.NET Web applications. Visual Studio 2013 includes pre-installed code generators for MVC and Web API projects. You add scaffolding to your project when you want to quickly add code that interacts with data models. Using scaffolding can reduce the amount of time to develop standard data operations in your project.+

By default, Visual Studio 2013 does not support generating code for a Web Forms project, but you can use scaffolding with Web Forms by either adding MVC dependencies to the project or installing an extension. Both approaches are shown below.

HTTP Methods
Whenever a client submits a request to a server, part of that request is an HTTP method, which is what the client would like the server to do with the specified resource. HTTP methods represent those requested actions. For example, some commonly-used HTTP methods will retrieve data from a server, submit data to a server for processing, delete an item from the server's data store, etc. For a more general overview of HTTP,

POST Create
GET Read
PUT Update/Replace
PATCH Update/Modify
DELETE Delete

There are quite a few testing and debugging oriented tools out there for Web API :

Fiddler - This is hands-down one of the most useful tools you will come across for any type of web request debugging and it is incredibly useful for Web API.
Hurl - Hurl is another excellent tool that can help both developers and consumer test out available APIs to ensure they are working properly.
HttpMaster - As previously mentioned, another more in-depth debugging for both handling REST-based Services.
Postman chrome extension


Media Formatters in ASP.NET Web API 2

Internet Media Types
A media type, also called a MIME type, identifies the format of a piece of data. In HTTP, media types describe the format of the message body. A media type consists of two strings, a type and a subtype. For example:

text/html
image/png
application/json

When the client sends a request message, it can include an Accept header. The Accept header tells the server which media type(s) the client wants from the server. For example:

Accept: text/html,application/xhtml+xml,application/xml

The media type determines how Web API serializes and deserializes the HTTP message body. Web API has built-in support for XML, JSON, BSON, and form-urlencoded data, and you can support additional media types by writing a media formatter.

To create a media formatter, derive from one of these classes:

MediaTypeFormatter. This class uses asynchronous read and write methods.
BufferedMediaTypeFormatter. This class derives from MediaTypeFormatter but uses sychronous read/write methods.

Deriving from BufferedMediaTypeFormatter is simpler, because there is no asynchronous code, but it also means the calling thread can block during I/O.

Attribute Routing 


Routing is how Web API matches a URI to an action. Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily create URIs that describe hierarchies of resources.

With attribute routing, it's trivial to define a route for this URI. You simply add an attribute to the controller action:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }


Enabling Attribute Routing

To enable attribute routing, call MapHttpAttributeRoutes during configuration. This extension method is defined in the System.Web.Http.HttpConfigurationExtensions class.


using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}


public class OrdersController : ApiController
{
    [Route("customers/{customerId}/orders")]
    [HttpGet]
    public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}


The string "customers/{customerId}/orders" is the URI template for the route. Web API tries to match the request URI to the template. In this example, "customers" and "orders" are literal segments, and "{customerId}" is a variable parameter. The following URIs would match this template:

http://localhost/customers/1/orders
http://localhost/customers/bob/orders
http://localhost/customers/1234-5678/orders


Route Prefixes

Often, the routes in a controller all start with the same prefix. For example:


[RoutePrefix("orders")]
public class OrdersController : ApiController
{
    [Route("{id:int}")] // constrained parameter
    public HttpResponseMessage Get(int id) { ... }

    [Route("details")]  // literal
    public HttpResponseMessage GetDetails() { ... }

    [Route("pending", RouteOrder = 1)]
    public HttpResponseMessage GetPending() { ... }

    [Route("{customerName}")]  // unconstrained parameter
    public HttpResponseMessage GetByCustomer(string customerName) { ... }

    [Route("{*date:datetime}")]  // wildcard
    public HttpResponseMessage Get(DateTime date) { ... }
}

These routes are ordered as follows.

orders/details
orders/{id}
orders/{customerName}
orders/{*date}
orders/pending


MediaTypeMapping Class

An abstract base class used to create an association between HttpRequestMessage or HttpResponseMessage instances that have certain characteristics and a specific MediaTypeHeaderValue.

Intro to MediaTypeMappings

By default, Accept and Request Content-Type headers play role on deciding which format you serve. One other way of involving a formatter to process your request is MediaTypeMapping.

MediaTypeMapping provides a way for us to participate the Conneg algorithm decision making process and decide if we would like the formatter to take part in writing the response. There are several built in MediaTypeMappings (actually 4) supported out of the box. These are QueryStringMapping, RequestHeaderMapping, UriPathExtensionMapping, MediaRangeMapping. All these classes are derived from MediaTypeMapping abstract class (yes, creating a custom one is tedious and I plan on writing a post on that as well). We have these mappings and the other great stuff is that all default formatters has a hook up point in order to register mappings

protected void Application_Start(object sender, EventArgs e) {

    GlobalConfiguration.Configuration.Routes.MapHttpRoute(
        "defaultHttpRoute",
        routeTemplate: "api/{controller}"
    );

    GlobalConfiguration.Configuration.Formatters.JsonFormatter.
        MediaTypeMappings.Add(
            new QueryStringMapping(
                "format", "json", "application/json"
        )
    );

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.
        MediaTypeMappings.Add(
            new QueryStringMapping(
                "format", "xml", "application/xml"
        )
    );
}


Model Validation in ASP.NET Web API

When a client sends data to your web API, often you want to validate the data before doing any processing. This article shows how to annotate your models, use the annotations for data validation, and handle validation errors in your web API.

Data Annotations

using System.ComponentModel.DataAnnotations;

namespace MyApi.Models
{
    public class Product
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        public decimal Price { get; set; }
        [Range(0, 999)]
        public double Weight { get; set; }
    }
}

You can see that the client did not include the Name property, which is marked as required. When Web API converts the JSON into a Product instance, it validates the Product against the validation attributes. In your controller action, you can check whether the model is valid:

using MyApi.Models;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MyApi.Controllers
{
    public class ProductsController : ApiController
    {
        public HttpResponseMessage Post(Product product)
        {
            if (ModelState.IsValid)
            {
                // Do something with the product (not shown).

                return new HttpResponseMessage(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }
    }
}


Handling Validation Errors

Web API does not automatically return an error to the client when validation fails. It is up to the controller action to check the model state and respond appropriately.

You can also create an action filter to check the model state before the controller action is invoked. The following code shows an example:

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;

namespace MyApi.Filters
{
    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }
}

To apply this filter to all Web API controllers, add an instance of the filter to the HttpConfiguration.Filters collection during configuration:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ValidateModelAttribute());

// ...
}
}

Authentication and Authorization in ASP.NET Web API

Authentication is knowing the identity of the user. For example, Alice logs in with her username and password, and the server uses the password to authenticate Alice.
Authorization is deciding whether a user is allowed to perform an action. For example, Alice has permission to get a resource but not create a resource.

Setting the Principal

If your application performs any custom authentication logic, you must set the principal on two places:

Thread.CurrentPrincipal. This property is the standard way to set the thread's principal in .NET.
HttpContext.Current.User. This property is specific to ASP.NET.
The following code shows how to set the principal:

Copy
C#
private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Controller: To restrict access for a specific controller, add the filter as an attribute to the controller

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

Alternatively, you can restrict the controller and then allow anonymous access to specific actions, by using the [AllowAnonymous] attribute. In the following example, the Post method is restricted, but the Get method allows anonymous access.

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
 
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

Authorization Inside a Controller Action

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}

n Visual Studio 2013, the Web API project template gives you three options for authentication:

Individual accounts.               The app uses a membership database.
Organizational accounts.         Users sign in with their Azure Active Directory, Office 365, or on-premise Active Directory credentials.
Windows authentication.          This option is intended for Intranet applications, and uses the Windows Authentication IIS module.

Individual accounts provide two ways for a user to log in:

Local login. The user registers at the site, entering a username and password. The app stores the password hash in the membership database. When the user logs in, the ASP.NET Identity system verifies the password.
Social login. The user signs in with an external service, such as Facebook, Microsoft, or Google. The app still creates an entry for the user in the membership database, but does not store any credentials. The user authenticates by signing into the external service.

Get an Access Token

Here is the JavaScript code that sends the AJAX request:

Copy
JavaScript
var loginData = {
    grant_type: 'password',
    username: self.loginEmail(),
    password: self.loginPassword()
};

$.ajax({
    type: 'POST',
    url: '/Token',
    data: loginData
}).done(function (data) {
    self.user(data.userName);
    // Cache the access token in session storage.
    sessionStorage.setItem(tokenKey, data.access_token);
}).fail(showError);

Using External Authentication Services

Visual Studio 2013 and ASP.NET 4.5.1 make integration with external authentication services easier for developers by providing built-in integration for the following authentication services:+

Facebook
Google
Microsoft Accounts (Windows Live ID accounts)
Twitter

Enabling Facebook Authentication
Using Facebook authentication requires you to create a Facebook developer account, and your project will require an application ID and secret key from Facebook in order to function. For information about creating a Facebook developer account and obtaining your application ID and secret key, see https://go.microsoft.com/fwlink/?LinkID=252166.

Preventing Cross-Site Request Forgery (CSRF) Attacks
Here is an example of a CSRF attack:

A user logs into www.example.com, using forms authentication.
The server authenticates the user. The response from the server includes an authentication cookie.
Without logging out, the user visits a malicious web site. This malicious site contains the following HTML form:

Copy
html
<h1>You Are a Winner!</h1>
  <form action="http://example.com/api/account" method="post">
    <input type="hidden" name="Transaction" value="withdraw" />
    <input type="hidden" name="Amount" value="1000000" />
  <input type="submit" value="Click Me"/>
</form>
Notice that the form action posts to the vulnerable site, not to the malicious site. This is the "cross-site" part of CSRF.

The user clicks the submit button. The browser includes the authentication cookie with the request.
The request runs on the server with the user's authentication context, and can do anything that an authenticated user is allowed to do.


Anti-Forgery Tokens
To help prevent CSRF attacks, ASP.NET MVC uses anti-forgery tokens, also called request verification tokens.

The client requests an HTML page that contains a form.
The server includes two tokens in the response. One token is sent as a cookie. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values.
When the client submits the form, it must send both tokens back to the server. The client sends the cookie token as a cookie, and it sends the form token inside the form data. (A browser client automatically does this when the user submits the form.)
If a request does not include both tokens, the server disallows the request.

<form action="/Home/Test" method="post">
    <input name="__RequestVerificationToken" type="hidden"
           value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />  
    <input type="submit" value="Submit" />
</form>

Anti-Forgery Tokens in ASP.NET MVC
To add the anti-forgery tokens to a Razor page, use the HtmlHelper.AntiForgeryToken helper method:

Copy
cshtml
@using (Html.BeginForm("Manage", "Account")) {
    @Html.AntiForgeryToken()
}


Anti-CSRF and AJAX

The form token can be a problem for AJAX requests, because an AJAX request might send JSON data, not HTML form data. One solution is to send the tokens in a custom HTTP header. The following code uses Razor syntax to generate the tokens, and then adds the tokens to an AJAX request. The tokens are generated at the server by calling AntiForgery.GetTokens.

Copy
html
<script>
    @functions{
        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;              
        }
    }

    $.ajax("api/values", {
        type: "post",
        contentType: "application/json",
        data: {  }, // JSON data goes here
        dataType: "json",
        headers: {
            'RequestVerificationToken': '@TokenHeaderValue()'
        }
    });
</script>

Enabling Cross-Origin Requests
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;

namespace WebService.Controllers
{
    [EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
    public class TestController : ApiController
    {
        // Controller methods not shown...
    }
}

For the origins parameter, use the URI where you deployed the WebClient application. This allows cross-origin requests from WebClient, while still disallowing all other cross-domain requests. Later, I'll describe the parameters for [EnableCors] in more detail.


Authentication Filter

o apply the filter to one action, decorate the action with the filter. The following code sets the [IdentityBasicAuthentication] filter on the controller's Post method.

Copy
C#
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
    public IHttpActionResult Get() { . . . }

    [IdentityBasicAuthentication] // Enable Basic authentication for this action.
    public IHttpActionResult Post() { . . . }
}
To apply the filter to all Web API controllers, add it to GlobalConfiguration.Filters.

Copy
C#
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new IdentityBasicAuthenticationAttribute());

        // Other configuration code not shown...
    }
}

Integrated Windows Authentication

Integrated Windows authentication enables users to log in with their Windows credentials, using Kerberos or NTLM. The client sends credentials in the Authorization header


Enabling SSL on the Server

Create or get a certificate. For testing, you can create a self-signed certificate.
Add an HTTPS binding.

Parameter Binding

Using [FromUri]
public class GeoPoint
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

public ValuesController : ApiController
{
    public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
The client can put the Latitude and Longitude values in the query string and Web API will use them to construct a GeoPoint. For example:

http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989


Using [FromBody]
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the value of name from the request body. Here is an example client request.

Copy
console
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"

When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object)


At most one parameter is allowed to read from the message body. So this will not work:

Copy
C#
// Caution: Will not work!  
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }

Type Converters

You can make Web API treat a class as a simple type (so that Web API will try to bind it from the URI) by creating a TypeConverter and providing a string conversion.

The following code shows a GeoPoint class that represents a geographical point, plus a TypeConverter that converts from strings to GeoPoint instances. The GeoPoint class is decorated with a [TypeConverter] attribute to specify the type converter. (This example was inspired by Mike

[TypeConverter(typeof(GeoPointConverter))]
public class GeoPoint
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public static bool TryParse(string s, out GeoPoint result)
    {
        result = null;

        var parts = s.Split(',');
        if (parts.Length != 2)
        {
            return false;
        }

        double latitude, longitude;
        if (double.TryParse(parts[0], out latitude) &&
            double.TryParse(parts[1], out longitude))
        {
            result = new GeoPoint() { Longitude = longitude, Latitude = latitude };
            return true;
        }
        return false;
    }
}

class GeoPointConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context,
        CultureInfo culture, object value)
    {
        if (value is string)
        {
            GeoPoint point;
            if (GeoPoint.TryParse((string)value, out point))
            {
                return point;
            }
        }
        return base.ConvertFrom(context, culture, value);
    }
}

Now Web API will treat GeoPoint as a simple type, meaning it will try to bind GeoPoint parameters from the URI. You don't need to include [FromUri] on the parameter.

public HttpResponseMessage Get(GeoPoint location) { ... }
http://localhost/api/values/?location=47.678558,-122.130989

Model Binders

A more flexible option than a type converter is to create a custom model binder. With a model binder, you have access to things like the HTTP request, the action description, and the raw values from the route data

To create a model binder, implement the IModelBinder interface. This interface defines a single method, BindModel:

bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext);

Here is a model binder for GeoPoint objects.

Copy
C#
public class GeoPointModelBinder : IModelBinder
{
    // List of known locations.
    private static ConcurrentDictionary<string, GeoPoint> _locations
        = new ConcurrentDictionary<string, GeoPoint>(StringComparer.OrdinalIgnoreCase);

    static GeoPointModelBinder()
    {
        _locations["redmond"] = new GeoPoint() { Latitude = 47.67856, Longitude = -122.131 };
        _locations["paris"] = new GeoPoint() { Latitude = 48.856930, Longitude = 2.3412 };
        _locations["tokyo"] = new GeoPoint() { Latitude = 35.683208, Longitude = 139.80894 };
    }

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(GeoPoint))
        {
            return false;
        }

        ValueProviderResult val = bindingContext.ValueProvider.GetValue(
            bindingContext.ModelName);
        if (val == null)
        {
            return false;
        }

        string key = val.RawValue as string;
        if (key == null)
        {
            bindingContext.ModelState.AddModelError(
                bindingContext.ModelName, "Wrong value type");
            return false;
        }

        GeoPoint result;
        if (_locations.TryGetValue(key, out result) || GeoPoint.TryParse(key, out result))
        {
            bindingContext.Model = result;
            return true;
        }

        bindingContext.ModelState.AddModelError(
            bindingContext.ModelName, "Cannot convert value to Location");
        return false;
    }
}

A model binder gets raw input values from a value provider. This design separates two distinct functions:

The value provider takes the HTTP request and populates a dictionary of key-value pairs.
The model binder uses this dictionary to populate the model.

The default value provider in Web API gets values from the route data and the query string. For example, if the URI is http://localhost/api/values/1?location=48,-122, the value provider creates the following key-value pairs:

id = "1"
location = "48,122"
(I'm assuming the default route template, which is "api/{controller}/{id}".)

The name of the parameter to bind is stored in the ModelBindingContext.ModelName property. The model binder looks for a key with this value in the dictionary. If the value exists and can be converted into a GeoPoint, the model binder assigns the bound value to the ModelBindingContext.Model property.

Setting the Model Binder

There are several ways to set a model binder. First, you can add a [ModelBinder] attribute to the parameter.

Copy
C#
public HttpResponseMessage Get([ModelBinder(typeof(GeoPointModelBinder))] GeoPoint location)

You can also add a [ModelBinder] attribute to the type. Web API will use the specified model binder for all parameters of that type.

Copy
C#
[ModelBinder(typeof(GeoPointModelBinder))]
public class GeoPoint
{
    // ....
}

Finally, you can add a model-binder provider to the HttpConfiguration. A model-binder provider is simply a factory class that creates a model binder. You can create a provider by deriving from the ModelBinderProvider class. However, if your model binder handles a single type, it's easier to use the built-in SimpleModelBinderProvider, which is designed for this purpose. The following code shows how to do this.

Copy
C#
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var provider = new SimpleModelBinderProvider(
            typeof(GeoPoint), new GeoPointModelBinder());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

        // ...
    }
}
With a model-binding provider, you still need to add the [ModelBinder] attribute to the parameter, to tell Web API that it should use a model binder and not a media-type formatter. But now you don't need to specify the type of model binder in the attribute:+

Copy
C#
public HttpResponseMessage Get([ModelBinder] GeoPoint location) { ... }

Value Providers

I mentioned that a model binder gets values from a value provider. To write a custom value provider, implement the IValueProvider interface. Here is an example that pulls values from the cookies in the request


HttpParameterBinding

Model binders are a specific instance of a more general mechanism. If you look at the [ModelBinder] attribute, you will see that it derives from the abstract ParameterBindingAttribute class. This class defines a single method, GetBinding, which returns an HttpParameterBinding object:


IActionValueBinder

The entire parameter-binding process is controlled by a pluggable service, IActionValueBinder. The default implementation of IActionValueBinder does the following:

Look for a ParameterBindingAttribute on the parameter. This includes [FromBody], [FromUri], and [ModelBinder], or custom attributes.
Otherwise, look in HttpConfiguration.ParameterBindingRules for a function that returns a non-null HttpParameterBinding.
Otherwise, use the default rules that I described previously.

If the parameter type is "simple"or has a type converter, bind from the URI. This is equivalent to putting the [FromUri] attribute on the parameter.
Otherwise, try to read the parameter from the message body. This is equivalent to putting [FromBody] on the parameter

How to version your Web API


Take advantage of the various ways to version your Web API to make it more flexible and adaptable to changes while keeping the functionality intact

You can version your Web API in one of the following ways:

Use URLs: Version information is specified in the URL as a query string.
Use Custom Request Headers: Version information for your controller is specified in the request header sans the need of any change in the URL.
Use Accept Headers: Accept headers generally define the media type and character encodings. You can also pass version information for your Web API via accept headers without having to change the URL.


Json serilization and Jtoken

string json =
@"{
    ""ADDRESS_MAP"":{

        ""ADDRESS_LOCATION"":{
            ""type"":""separator"",
            ""name"":""Address"",
            ""value"":"""",
            ""FieldID"":40
        },
        ""LOCATION"":{
            ""type"":""locations"",
            ""name"":""Location"",
            ""keyword"":{
                ""1"":""LOCATION1""
            },
            ""value"":{
                ""1"":""United States""
            },
            ""FieldID"":41
        },
        ""FLOOR_NUMBER"":{
            ""type"":""number"",
            ""name"":""Floor Number"",
            ""value"":""0"",
            ""FieldID"":55
        },
        ""self"":{
            ""id"":""2"",
            ""name"":""Address Map""
        }
    }
}";

JToken outer = JToken.Parse(json);
JObject inner = outer["ADDRESS_MAP"].Value<JObject>();

List<string> keys = inner.Properties().Select(p => p.Name).ToList();

foreach (string k in keys)
{
    Console.WriteLine(k);
}

Output:

ADDRESS_LOCATION
LOCATION
FLOOR_NUMBER
self



No comments:

Post a Comment