{"id":13429529,"url":"https://github.com/letsar/RestLess","last_synced_at":"2025-03-16T03:31:49.214Z","repository":{"id":37952469,"uuid":"100587747","full_name":"letsar/RestLess","owner":"letsar","description":"The automatic type-safe-reflectionless REST API client library for .Net Standard","archived":false,"fork":false,"pushed_at":"2022-12-07T19:40:34.000Z","size":312,"stargazers_count":111,"open_issues_count":8,"forks_count":11,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-03T11:14:54.462Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/letsar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-17T09:42:02.000Z","updated_at":"2024-09-16T14:36:26.000Z","dependencies_parsed_at":"2023-01-24T22:00:15.235Z","dependency_job_id":null,"html_url":"https://github.com/letsar/RestLess","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2FRestLess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2FRestLess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2FRestLess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2FRestLess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/letsar","download_url":"https://codeload.github.com/letsar/RestLess/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243822309,"owners_count":20353496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T02:00:41.329Z","updated_at":"2025-03-16T03:31:48.829Z","avatar_url":"https://github.com/letsar.png","language":"C#","readme":"# RestLess\n\n**RestLess** is another type-safe REST API client library.\nIt is heavily inspired by [Refit](https://github.com/paulcbetts/refit), which is inspired by [Retrofit](http://square.github.io/retrofit), but does not works exactly the same way.\n\nIn fact **RestLess** is built by keeping in mind that reflection is slow. When we build beautiful apps we don't want them to be slow down because of an external library, that's why all the **RestLess** REST clients are fully generated during the compilation.\n\nThis library fully supports the uri template defined by the [RFC6570](http://tools.ietf.org/html/rfc6570) thanks to [DoLess.UriTemplates](https://github.com/letsar/DoLess.UriTemplates).\n\n## Performance comparison\n\nThe main goal of **RestLess** is to be fast. So I created a little benchmark project that can be run on Android.\nI benchmarked Refit against **RestLess** for now, on two devices (time is in ms):\n\n### Device 1\n\n[Orange Nura](https://www.gsmmachine.com/orange-nura-2485.html)\n\nThis is an old device running under Android 4.4.\n\n![benchmark-nura](docs/images/benchmark_nura.png)\n\n### Device 2\n\n[Samsung Galaxy S8](https://www.gsmmachine.com/samsung-galaxy-s8-8439.html)\n\nSamsung 2017 flagship runnind under Android 7.0.\n\n![benchmark-s8](docs/images/benchmark_s8.png)\n\n### Conclusion\n\n**RestLess** is really fast, especially at startup time. On an old device, with Refit, the startup time is more than one second while **RestLess** is 9 times faster with 120ms!\nOn the request time side, **RestLess** is faster thant Refit, but I don't think this is very relevant since the network will be the bottleneck.\n\n## How it works\n\nInstall the NuGet package called **RestLess** and one of the extra package (like **RestLess.JsonNet**) into your project.\nA **RestLess** folder with a file named RestClient.g.dl.rest.cs will be inserted in your project.\nThis file will allow you to create the Rest client from your interface without reflection.\n\nAs in Refit, you have to create an interface representing your REST API and use attributes to indicate what to do.\nDuring the project compilation, all REST clients will be generated.\n\n**Warning**: In Visual Studio for Mac, with versions prior to 0.7.1, the intellisense does not detect `RestClient`, but it will compile nonetheless.\nFrom 0.7.1, you will have to close Visual Studio for Mac after adding this package and open it again. This is because Visual Studio for Mac keeps some project properties in cache.\n\n## Install\n\nAvailable on NuGet.\n\nInstall **RestLess**\n\n[![NuGet](https://img.shields.io/nuget/v/RestLess.svg?label=NuGet)](https://www.nuget.org/packages/RestLess/)\n\nInstall **RestLess.JsonNet** if you want to serialize/deserialize using [Json.Net](https://github.com/JamesNK/Newtonsoft.Json)\n\n[![NuGet](https://img.shields.io/nuget/v/RestLess.JsonNet.svg?label=NuGet)](https://www.nuget.org/packages/RestLess.JsonNet/)\n\n## Quick start\n\n### 1°) Create your REST API interface\n\n```csharp\n[Header(\"User-Agent\", \"RestLess\")]\npublic interface IGitHubApi\n{\n    [Get(\"users{/userId}\")]\n    Task\u003cUser\u003e GetUserAsync(string userId);\n}\n```\n\n### 2°) Get the actual REST client\n\n```csharp\nIGitHubApi gitHubApi = RestClient.For\u003cIGitHubApi\u003e(\"https://api.github.com\");\n```\n\n### 3°) Make the call\n\n```csharp\nUser user = await gitHubApi.GetUserAsync(\"lestar\");\n```\n\n## REST client customization\n\nYou can change the way the API works with two things: The ```HttpClient``` that can be passed to the ```For``` method, or with the ```RestSettings```\n\n### HttpClient\n\nThe ```For``` method accepts a ```HttpClient```, so you can initialize it with a ```HttpMessageHandler```.\nWith this, you can set default headers with a runtime value that will be the same accross all calls. You can also create a ```DelegatingHandler``` for authentification, an other one for logging, etc.\n\n### RestSettings\n\nThese settings are used directly by the requests generated by **RestLess**.\n\nYou can set custom parameters or formatters but the default ```RestSettings``` does not come with useful formatters (Because we don't want the core package to have a lot of dependencies).\n\nFor example, if you want to serialize/deserialize JSON content with [Json.Net](https://github.com/JamesNK/Newtonsoft.Json) you'll have to get the **RestLess.JsonNet** NuGet package.\nOf course, you can write your own, if what you are looking for does not yet exists.\n\n#### Custom parameters\n\nCustom parameters can be set at runtime but they are common to the entire REST client. These parameters can be used inside ```HeaderAttributes``` (set the ```isCustomParameter``` parameter to true) or inside the uri templates defined into the [HTTP Method attributes](#http-method-attributes)\n\n```csharp\nsettings.CustomParameters.Add(\"api_key\", \"bXlhcGlrZXk=\");\n```\n\n#### Formatters\n\nThe formatters can change the way an object is serialized/deserialized. There must be a default formatter for each kind of formatter.\n\nAs opposed to **Refit** you can use a specific formatter for a method and use the default one for the others.\n\n##### MediaTypeFormatters\n\nThese formatters are used to serialize/deserialize the body of a HTTP request/response from/into an object.\n\nYou can set a method-specific MediaTypeFormatter like this:\n\n```csharp\nRestSettings restSettings = new RestSettings();\nrestSettings.MediaTypeFormatters.Set(\"MediaTypeJsonFormatter\", new MyMediaTypeJsonFormatter());\n\n...\n\n[Get(\"/whatever\")]\n[MediaTypeFormatter(\"MediaTypeJsonFormatter\")]\nTask\u003cHttpResponseMessage\u003e GetAsync();\n```\n\n##### UrlParameterFormatters\n\nThese formatters are used to serialize an object into a string. This string will be used to expand the variable in the uri template.\n\nYou can set a method-specific UrlParameterFormatter like this:\n\n```csharp\nRestSettings restSettings = new RestSettings();\nrestSettings.UrlParameterFormatters.Set(\"UrlParameterFormatter\", new MyUrlParameterFormatter());\n\n...\n\n[Get(\"/whatever{?obj}\")]\n[UrlParameterFormatter(\"UrlParameterFormatter\")]\nTask\u003cHttpResponseMessage\u003e GetAsync(MyObject obj);\n```\n\n##### FormFormatters\n\nThese formatters are used to serialize an object into a ```IEnumerable\u003cKeyValuePair\u003cstring,string\u003e\u003e``` that will be used to create an encoded url form content.\n\nYou can set a method-specific FormFormatter like this:\n\n```csharp\nRestSettings restSettings = new RestSettings();\nrestSettings.FormFormatters.Set(\"FormFormatter\", new MyFormFormatterFormatter());\n\n...\n\n[Post(\"/whatever\")]\n[FormFormatter(\"FormFormatter\")]\nTask\u003cHttpResponseMessage\u003e PostAsync([FormUrlEncodedContent] MyObject obj);\n```\n\n## Attributes\n\n### Setting the Http Method\n\nIn order to be identified as a REST interface, all methods of the interface must have a **HTTP Method attribute** that provides the request method and relative URL.\n\nThere are 8 built-in attributes: **Delete**, **Get**, **Head**, **Options**, **Patch**, **Post**, **Put** and **Trace**.\nThe relative URL of the resource must be specified as the argument of this attribute and it have to respect the [RFC6570 Uri template specification](http://tools.ietf.org/html/rfc6570).\n\n*Note: You can use a literal string, or any expression (like a constant) as the attribute argument.*\n\n```csharp\n[Get(\"/users{/userId}\")]\n[Get(\"/users{?since}\")]\n```\n\nA request URL can be updated dynamically with the values of the method parameters or through the *CustomParameters* of the ```RestSettings```class.\nThe parameter names are **case-insensitive**, so it will work correctly in this case:\n\n```csharp\n[Get(\"/users{/userid}\")]\nTask\u003cHttpResponseMessage\u003e GetUserAsync(string userId);\n```\n\nYou can use the ```Name``` attribute to override the name of the parameter that will be used by the UriTemplate:\n\n```csharp\n[Get(\"/users{/userId}\")]\nTask\u003cHttpResponseMessage\u003e GetUserAsync([Name(\"userId\")] string id);\n```\n\n### Setting global request uri prefix or suffix\n\nIf the REST API always starts with a common string, let's say a version number for example, you can put it into a ```UriTemplatePrefixAttribute```\nAlternatively you can do the same if you REST API always ends with the same string, using the ```UriTemplateSuffixAttribute```\n\n### Setting headers\n\nThere are three ways to set a header value:\n\n#### With a constant\n\nIf you have a header with a constant value you can use the ```HeaderAttribute``` that way:\n\n```csharp\n[Header(\"User-Agent\", \"AppAgentForAllMethods\")]\npublic interface IRestApi\n{\n    [Header(\"User-Agent\", \"AppAgentForThisMethodOnly\")]\n    Task\u003cUser\u003e GetUserAsync();\n    // .. Some code...\n}\n```\n\n#### At runtime through the settings\n\nYou can also set a global header value at runtime for all the methods of your REST client:\n\n```csharp\nRestSettings restSettings = new RestSettings();\nrestSettings.CustomParameters.Add(\"apiKey\", ApiKey);\n\n...\n\n[Header(\"User-Agent\", \"apiKey\", true)]\npublic interface IRestApi\n{\n    // .. Some code...\n}\n```\n\n#### Dynamically\n\nIf the content of the header can change between calls, you can apply a ```HeaderValueAttribute``` to a parameter:\n\n```csharp\n[Get(\"api/posts\")]\nTask\u003cUser\u003e GetUserAsync([HeaderValue(\"User-Token\")] string token);\n```\n\n### Overwrite header values\n\nRedefining a header will replace it in the following order of precedence:\n\n* ```Header``` attribute on the interface (**lowest priority**)\n* ```Header``` attribute on the method\n* ```HeaderValue``` attribute on a method parameter (**highest priority**)\n\n## Setting the body content\n\nYou can add a body content to your request by applying the ```Content``` or the ```FormUrlEncodedContent``` attribute to a method parameter.\n\nWith the ```Content``` attribute, the parameter can have one the following types:\n\n* ```HttpContent```\n* ```Stream```\n* ```string```\n* ```byte[]```\n* ```object```   =\u003e This will use the specified *MediaTypeFormatter* (or the default one if not set)\n* ```FileInfo``` =\u003e This trigger creates a ```MultipartFormDataContent``` even if it is the only parameter\n\nWith the ```FormUrlEncodedContent``` attribute, the parameter can have one the following types:\n\n* ```IEnumerable\u003cKeyValuePair\u003cstring, string\u003e\u003e```\n* ```object```   =\u003e This will use the specified *FormFormatter* (or the default one if not set)\n\nYou can decorate multiple parameters with the ```Content``` or the ```FormUrlEncodedContent``` attribute. The body of the request will be a ```MultipartFormDataContent```.\nWhen you want to create a multipart request, the default name for each parameter will be the parameter name. You can ovveride this behavior with the ```Name``` attribute.\nYou  can also set an optional fileName or contentType through the attribute parameters.\n\nExample:\n\n```csharp\n[Post(\"api/posts\")]\nTask\u003cHttpResponseMessage\u003e PostMultipartContent03Async([Content]string content, [Name(\"firstName\")][Content(\"f\", \"text/plain\")]string content2);\n```\n\nWill create a content like this:\n\n```HTTP\n--RestLessBoundary\nContent-Type: text/plain; charset=utf-8\nContent-Disposition: form-data; name=content\n\ndoe\n--RestLessBoundary\nContent-Type: text/plain\nContent-Disposition: form-data; name=firstName; filename=f; filename*=utf-8''f\n\njohn\n--RestLessBoundary--\n```\n\n## Retrieving the response\n\nAll the REST client methods must return a ```Task```.\n\nYou can set a generic parameter type to the ```Task```, the valid ones are:\n\n* ```HttpResponseMessage```\n* ```Stream```\n* ```string```\n* ```bool```     =\u003e This will return if the response has a success code without throwing an error\n* ```byte[]```\n* ```object```   =\u003e This will use the specified *MediaTypeFormatter* (or the default one if not set)\n\n## Get response header\n\nSometimes REST APIS return useful information in the headers of the response.\nYou can get them by setting the `HeaderWriter`property of the `RestSettings`:\n\n```csharp\npublic class HeaderWriter : IHeaderWriter\n{\n    public void Write(HttpResponseHeaders headers, object obj)\n    {\n        if (obj is IPagedResponse pagedResponse)\n        {\n            if (headers.TryGetValue(PaginationPage, out int page))\n            {\n                pagedResponse.Page = page;\n            }\n\n            if (headers.TryGetValue(PaginationPageCount, out int pageCount))\n            {\n                pagedResponse.PageCount = pageCount;\n            }\n        }\n    }\n}\n\n...\n\nmockHttp.Expect(HttpMethod.Get, url)\n        .Respond(x =\u003e\n        {\n            var response = new HttpResponseMessage(HttpStatusCode.OK)\n            {\n                Content = new StringContent(\"[{'firstName':'A','lastName':'AA'},{'firstName':'B','lastName':'BB'}]\", Encoding.UTF8, \"application/json\")\n            };\n\n            response.Headers.Add(PaginationPage, \"1\");\n            response.Headers.Add(PaginationPageCount, \"2\");\n\n            return response;\n        });\n\nvar settings = new JsonRestSettings()\n{\n    HttpMessageHandlerFactory = () =\u003e mockHttp,\n    HeaderWriter = new HeaderWriter()\n};\n\nIApi09 restClient = RestClient.For\u003cIApi09\u003e(url, settings);\nvar people = await restClient.GetPagedPeopleAsync();\n\npeople.Page.ShouldBeEquivalentTo(1);\npeople.PageCount.ShouldBeEquivalentTo(2);\n```\n\n## Differences with Refit\n\nUnlike Refit, the core of **RestLess** does not use reflection at runtime (For MediaFormatters it depends of the implementation). All the REST methods are generated during compile-time.\n\nThe *RestLess* package does not have any dependencies to another third-party library (except *DoLess.UriTemplates*). In order to read/write Json, you need to reference *RestLess.JsonNet* for example, but you can also write your own formatting strategies.\n\n**RestLess** supports:\n\n* Generic methods\n* Method polymorphism\n* The use of constants inside the attributes\n* UriTemplates: see the [Spec](http://tools.ietf.org/html/rfc6570)\n* Method specific formatters\n* Getting response headers in the returned object\n\n## Not supported features\n\nRight now, **RestLess** does not supports:\n\n* Interface inheritance\n","funding_links":[],"categories":["Frameworks, Libraries and Tools","others","Clients","Network","HTTP","框架, 库和工具","API","C# #"],"sub_categories":["API",".NET Clients"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletsar%2FRestLess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fletsar%2FRestLess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletsar%2FRestLess/lists"}