Coder Social home page Coder Social logo

canton7 / restease Goto Github PK

View Code? Open in Web Editor NEW
1.1K 1.1K 105.0 3.48 MB

Easy-to-use typesafe REST API client library for .NET Standard 1.1 and .NET Framework 4.5 and higher, which is simple and customisable. Inspired by Refit

License: MIT License

C# 100.00%

restease's People

Contributors

canton7 avatar davidroth avatar fazouane-marouane avatar husqvik avatar maxdeg avatar nikeee avatar sharparam avatar tidusjar avatar tobiasbreuer avatar zoalasaurusrawr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

restease's Issues

dnx

Hi,

Is any work being done to get this to work with dot net core?

Thanks

[Body] with key?

Hello,

Is there a way to explicity set a key for a body param, without url encoding?
The problem is that I have a Json Api, which expects certain json body params (one of them is an array), with certain keys.
For now as a workaround I'm sending it in the Query, but I have to encode the array and parse it in the client and server as follows: ?stuff_key=bla1,bla2, and as stated before, I wouldn't really want to send it via query/url.
I think it would rather be simplier to send stuff_key: ["bla1", "bla2",...] in the body.

It would be great to have something like

([Body("stuff_key")] IList<string> stuff)

Support for IDisposable?

Your example code:

public interface IGitHubApi
{
  [Get("users/{userId}")]
  Task<User> GetUserAsync([Path] string userId);
}

IGitHubApi api = RestClient.For<IGitHubApi>("https://api.github.com");
User user = await api.GetUserAsync("canton7");

If I want to dispose the RestClient after use, is this possible like:

public interface IGitHubApi : IDisposable
{
  [Get("users/{userId}")]
  Task<User> GetUserAsync([Path] string userId);
}

using (IGitHubApi api = RestClient.For<IGitHubApi>("https://api.github.com"))
{
  User user = await api.GetUserAsync("canton7");
}

Hiding API interface nested in a class/namespace (documentation)

I think the docs could add a small tip under Quick Start: the API interface should be public but this shouldn't prevent you from "hiding" it nested in an existing public class or another namespace for the sake of not cluttering your namespaces, should this be a concern.

Is Newtonsoft.Json >= 9.0.1 needed?

First of all, great library! Secondly, in my project for some obscure reason I need to use v7.0.1 of Newtonsoft.Json. I see that with RestEase 1.4.0 the required version of Newtonsoft.Json was upped to 9.0.1

Is indeed this version that is required for RestEase at the minimum or could the latest RestEase work with some older JSON.NET too?

URLEncode with unicode characters is malformed inside a Querystring

Hi,

I found a bug in the Query building part of RestEase.

In Requester.cs line 132 the following code does a double encode, which causes the character é to become %u00e9 first (from the QueryParamBuilder), and finally %25u00e9 when the uriBuilder.Uri is returned.

uriBuilder.Query = QueryParamBuilder.Build(initialQueryString, queryParams).TrimStart('?');

return uriBuilder.Uri;

In other words the QueryParamBuilder class does an encode, and the UriBuilder class does another encode. So the string get encoded twice.

cannot install into a PCL 78 project

Could not install package 'RestEase 1.0.0'. You are trying to install this package into a project that targets 'portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarinios10', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

{path} placeholders are URLEncoded making usage of composite url paths harder.

If I have the following interface:

public interface ITest
{
  [Post("/v1/Storage/{path}")]
  Task Upload([Path] string path, [Body] Stream data);
}

and I call it like that:

var api = RestClient.For<ITest>("http://localhost");
var data = new Stream(...);
api.Upload("volumeA/partitionB/test.txt", data);

it fails because the path is URLEncoded in Requester.SubstitutePathParameters(...) and the resulting request looks like this:

POST http://localhost/v1/Storage/volumeA%2FpartitionB%2Ftest.txt
while my service endpoint expects:
POST http://localhost/v1/Storage/volumeA/partitionB/test.txt

The only workaround I found is to declare the interface like that:

[Post("/v1/Storage/{volume}/{partition}/{filename}")]
 Task Upload([Path] string volume, [Path] string partition, [Path] string filename, [Body] Stream data);

and call it like that, which reduces the flexibility of the method:
api.Upload("volumeA", "partitionB", "test.txt", data);

I understand the reason behind URLEncode, but it will be nice if paths could be handled a little bit more precisely. One option is to change the Requester.SubstitutePathParameters() so UrlEncode is used like this:

serialized.Value = string.Join('/', serialized.Value.Split('/').Select(s => UrlEncode(s)));

i.e. the individual "folders" in the path parameter are URLEncoded, but the existing path separators (backsplashes) are preserved.

Culture during serialization

Is it possible to set the culture during serialization, i couldn't find any option for that?
I found only the Format string parameter of the QueryAttribute but it is not possible to control the separator characters with format string.
The serialization culture should be adjustable separately from thread culture.

Porting to .net core

Hey there,

I spent a few hours today porting this to .net core. The team I work with love RestEase. If we contributed back, would you consider taking the port?

I still have a ways to go with Moq not being ported yet, but I'm looking to contribute to them too in order to get it working with this project.

I figured I'd reach out before I assumed anything. I was also weary of starting a pull request only because I'm not sure of the best way to organize the project files considering that netfull and netcore would be living side by side.

In a nutshell, wanting to help, but I don't want to step on your toes!

Could not load file or assembly 'System.Diagnostics.DiagnosticSource, Version=4.0.0.0

I reference RestEase from a net461 project. It builds fine but gives a runtime exception:

Message: System.IO.FileLoadException : Could not load file or assembly 'System.Diagnostics.DiagnosticSource, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Any idea on how to solve this?

Customise serialisation through custom attributes

An integration with an API that has a wrapper JSON property around all response/requests. This is noisy and I'd like to NOT have to create a custom class for each of these wrappers (i.e. ContactResponse, ContactsResponse, InvoiceResponse, InvoicesResponse, InvoiceCreateRequest) as it gets noisy and tedious very quickly.

A previous implementation of this integration used RestSharp that had a simple property RootElement you could set per request to skip the root element and deserialise the inner.

I thought I could create a custom serialiser / deserialiser. My issue is when it comes to that method I only have the body of the request/response. I'd like to know what method was used, so I could look at attributes:

public T Deserialize<T>(string content, HttpResponseMessage response, IRequestInfo requestInfo)
{
   var rootElement = requestInfo.Method.GetCustomAttributes<JsonRoot>();
   return DeserialiseFromRoot<T>(rootElement, content);
}

It requires the Method to be added to RequestInfo (non-breaking) and access to the RequestInfo in deserialisers etc. which is breaking.

Any thoughts on what could be achieved, given current API and/or changes?

iOS - PlatformNotSupportedException

This is PCL Profile 151. It appears that dynamic code generation is not supported? Is there a workaround?


System.TypeInitializationException: The type initializer for 'RestEase.RestClient' threw an exception. ---> System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.Reflection.Emit.AssemblyBuilder.DefineDynamicAssembly (System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.10.0.21/src/mono/mcs/class/corlib/System.Reflection.Emit/AssemblyBuilder.pns.cs:37
at RestEase.Implementation.ImplementationBuilder..ctor () [0x00026] in <221e03a95ca543aab2dbf1ba844a9cf1>:0
at RestEase.RestClient..cctor () [0x00000] in <221e03a95ca543aab2dbf1ba844a9cf1>:0

Improving the QuickStart

Hi,

It looks like you have a really great project here, but it's been really tough to get going because there's a lot assumed in the example. Once I found #8 and added the necessary imports, I finally had a working example.

It'd be great to see full working examples that can be copy-pasted. It lowers the barrier to entry.

Quick Start:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RestEase;
using Newtonsoft.Json;

namespace SampleApp
{
    public class User
    {
        public string Login;
        public int Id;
        public string Url;
    }

    [Header("User-Agent", "RestEase")]
    public interface IGitHubApi
    {
        [Get("/users/{userId}")]
        Task<User> GetUserAsync([Path] string userId);
    }

    public class Program
    {
        public static void Main()
        {
            var api = RestClient.For<IGitHubApi>("https://api.github.com");
            var user = api.GetUserAsync("canton7").Result;
            Console.WriteLine("Login: {0}, ID: {1}, URL: {2}", user.Login, user.Id, user.Url);
            Console.ReadLine();
        }
    }
}

Not sure if anything is gained with the more verbose property declarations in User:

    [JsonProperty("login")]
    public string Login { get; set; }

In any case, keep up the great work!

Add For(Type) to support convention registrations

Thanks for creating RestEase. It looks like exactly what I'm after to replace RestSharp.

I'm writing an Autofac module where I am doing convention based registrations for multiple interfaces that use RestEase. At the point of registration, I am resolving a set of classes that follow a naming convention. I then want to register these with an Autofac container builder. Unfortunately this isn't possible without falling down to reflection calls because RestEase only supports For<T>, however I have IEnumerable<Type> to work with.

My current implementation is

        private void RegisterQueries(ContainerBuilder builder)
        {
            var assemblyTypes = ThisAssembly.GetTypes();
            var configTypes = assemblyTypes
                .Where(x => x.IsAssignableTo<IResolverConfig>() && x.GetTypeInfo().IsClass).ToList();
            var registerWrapperTemplate = typeof(RegisterWrapper<IResolverConfig>).GetGenericTypeDefinition();

            foreach (var configType in configTypes)
            {
                var name = configType.Name.Replace("Config", string.Empty);
                var configInterface = configType.GetTypeInfo().GetInterface("I" + configType.Name);
                var queryTypeName = "I" + name + "LocationQuery";
                var queryType = assemblyTypes.Single(x => x.Name == queryTypeName && x.GetTypeInfo().IsInterface);
                var registerWrapper = registerWrapperTemplate.MakeGenericType(queryType);

                builder.Register(
                    c =>
                    {
                        var config = (IResolverConfig)c.Resolve(configInterface);

                        var wrapper = (IRegisterWrapper)Activator.CreateInstance(registerWrapper);

                        return wrapper.Create(config.Address);
                    }).As(queryType);
            }
        }

        private interface IRegisterWrapper
        {
            object Create(string address);
        }

        private class RegisterWrapper<T> : IRegisterWrapper
        {
            public object Create(string address)
            {
                return RestClient.For<T>(address);
            }
        }

If RestEase supported returning object via a Type parameter, this could be streamlined to this

        private void RegisterQueries(ContainerBuilder builder)
        {
            var assemblyTypes = ThisAssembly.GetTypes();
            var configTypes = assemblyTypes
                .Where(x => x.IsAssignableTo<IResolverConfig>() && x.GetTypeInfo().IsClass).ToList();

            foreach (var configType in configTypes)
            {
                var name = configType.Name.Replace("Config", string.Empty);
                var configInterface = configType.GetTypeInfo().GetInterface("I" + configType.Name);
                var queryTypeName = "I" + name + "LocationQuery";
                var queryType = assemblyTypes.Single(x => x.Name == queryTypeName);

                builder.Register(
                    c =>
                    {
                        var config = (IResolverConfig)c.Resolve(configInterface);

                        return RestClient.For(queryType, config.Address);
                    }).As(queryType);
            }
        }

Cant get it to work in a console app

Hi,
Been trying to use this lib to access several rest endpoints from a console app.
Nothing happens, like nada, not even an exception is thrown.
Wrapped the async methods using several techniques in the main method,
still nothing happens.
Any pointers as to how I might get it working from a console app?

Thanks,
Ozzy

Add AllowStatusCodeAttribute

I'm finding that the AllowAnyStatusCodeAttribute is a bit of a sledgehammer option. A common scenario I hit is to throw if there are any failures, but allow a 404 to be interpreted as a null. Under the current implementation I have to use AllowAnyStatusCode but this then takes away the benefit of throwing an exception on any other unexpected status code.

It would be great if there was also an AllowStatusCode attribute (allow multiple) that could target specific scenarios against a member.

For example:

        [Delete("customers/{id}")]
        [AllowStatusCode(HttpStatusCode.NotFound)]
        Task DeleteAsync([Path("id")] string id, CancellationToken cancellationToken = default(CancellationToken));

This would allow for the expected outcomes as well as 404, but throw on anything else.

Thoughts?

Ability to add dynamic path variable to every request

I am looking for a way to add the same dynamic path variable to every call within a given interface

Here is an example of what I am currently doing

[Header("Accept", "application/json")]
public interface ICategoryApi
{
    #region Category
    [Get("/API/Account/{accountId}/Category")]
    Task<Category> GetCategoryAsync([Path] string accountId, [Query("name")]string name);

    [Post("/API/Account/{accountId}/Category")]
    Task<Category> CreateCategoryAsync([Path] string accountId, [Body] Category item);

    [Put("/API/Account/{accountId}/Category")]
    Task<Category> UpdateCategoryAsync([Path] string accountId, [Body] Category item);

    [Delete("/API/Account/{accountId}/Category")]
    Task<Category> DeleteCategoryAsync([Path] string accountId, [Body] string itemId);
    #endregion Category
}

Ideally, I'd love it if I could define a more "global" way (either per interface, or per method) to define something like a call back to set the value for the path argument

[Header("Accept", "application/json")]
public interface ICategoryApi
{
    #region Category
    [Get("/API/Account/{accountId}/Category")]
    Task<Category> GetCategoryAsync([Query("name")]string name);

    [Post("/API/Account/{accountId}/Category")]
    Task<Category> CreateCategoryAsync([Body] Category item);

    [Put("/API/Account/{accountId}/Category")]
    Task<Category> UpdateCategoryAsync([Body] Category item);

    [Delete("/API/Account/{accountId}/Category")]
    Task<Category> DeleteCategoryAsync([Body] string itemId);
    #endregion Category
}

and then allow something like

_apiInterface = restClient.For<TInterface>().WithPathParameter("accountId", _accountId);

or maybe

_apiInterface = restClient.For<TInterface>().WithPathParameter("accountId", (s) => _accountId);

Bug with extra ? in URL on mono (Xamarin/Android/.NET Standard 1.1)

I am getting two ?'s in the URLs when building for NETSTANDARD (Xamarin Android) (WPF seemed to work fine.) This causes REST HTTP calls to fail.

  • Potential Problem 1: initialQueryString = "?" + rawQueryParameter; (Requester.ConstructUri)
  • Potential Problem 2: char separator = String.IsNullOrWhiteSpace(query) ? '?' : '&'; (QueryParamBuilder.Build)
  • Definite problem for me 3: sb.Append(hasQuery ? '&' : '?'); (QueryHelpers.AddQueryString)

You can see ?'s are added in a few places and there is a different code path for .NET Framework, so I'm not sure how to best fix this aside from just forcing it to be right inside Requester.ConstructUri.

Temporary hackish fix in Requester.ConstructUri() that worked for me:

var builtQuery = QueryParamBuilder.Build(initialQueryString, queryParams);
if (builtQuery != null && builtQuery.Length > 0 && builtQuery.StartsWith("?")) { builtQuery = builtQuery.Substring(1); }
uriBuilder.Query = builtQuery;

Reference on Stackoverflow: http://stackoverflow.com/a/11083456/208304

Strong name signing

We are using this great library in an environment where every package is signed. Currently we are signing this library during our build process. However it is currently fine but it would be great if you can sign the library. What do you think about this?

Could not load file or assembly 'System.Diagnostics.DiagnosticSource'

Hello,

I cannot seem to get the package working under the following environment:

  • Visual Studio 2017 RC3
  • .NET Framework v4.6.2
  • RestEasy 1.3.2

After installing RestEasy from Nuget, I'm trying to run the first example provided in the README, but it results into the following error:

FileLoadException: Could not load file or assembly 'System.Diagnostics.DiagnosticSource, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

System.Net.Http.WinHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
System.Net.Http.HttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
RestEase.Implementation.Requester+<SendRequestAsync>d__23.MoveNext()
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
RestEase.Implementation.Requester+<RequestAsync>d__26.MoveNext()
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
<...>

Any ideas? Am I missing something?

Possible support for multipart/form-data?

I am curious if it is possible to use RestEase for APIs that leverage multipart/form-data in a more structured way?

Here is an example of what I'd like to "post":

POST /API/Image
Accept: application/xml
Content-Type: multipart/form-data; boundary=25112bca3fa7474e9874f9e8654c27aa
--25112bca3fa7474e9874f9e8654c27aa
Content-Disposition: form-data; name="data"
<Image>
<filename>2014 red racer.jpg</filename>
<description>Our new 2014 Racer model in Red!</description>
<itemID>5</itemID>
</Image>
--25112bca3fa7474e9874f9e8654c27aa
Content-Disposition: form-data; name="image"; filename="2014 red racer.jpg"
Content-Type: image/jpeg
.... Image data ....
--25112bca3fa7474e9874f9e8654c27aa--

I think I can do this with using MultipartFormDataContent as a parameter type. The only issue is with that I loose control over the clean API that RestEase creates.

Ideally I'd like do something like this (this is just a thought)

public interface IMyApi
{
   [Post("/API/Item"]
   [BodyType(BodySerializtionMethod.MultiPart)]
   Task<Response<ApiResponse>> CreateImage([BodyPart(Name="data")] ImageMetaData metaData, [BodyPart(Name="image"] Stream imageData);
}

Visual Studio complains method is not 'async'

I have code that looks like this

var APIResponse = await someApi.GetStudentsAsync(); // "The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'"

public interface ISomeApi
{
    [Get("students")]
    Task<Response<List<Student>>> GetStudentsAsync();
}

Visual Studio won't build this, claiming the method is not async? This code was mostly cribbed from the Quick Start documentation.

Path Parameter is not encoded properly for white spaces

My path parameter has a white space in it and the space is encoded by '+' sign which actually need to be encoded with %20 so my webapi controller understand these url encode and can do a urldecode by itself.

Note: A code needs to be changed in RestEase.Implementation.Requester.SubstitutePathParameters method where HttpUtility.UrlEncode need to be updated by HttpUtility.UrlPathEncode

Add request info to ApiException

It would be helpful if ApiException carried some information about the failed request. For example, the request method and request URI.

Attribute to ignore specific interface members

I wrote a Swagger Codegen template specifically for one of our projects, using RestEase (mostly because of the option to manually process the response itself, which is not yet present in Refit). However I ran into an issue.

I am not able to define an interface that has both regular members (method definitions), and RestEase-annotated ones.

The issue with this is because how Swagger Codegen handles files. You cannot create separate directories for separate files easily, so I can't have a separate Api, Interfaces, and Clients library for three definitions (Api for actual API client classes that black-box the inner workings, error handling, whatnot, Interfaces for the interfaces of Api classes for dependency injection, and Clients for the RestEase client interfaces with proper annotations). So for that, I use file extensions to separate files (.cs for the Api class, .interface.cs for Interface classes, and .client.cs for, yes, you guessed right, the interfaces used by RestClient).

The issue with this is code redundancy. Since my interfaces use the same exact fingerprint (so e.g. a Task<Response<User>> GetUser() function has the same return type, same arguments in same order, et cetera), it should be able to merge. Except, in the Api I'm proxy-ing for calls without CancellationToken (just calling samecall(sameparams, CancellationToken.None) ), but that one does not exist in the RestClient-only interfaces. So I've added them, without annotation, expecting it to work. However it does not, it cannot create the client, due to missing annotations.

Would it be possible to add a [RestIgnore] attribute, in order to be able to add calls that aren't supposed to be part of the client, but can be used in other implementations of the same interface?

Bug: Unable to remove interface defined header value by nullifying on method level

According to the documentation you should be able to remove a header by redefinition.

[Header("X-InterfaceAndMethod-ToBeRemoved", "InterfaceValue")]
public interface ISomeApi
{
  ...
  [Header("X-InterfaceAndMethod-ToBeRemoved", null)]
  Task DoSomethingAsync(
    ...
  )
}

However, having code like this, results in an exception. Below is the output from the attached POC project

Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: str
   at System.Reflection.Emit.ModuleBuilder.GetStringConstant(String str)
   at System.Reflection.Emit.ILGenerator.Emit(OpCode opcode, String str)
   at RestEase.Implementation.ImplementationBuilder.AddMethodHeader(ILGenerator methodIlGenerator, HeaderAttribute header)
   at RestEase.Implementation.ImplementationBuilder.AddMethodHeaders(ILGenerator methodIlGenerator, MethodInfo methodInfo)
   at RestEase.Implementation.ImplementationBuilder.HandleMethods(TypeBuilder typeBuilder, Type interfaceType, FieldBuilder requesterField, FieldInfo classHeadersField, AllowAnyStatusCodeAttribute classAllowAnyStatusCodeAttribute, SerializationMethodsAttribute classSerializationMethodsAttribute, PropertyGrouping properties)
   at RestEase.Implementation.ImplementationBuilder.BuildImplementationImpl(Type interfaceType)
   at RestEase.Implementation.ImplementationBuilder.CreateImplementation[T](IRequester requester)
   at RestEasePOC.Program.Main(String[] args) in D:\POC\RestEasePOC\RestEasePOC\Program.cs:line 11

The ability to remove a "default" header for certain endpoints are very convenient.

RestEasePOC.zip

Ability to NOT use the method param name in the QueryString?

I am looking to do something like this

public class Category {
    public string p {get;set;}
    public int i {get;set;}

    public override string ToString() { return $"p={p}&i={i}"; }
}

Task<Category> GetCategoryAsync([Query(QuerySerializationMethod.ToString)] Category item);

the problem is, even though I am override the "ToString" method and creating a valid QueryString, the RestEase framework is prepending the name "item=" to the beginning of the query string. I'd like to avoid this.

I am getting this

/Category.json?item=p=test&i=1

and I want to get this

/Category.json?p=test&i=1

I know I change the signature of the method to contain individual properties, unfortunately that is not practical as my actual real-world use case has an option with 10 to 15 different properties and I only want to include the ones that are non-null/non-default values.

Any suggestions? About the only thing I can think of is creating my own IRequestQueryParamSerializer however I don't think even that will work as it is requiring a "key" to be returned with the serialized result.

System.NullReferenceException in Requester.ConstructUri

I am running into a null reference error when I move from simple test cases in NUnit into my actual application.

System.NullReferenceException: Object reference not set to an instance of an object.
   at RestEase.Implementation.Requester.ConstructUri(String path, IRequestInfo requestInfo)
   at RestEase.Implementation.Requester.<SendRequestAsync>d__23.MoveNext()

Here is the sample code that is throwing the error. The odd part is -- this is 100% identical to what I am using in my NUnit test with the exception that this is running inside a Thread that is launched from a console application (it is a thread pool thread)

var httpClientApi = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential(userName, password) })
{
    BaseAddress = new Uri($"https://localhost/Account/{_account.Id}/")
};

_restClientApis = new RestClient(httpClientApi)
                    {
                        RequestQueryParamSerializer = new CustomQueryParamSerializer(),
                        ResponseDeserializer = new HybridResponseDeserializer(),
                        RequestBodySerializer = new JsonRequestBodySerializer()
                                                    {
                                                        JsonSerializerSettings =
                                                            new JsonSerializerSettings
                                                                {
                                                                    DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
                                                                    Formatting = Formatting.Indented
                                                                }
                                                    }
                    };

_itemApi = _restClientApis.For<IItemApi>();

var categoryResult = await _itemApi.GetCategoryAsync(); // null reference here

Allow body serializers to specify their content-type

Currently, we will default all serialized bodies to text/plain, as that's what HttpClient does.

Instead, allow the serializer to specify its default content-type (or the content-type for a particular serialization? That might make more sense), but allow this to be overridden by [Header]s

Duplicate type name within an assembly

Found in serverlog... probably not thread safe?

System.ArgumentException: Duplicate type name within an assembly.
   at System.Reflection.Emit.TypeBuilder.DefineType(RuntimeModule module, String fullname, Int32 tkParent, TypeAttributes attributes, Int32 tkEnclosingType, Int32[] interfaceTokens)
   at System.Reflection.Emit.TypeBuilder.Init(String fullname, TypeAttributes attr, Type parent, Type[] interfaces, ModuleBuilder module, PackingSize iPackingSize, Int32 iTypeSize, TypeBuilder enclosingType)
   at System.Reflection.Emit.ModuleBuilder.DefineType(String name, TypeAttributes attr)
   at RestEase.Implementation.ImplementationBuilder.BuildImplementationImpl(Type interfaceType) in c:\projects\restease\src\RestEase\Implementation\ImplementationBuilder.cs:Zeile 107.
   at RestEase.Implementation.ImplementationBuilder.<CreateImplementation>b__0[T](RuntimeTypeHandle key) in c:\projects\restease\src\RestEase\Implementation\ImplementationBuilder.cs:Zeile 87.
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at RestEase.Implementation.ImplementationBuilder.CreateImplementation[T](IRequester requester) in c:\projects\restease\src\RestEase\Implementation\ImplementationBuilder.cs:Zeile 90.
   at RestClient.For<T>(...)

Cannot install RestEase on a PCL with profile 259

Hi,
First to commend on this great library, really awesome.

I initially tested this project on a shared project, for xamarin development, which has worked well (except for deserialization issues). I then wanted to use this library in a PCL, but I cannot get it to work. Does this library not support PCL? or are there other profiles I can try (already tried 111).

Could not install package 'RestEase 1.3.1'. You are trying to install this package into a project that targets '.NETPortable,Version=v4.5,Profile=Profile259', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author. 0

Thanks

.NET 4.0 ?

Documentation says : .NET Framework 4.0, however I cannot use it in a .net 4.0 project ?

Feature Request: Option to not throw exception on invalid HTTP Status Codes

There are a lot of times when it is useful to simple check the status code of the response to do some logic rather than implement an exception handler, test for the status code, and then potentially re-setup the method. It wold be very useful if there was a way (either at the Client level are at an indivdual interface level) to disable throwing an exception on Http Status result <> 200 which would then allow me to use

var responseObject= await api.GetItemAsync();

if (!responseObject.ResponseMessage.IsSuccessStatusCode)
{
...
}

String responses are not passed to Response Deserializer

Hi,

I'm finding that if a route has a return type as string, the http content from the response is directly passed back instead of being first sent to the deserializer. It might be a saving but it's wrong in the case of custom deserializers.

I use restease exclusively with a protobuf-net serializer and this causes a rather unexpected gotcha as you can imagine. As i get the protobuf message directly interpreted as a string...

Let me know if that makes sense? Can dig around and make the PR if it helps. Thanks.

Move Attributes to RestEase.Annotations

I guess it would be nice if all attributes were moved to separate package, let's say RestEase.Annotations so interface won't have dependency on specific version of RestEase (RestEase.Annotations should not change that often).

Provide overload RestClient<T>.For(System.Uri)

Hi,

I just noticed that the RestEase.RestClient.For<T>( .. ); method does not accept a Uri instance. When looking through the code for the variant accepting a string, I can see that internally RestClient is instantiated with a Uri.

Could you provide an overload for this case? :)

Mike.

Feature requests/proposals

Hi 😄

I'd like to know your opinion on the following features:

  1. Make instances of RestEase IDisposable when the provided interface is also IDisposable. The proposed semantics would be to dispose of the HttpClient. When the restease client is being disposed.
  2. Accept an IProgress<int> and provide progress info to the caller. This can be interesting when downloading a big file using restease.

I can create a pullrequest but I wanted your preliminary input.
Thanks!

Multiple attributes (Post and Put) on same Method ?

Is it also possible to use the same method for both Post and Put ? Because the code below throws exception.

public interface ISomeApi
{
    [Post("users")]
    [Put("users")]
    Task CreateOrUpdateUserAsync([Body] User user);
}

Support PATCH requests

Please add support for PATCH requests. This code should do it:

  /// <summary>
    /// Patch Request
    /// </summary>
    public class PatchAttribute: RestEase.RequestAttribute
    {
        /// <summary>
        /// Initialises a new instance of the <see cref="PatchAttribute"/> class
        /// </summary>
        public PatchAttribute() : base(new HttpMethod("PATCH")) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="PatchAttribute"/> class, with the given relative path
        /// </summary>
        /// <param name="path">Relative path to use</param>
        public PatchAttribute(string path) : base(new HttpMethod("PATCH"), path) { }
    }

Path and Query supported at the same time?

I am trying to do something like this and am getting an error " Unable to find both a placeholder {name} and a [Path("name")] for parameter name."

[Get("/API/Account/{accountId}/Category")]
Task<Category> GetCategoryAsync([Path] string accountId, [Query("name")]string name);

Is it possible to leverage both a dynamic path and query string parameters at the same time?

Underlying Connection Closed - How to Debug?

Hi,

I am trying to run some simple tests against the Asana API and am getting a connection closed exception. The call has been tested in ARC for Chrome and translating it into RestEase syntax should be trivial but it does not work (very very simple GET request). I can't seem to drill down into any useful info to work out what is failing - the response object never gets populated as it never gets that far.

Can I somehow examine the concatenated URL / headers / payload that is being sent just before the request is made etc. to see if there is an obvious syntax issue?

Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
--- End of inner exception stack trace ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at RestEase.Implementation.Requester.d__23.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at RestEase.Implementation.Requester.d__281.MoveNext() --- End of inner exception stack trace --- at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification)

ApiException class having only internal constructors makes unit testing difficult

In order to unit test that my code handled an error response correctly, I wanted to have a mock/fake implement the interface for my api, and simply throw an ApiException when the method in question is called.

Unfortunately, that's not possible with ApiException constructors being internal, so instead I had to research how to customize and override HttpClient and HttpMessageHandler to actually form a 500 status code response message, without actually making a request. This was made made even more complicated by RestEase.Implementation.Request.ConstructUri insisting that the HttpClient have a valid BaseAddress (otherwise generating a NullReferenceException), even when the HttpClient itself would have worked fine without one.

I strongly recommend making the constructors for ApiException public to facilitate testing. There needs to be an easier way to force an api call to fail (without actually using the network), for the purposes of unit testing.

Cannot inherit Variable Interface Headers

I have defined a common parent because I need authentication in almost all my api endpoints.
The parent endpoint looks like this:

public interface IAuthenticatedEndpoint
    {
        [Header("X-Api-Token")]
        string ApiToken { get; set; }
        [Header("X-Api-Username")]
        string ApiUsername { get; set; }
    }

And I inherit from it like this:

public interface IDevicesEndpoint : IAuthenticatedEndpoint
    {
        [Get("/devices")]
        Task<IList<Device>> GetAllDevices([QueryMap] IDictionary<string, object> filters);
    }

But when I try to call

RestClient.For<IDevicesEndpoint>(BASE_URL, settings); 

it throws the following exception:

((System.TypeLoadException)e.InnerException).Message "Method 'get_ApiToken' in type 'RestEase.AutoGenerated.<>SGAM+Elfec+DataAccess+WebServices+ApiEndpoints+IDevicesEndpoint' from assembly 'RestEaseFactory, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation."

with the message:

Unable to create implementation for interface SGAM.Elfec.DataAccess.WebServices.ApiEndpoints.IDevicesEndpoint. Ensure that the interface is public,or add [assembly: InternalsVisibleTo(RestClient.FactoryAssemblyName)] to your AssemblyInfo.cs

Then, I've tried to do what it suggested (added the line on AssemblyInfo) but with the same result.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.