[GitHub](https://github.com/dmitrynogin/dynoproxy) and [NuGet](https://www.nuget.org/packages/Dynoproxy) Allows to invoke public/private REST API just by defining an interface. 200 lines of C# in total. ### Demo Sample API is publicly available at http://jsonplaceholder.typicode.com: public interface ITypicode : IDisposable { [Description("GET posts")] Task<BlogPost[]> GetAsync(); [Description("GET posts/{0}")] Task<BlogPost> GetAsync(int id); [Description("PUT posts/{0} {1}")] Task<BlogPost> PutAsync(int id, BlogPost data); } public class BlogPost { public int UserId { get; set; } public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } } Now we could test proxy generator: [TestMethod] public async Task Call_REST_API() { using (var proxy = Proxy.Create<ITypicode>("http://jsonplaceholder.typicode.com")) { var posts = await proxy.GetAsync(); Assert.AreEqual(100, posts.Length); var post = await proxy.GetAsync(1); Assert.AreEqual(1, post.Id); post.Title = "XYZ"; post = await proxy.PutAsync(1, post); Assert.AreEqual("XYZ", post.Title); } } Optional `authenticate` parameter supports custom authentication schemas using ad-hoc `HttpClient` extension method to setup `HttpClient.DefaultRequestHeaders` in a way like this: Proxy.Create<ITypicode>(url, (HttpClient client) => client.AuthenticateAsync(...)) ### Library Library code contains a unified proxy generator based on Castle.Core: public static class Proxy { public static T Create<T>(object target) where T : class => Create<T>(call => Dynamic.InvokeMember( target, call.Name, call.Args.ToArray())); public static T Create<T>(string apiUrl, Func<HttpClient, Task> authenticate = null) where T : class, IDisposable => Create<T>(new Uri(apiUrl, UriKind.Absolute), authenticate); public static T Create<T>(Uri apiUrl, Func<HttpClient, Task> authenticate = null) where T : class, IDisposable => RestProxy.Create<T>(apiUrl, authenticate); public static T Create<T>(Func<ProxyCall, object> target) where T : class { var proxyGenerator = new ProxyGenerator(); return proxyGenerator.CreateInterfaceProxyWithoutTarget<T>( ProxyGenerationOptions.Default, new Interceptor(target)); } class Interceptor : IInterceptor { public Interceptor(Func<ProxyCall, object> target) => Target = target; Func<ProxyCall, object> Target { get; } public void Intercept(IInvocation invocation) => invocation.ReturnValue = Target( new ProxyCall(invocation.Method.Name, invocation.Arguments) .Returns(invocation.Method.ReturnType) .Define(invocation.Method.GetCustomAttributes())); } } Where helpers are: public class ProxyCall { public ProxyCall(string name, IEnumerable<object> args) : this(name, args.ToArray()) { } public ProxyCall(string name, params object[] args) { Name = name; Args = args; } public string Name { get; } public IReadOnlyList<object> Args { get; } public CallResult Result { get; private set; } = CallResult.None; public ProxyCall Returns<T>() => Returns(typeof(T)); public ProxyCall ReturnsAsync<T>() => Returns(typeof(Task<T>)); public ProxyCall Returns(Type type) => With(result: new CallResult(type)); public CallMethod Method { get; private set; } = CallMethod.Undefined; public ProxyCall Define(string description) => Define(new DescriptionAttribute(description)); public ProxyCall Define<TAttribute>() where TAttribute : Attribute, new() => Define(new TAttribute()); public ProxyCall Define(IEnumerable<Attribute> attributes) => Define(attributes.ToArray()); public ProxyCall Define(params Attribute[] attributes) => With(method: new CallMethod(attributes.Concat(Method))); ProxyCall With(CallResult result = null, CallMethod method = null) => new ProxyCall(Name, Args) { Result = result ?? Result, Method = method ?? Method }; public bool IsDispose => Name == nameof(IDisposable.Dispose) && Result.Void && !Args.Any(); } And: public class CallResult { public static readonly CallResult None = new CallResult(typeof(void)); internal CallResult(Type raw) => Raw = raw; public Type Raw { get; } public bool Sync => !Async; public bool Async => typeof(Task).IsAssignableFrom(Raw); public bool Void => Raw == typeof(void) || Raw == typeof(Task); public Type Type => Async ? (Void ? typeof(void) : Raw.GetGenericArguments()[0]) : Raw; } And: public class CallMethod : ReadOnlyCollection<Attribute> { public static readonly CallMethod Undefined = new CallMethod(); internal CallMethod(IEnumerable<Attribute> attributes) : this(attributes.ToArray()) { } internal CallMethod(params Attribute[] attributes) : base(attributes) { } public bool Contains<TAttribute>() where TAttribute : Attribute => Select<TAttribute>().Any(); public T Peek<TAttribute, T>(Func<TAttribute, T> selector) where TAttribute : Attribute => Select(selector).FirstOrDefault(); public IEnumerable<Attribute> Select<TAttribute>() where TAttribute : Attribute => Select((TAttribute a) => a); public IEnumerable<T> Select<TAttribute, T>(Func<TAttribute, T> selector) where TAttribute : Attribute => Items.OfType<TAttribute>().Select(selector); public string Description => Peek((DescriptionAttribute a) => a.Description); } The following class provides all necessary support for REST API calls: static class RestProxy { public static T Create<T>(Uri apiUrl, Func<HttpClient, Task> authenticate = null) where T : class, IDisposable { var client = new HttpClient() { BaseAddress = apiUrl }; return Proxy.Create<T>(Execute); object Execute(ProxyCall call) { if(call.IsDispose) { client.Dispose(); return null; } return call.Result.Void ? Send() : SendAndReceive(); object Send() => client.SendAsync(call, authenticate); object SendAndReceive() => typeof(RestProxy) .GetMethod(nameof(SendAndReceiveAsync), BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(call.Result.Type) .Invoke(null, new object[] { client, call, authenticate }); } } static async Task<T> SendAndReceiveAsync<T>( HttpClient client, ProxyCall call, Func<HttpClient, Task> authenticate) { var response = await client.SendAsync(call, authenticate); var json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<T>(json); } static async Task<HttpResponseMessage> SendAsync( this HttpClient client, ProxyCall call, Func<HttpClient, Task> authenticate = null) { var description = call.Method.Description .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var request = new HttpRequestMessage( new HttpMethod(description[0]), new Uri( string.Format(description[1], call.Args.ToArray()), UriKind.Relative)) { Content = description.Length < 3 || !int.TryParse(description[2].Trim("{}".ToCharArray()), out var index) ? null : new StringContent( JsonConvert.SerializeObject(call.Args[index]), Encoding.UTF8, "application/json") }; var response = await client.SendAsync(request); if (response.StatusCode == HttpStatusCode.Unauthorized) if(authenticate == null) throw new AuthenticationException(); else { await authenticate(client); return await SendAsync(client, call); } response.EnsureSuccessStatusCode(); return response; } } _P.S. Let me know please if something is missing here which could prevent it from being really useful :)_