Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST webservice WebAPI - create endpoint based on multiple entities

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; }
    }
}
like image 553
RSilva Avatar asked Aug 08 '16 12:08

RSilva


3 Answers

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/

like image 74
haider_kazal Avatar answered Nov 15 '22 08:11

haider_kazal


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.

like image 41
spoonraker Avatar answered Nov 15 '22 08:11

spoonraker


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.

like image 43
devstruck Avatar answered Nov 15 '22 10:11

devstruck