7
\$\begingroup\$

Trying to create something really lightweight. Sources are on GitHub.

To create a proxy we need to define an interface first, e.g.:

// Fake Online REST API for Testing and Prototyping
[Site("https://jsonplaceholder.typicode.com")] 
public interface ITypicode
{
 [Get("posts")]
 Task<BlogPost[]> GetAsync();
 [Get("posts/{0}")]
 Task<BlogPost> GetAsync(int id);
 [Post("posts")]
 Task<BlogPost> PostAsync([Body] BlogPost data);
 [Put("posts/{0}")]
 Task<BlogPost> PutAsync(int id, [Body] BlogPost data);
 [Delete("posts/{0}")]
 Task<BlogPost> DeleteAsync(int id);
}
public class BlogPost
{
 public int UserId { get; set; }
 public int Id { get; set; }
 public string Title { get; set; }
 public string Body { get; set; }
}

At this moment we actually know enough to generate a proxy:

ITypicode typicode = RestClient.Create<ITypicode>();
BlogPost blogPost = await typicode.PutAsync(1, new BlogPost { Body = "Wow!" });
Console.WriteLine(blogPost.Body);

We can inject HttpMessageHandler:

ITypicode typicode = RestClient.Create<ITypicode>(handler);

We can also emit the proxy class for dependency injection:

Type typicodeType = RestClient.Emit<ITypicode>();

About error handling – this exception is thrown for unsuccessful HTTP status codes:

public class RestException : Exception
{
 public RestException(HttpResponseMessage response)
 {
 Response = response;
 }
 public HttpResponseMessage Response { get; }
 public override string ToString() => 
 Response.Content.ReadAsStringAsync().Result;
}

We could specify extra type parameter for the SiteAttribute:

[Site("https://jsonplaceholder.typicode.com", Error = typeof(TypicodeError))]
public interface ITypicode
{
 // ...
}

to have generic exception be thrown instead:

public class RestException<TError> : RestException
{
 public RestException(HttpResponseMessage response)
 : base(response)
 {
 }
 public T Error => JsonConvert.DeserializeObject<TError>(ToString());
}

So error response body will be kindly deserialized for us.

It is possible to implement API interface on a server side ASP.NET Web API Controller also to insure compatibility.

What do you think about this design?

To be continued with implementation details.


UPDATE: Adding support for HTTP headers – something like this:

[Site("https://jsonplaceholder.typicode.com")]
public interface ITypicode
{
 [Get("posts/{0}")]
 [Header("X-API-KEY: {1}")] // req - in
 [Header("Content-Type: {2}; charset={3}")] // res - out
 Task<BlogPost> GetAsync(
 int id, string apiKey, out string contentType, out string charset); 
}

Does it look good? Anything else that might be useful?

Der Kommissar
20.2k4 gold badges70 silver badges158 bronze badges
asked Sep 4, 2016 at 17:10
\$\endgroup\$
1
  • \$\begingroup\$ P.S. I started it yesterday, so it is definitely rough ;) \$\endgroup\$ Commented Sep 4, 2016 at 17:24

1 Answer 1

4
\$\begingroup\$

Usually when I see your questions there isn't much for me to say, because you usually flesh everything out really well. :) (Probably why this has been unanswered so long.)

That said, I think I do have one comment here:

If you have support to edit the HeaderAttribute or the GetAttribute, I would consider replacing the {0}, {1} (etc.) format symbols with a named format symbol.

[Get("posts/{id}")]
Task<BlogPost> GetAsync(int id);
[Site("https://jsonplaceholder.typicode.com")]
public interface ITypicode
{
 [Get("posts/{id}")]
 [Header("X-API-KEY: {apiKey}")] // req - in
 [Header("Content-Type: {contentType}; charset={charset}")] // res - out
 Task<BlogPost> GetAsync(
 int id, string apiKey, out string contentType, out string charset); 
}

It makes everything more meaningful.

If it's WebAPI you probably don't have access to the source for these attributes, but you can probably wrap them with a new one to add support for this feature. (Adds a little complexity, but should be really awesome to see happen.)

answered Jan 10, 2017 at 17:04
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.