HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

JSON REST client proxy generator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
restproxygeneratorclientjson

Problem

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 GetAsync();

    [Get("posts/{0}")]
    Task GetAsync(int id);

    [Post("posts")]
    Task PostAsync([Body] BlogPost data);

    [Put("posts/{0}")]
    Task PutAsync(int id, [Body] BlogPost data);

    [Delete("posts/{0}")]
    Task 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();
BlogPost blogPost = await typicode.PutAsync(1, new BlogPost { Body = "Wow!" });
Console.WriteLine(blogPost.Body);


We can inject HttpMessageHandler:

ITypicode typicode = RestClient.Create(handler);


We can also emit the proxy class for dependency injection:

Type typicodeType = RestClient.Emit();


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 : RestException
{
    public RestException(HttpResponseMessage response)
        : base(response)
    {
    }

    public T Error => JsonConvert.DeserializeObject(ToString());
}


So error re

Solution

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 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 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.)

Code Snippets

[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);    
}

Context

StackExchange Code Review Q#140491, answer score: 4

Revisions (0)

No revisions yet.