I have the need to create a REST webservice. For that I followed this tutorial: http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/
Everything worked fine, I also added BasicAuth to it, it works like a glove.
Now, my question... This webservice will work with possible versions of it, so we decided to implement a sort of versions systems. Also, we want the client applications to choose the database that they want to perform their actions. For that, we thought that it would be nice to have URIs with this style:
http://localhost/Connection/northwind/API/1/DataRow
This is the code that I have. I used to have only the entity DataRow defined. Now I have also defined the entity API and Connection.
How do I implement a URI/endpoint like what I want? This is the code that I have, so far.
File: WebApiConfig.cs
using Integration.Models;
using Microsoft.OData.Edm;
using System.Web.Http;
using System.Web.OData.Batch;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using Integration.Controllers;
namespace Integration
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnsureInitialized();
}
private static IEdmModel GetEdmModel()
{
//GlobalConfiguration.Configuration.Filters.Add(new BasicAuthenticationFilter()); // basicAutenthentication
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "Integration";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<DataRow>("DataRow");
builder.EntitySet<Connection>("Connection");
builder.EntitySet<API>("API");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
}
Controllers\DataRows.cs
using Integration.DataSource;
using System.Linq;
using System.Web.Http;
using System.Web.OData;
using System.Net;
namespace Integration.Controllers
{
[EnableQuery]
public class DataRowController : ODataController
{
[BasicAuthenticationFilter]
public IHttpActionResult Get()
{
return Content(HttpStatusCode.NoContent,"NoContent");
}
[BasicAuthenticationFilter]
public IHttpActionResult Post(Models.DataRow row)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//do stuff to save data
// ..
return Content(HttpStatusCode.Created, "OK");
}
}
}
Controllers\Connections.cs
using Integration.DataSource;
using System.Linq;
using System.Web.Http;
using System.Web.OData;
using System.Net;
namespace Integration.Controllers
{
[EnableQuery]
public class ConnectionController : ODataController
{
[BasicAuthenticationFilter]
public IHttpActionResult Get()
{
return Ok(IntegrationDataSources.Instance.Connection.AsQueryable());
}
[BasicAuthenticationFilter]
public IHttpActionResult Post(Models.Connection connection)
{
return Content(HttpStatusCode.NotImplemented, "NotImplemented");
}
}
}
Models\DataRow.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Integration.Models
{
public class DataRow
{
[Key]
public int ID { get; set; }
[Required]
public int Type { get; set; }
[Required]
public string DataType { get; set; }
[Required]
public string Data { get; set; }
[Required]
public int APIVersion { get; set; }
[Required]
public string IntegrationProvider { get; set; }
}
public class Connection
{
[Key]
public string ConnectionName { get; set; }
public API Api { get; set; }
}
public class API
{
[Key]
public int Version { get; set; }
public DataRow row { get; set; }
}
}
You can config API versioning with Global Route Prefixes with Attribute Routing... You can create a class that inherits from DefaultDirectRouteProvider
like one from below
public class CentralizedPrefixProvider : DefaultDirectRouteProvider
{
private readonly string _centralizedPrefix;
public CentralizedPrefixProvider(string centralizedPrefix)
{
_centralizedPrefix = centralizedPrefix;
}
protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
{
var existingPrefix = base.GetRoutePrefix(controllerDescriptor);
if (existingPrefix == null) return _centralizedPrefix;
return string.Format("{0}/{1}", _centralizedPrefix, existingPrefix);
}
}
and add it in WebApiConfig.cs like this
config.MapHttpAttributeRoutes(new CentralizedPrefixProvider("api/v{version:int}"));
For more information, you can get help from this link... http://www.strathweb.com/2015/10/global-route-prefixes-with-attribute-routing-in-asp-net-web-api/
There are number of ways to handle versioning with Web-API: via URI, via query string, or via the request header.
Ultimately, whatever approach you take, you'll need to make some kind of "Controller selector" abstraction with logic for interpreting your version number and selecting the appropriate controller.
This article does a good job explaining the basics.
Just to throw an off-the-wall idea at you: what about using dependency injection to have either the DI container or a container-defined factory interface to create an instance of your versioned API to be used by the controller?
public interface IApiVersion {
//the public method signatures
}
public interface IApiVersionFactory {
IApiVersion Create(int version);
}
Inject the factory into the controller and invoke the methods for the correctly versioned API and you'd be good to go.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With