{"id":13652595,"url":"https://github.com/jgiacomini/Tiny.RestClient","last_synced_at":"2025-04-23T03:31:00.377Z","repository":{"id":33204794,"uuid":"109750554","full_name":"jgiacomini/Tiny.RestClient","owner":"jgiacomini","description":"Simpliest Fluent REST client for .NET","archived":false,"fork":false,"pushed_at":"2024-10-17T14:04:20.000Z","size":1110,"stargazers_count":209,"open_issues_count":18,"forks_count":29,"subscribers_count":9,"default_branch":"develop","last_synced_at":"2025-04-14T12:58:35.805Z","etag":null,"topics":["csharp","csharp-library","dotnet","dotnet-core","httpclient","rest-client","uwp","xamarin"],"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/jgiacomini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-06T21:13:18.000Z","updated_at":"2025-04-10T13:56:01.000Z","dependencies_parsed_at":"2024-10-19T11:11:07.995Z","dependency_job_id":"18c7bd5c-11a9-4ea8-a801-dcb0ca5725f3","html_url":"https://github.com/jgiacomini/Tiny.RestClient","commit_stats":{"total_commits":534,"total_committers":5,"mean_commits":106.8,"dds":"0.029962546816479363","last_synced_commit":"213a8bc5e74a51d284cf2d559b42dde6736069a0"},"previous_names":["jgiacomini/tinyhttp"],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgiacomini%2FTiny.RestClient","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgiacomini%2FTiny.RestClient/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgiacomini%2FTiny.RestClient/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgiacomini%2FTiny.RestClient/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jgiacomini","download_url":"https://codeload.github.com/jgiacomini/Tiny.RestClient/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250365388,"owners_count":21418682,"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":["csharp","csharp-library","dotnet","dotnet-core","httpclient","rest-client","uwp","xamarin"],"created_at":"2024-08-02T02:01:00.793Z","updated_at":"2025-04-23T03:30:57.812Z","avatar_url":"https://github.com/jgiacomini.png","language":"C#","readme":"\u003cimg src=\"https://raw.githubusercontent.com/jgiacomini/Tiny.RestClient/master/icon.png\" width=\"200\" height=\"200\" /\u003e\n\n[![NuGet](https://img.shields.io/nuget/v/Tiny.RestClient.svg?label=NuGet)](https://www.nuget.org/packages/Tiny.RestClient/)\n[![Build status](https://ci.appveyor.com/api/projects/status/08prv6a3pon8vx86?svg=true)](https://ci.appveyor.com/project/jgiacomini/tinyhttp)\n[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/Tiny-RestClient/Lobby)\n[![StackOverflow](https://img.shields.io/badge/questions-on%20StackOverflow-orange.svg?style=flat)](http://stackoverflow.com/questions/tagged/tiny.restclient)\n\n[Please visit the main site.](https://jgiacomini.github.io/Tiny.RestClient/)\n\nTiny.RestClient facilitates the dialog between your API and your application.\nIt hides all the complexity of communication, deserialisation ...\n\n\n## Platform Support\n\nThe support of **.NET Standard 1.1 to 2.0** allows you to use it with :\n- .Net Framework 4.5+\n- Xamarin iOS, Xamarin Android\n- .Net Core\n- UWP\n- Windows Phone 8.1\n- Windows 8.1\n\n## Features\n* Modern async http client for REST API.\n* Support of verbs : GET, POST , PUT, DELETE, PATCH and custom http verbs\n* Support of ETag\n* Support of multi-part form data\n* Support of cancellation token on each requests\n* Support of : download file and Upload file\n* Automatic XML and JSON serialization / deserialization\n* Support of custom serialisation / deserialisation\n* Support of camelCase, snakeCase kebabCase for json serialization\n* Support of compression and decompression (gzip and deflate)\n* Typed exceptions which are easier to interpret\n* Define timeout globally or per request\n* Timeout exception thrown if the request is in timeout (by default HttpClient sends OperationCancelledException, so we can't distinguish between user cancellation and timeout)\n* Provide an easy way to log : all sending of request, failed to get response, and the time get response.\n* Support of export requests to postman collection\n* Support of display cURL requests in debug output\n* Support of Basic Authentification\n* Support of OAuth2 Authentification\n\n## Basic usage\n\n### Create the client\n\n```cs\nusing Tiny.RestClient;\n\nvar client = new TinyRestClient(new HttpClient(), \"http://MyAPI.com/api\");\n```\n\n### Headers\n\n#### Default header for all requests\n\n```cs\n// Add default header for each calls\nclient.Settings.DefaultHeaders.Add(\"CustomHeader\", \"Header\");\n```\n\n```cs\n// Add Auth2.0 token\nclient.Settings.DefaultHeaders.AddBearer(\"token\");\n```\n\n```cs\n// Add default basic authentication header\nclient.Settings.DefaultHeaders.AddBasicAuthentication(\"username\", \"password\");\n```\n#### Add header for current request\n\n```cs\n// Add header for this request only\nclient.GetRequest(\"City/All\").\n      AddHeader(\"CustomHeader\", \"Header\").\n      ExecuteAsync();\n```\n\n```cs\n// Add header for this request only\nclient.GetRequest(\"City/All\").\n      WithOAuthBearer(\"MYTOKEN\").\n      ExecuteAsync();\n```\n\n```cs\n// Add basic authentication for this request only\nclient.GetRequest(\"City/All\").\n      WithBasicAuthentication(\"username\", \"password\").\n      ExecuteAsync();\n```\n\n\n#### Calculate headers before send the requests\n\nBefore send requests to server we can add calculate dynamically the headers to add to resquest like below :\n```cs\nclient.Settings.CalculateHeadersHandler = async () =\u003e\n{\n   var token = await GetACustomTokenAsync();\n\n   var headers = new Headers\n   {\n       { \"CustomToken\", token },\n   };\n   return headers;\n};\n  ```\n\n#### Read headers of response\n\n```cs\nawait client.GetRequest(\"City/GetAll\").\n             FillResponseHeaders(out headersOfResponse Headers).\n             ExecuteAsync();\nforeach(var header in headersOfResponse)\n{\n    Debug.WriteLine($\"{current.Key}\");\n    foreach (var item in current.Value)\n    {\n        Debug.WriteLine(item);\n    }\n}\n```\n\n\n### Basic GET http requests\n\n```cs\nvar cities = client.GetRequest(\"City/All\").ExecuteAsync\u003cList\u003cCity\u003e\u003e();\n// GET http://MyAPI.com/api/City/All an deserialize automaticaly the content\n\n// Add a query parameter\nvar cities = client.\n    GetRequest(\"City\").\n    AddQueryParameter(\"id\", 2).\n    AddQueryParameter(\"country\", \"France\").\n    ExecuteAsync\u003cCity\u003e\u003e ();\n\n// GET http://MyAPI.com/api/City?id=2\u0026country=France deserialize automaticaly the content\n\n```\n\n### Basic POST http requests\n```cs\n// POST\n var city = new City() { Name = \"Paris\" , Country = \"France\"};\n\n// With content\nvar response = await client.PostRequest(\"City\", city).\n                ExecuteAsync\u003cbool\u003e();\n// POST http://MyAPI.com/api/City with city as content\n\n// With form url encoded data\nvar response = await client.\n                PostRequest(\"City/Add\").\n                AddFormParameter(\"country\", \"France\").\n                AddFormParameter(\"name\", \"Paris\").\n                ExecuteAsync\u003cResponse\u003e();\n// POST http://MyAPI.com/api/City/Add with from url encoded content\n\n\nvar fileInfo = new FileInfo(\"myTextFile.txt\");\nvar response = await client.\n                PostRequest(\"City/Image/Add\").\n                AddFileContent(fileInfo, \"text/plain\").\n                ExecuteAsync\u003cResponse\u003e();\n// POST text file at http://MyAPI.com/api/City/Add \n```\n\n\n### Custom Http Verb requests\n```cs\n await client.\n       NewRequest(new System.Net.Http.HttpMethod(\"HEAD\"), \"City/All\").\n       ExecuteAsync\u003cList\u003cCity\u003e\u003e();\n```\n\n### Define timeout\nDefine a global timeout for all client. (By default it's setted to 100 secondes)\n\n```cs\nclient.Settings.DefaultTimeout = TimeSpan.FromSeconds(100);\n```\n\nDefine the timeout for one request\n```cs\nrequest.WithTimeout(TimeSpan.FromSeconds(100));\n```\n### Allow non http 2xx responses\n\n#### Globaly\n\nAllow any status codes :\n```cs\nclient.Settings.HttpStatusCodeAllowed.AllowAnyStatus = true;\n```\n\nAllow only a range of http status codes :\n```cs\nclient.Settings.HttpStatusCodeAllowed.Add(new HttpStatusRange(400, 420));\n```\n\nor\n\n```cs\nclient.Settings.HttpStatusCodeAllowed.Add(new HttpStatusRange(System.Net.HttpStatusCode.BadRequest, System.Net.HttpStatusCode.BadGateway));\n```\n\n#### By request\n\nAllow all status code :\n```cs\nrequest.AllowAllHttpStatusCode().ExecuteAsync();\n```\n\nAllow only a range of http status codes :\n```cs\nrequest.AllowRangeHttpStatusCode(400, 420).ExecuteAsync();\n```\n\nAllow only on stats code of http status codes :\n```cs\nrequest.AllowSpecificHttpStatusCode(409).ExecuteAsync();\n```\n\n### Download file\n```cs\nstring filePath = \"c:\\map.pdf\";\nFileInfo fileInfo = await client.\n                GetRequest(\"City/map.pdf\").\n                DownloadFileAsync(\"c:\\map.pdf\");\n// GET http://MyAPI.com/api/City/map.pdf \n```\n\n## Get raw HttpResponseMessage\n\n```cs\nvar response = await client.\n                PostRequest(\"City/Add\").\n                AddFormParameter(\"country\", \"France\").\n                AddFormParameter(\"name\", \"Paris\").\n                ExecuteAsHttpResponseMessageAsync();\n// POST http://MyAPI.com/api/City/Add with from url encoded content\n```\n\n## Get raw string result\n\n```cs\nstring response = await client.\n                GetRequest(\"City/All\").\n                ExecuteAsStringAsync();\n// GET http://MyAPI.com/api/City/All with from url encoded content\n```\n\n## Multi-part form data\n\n```cs\n// With 2 json content\nvar city1 = new City() { Name = \"Paris\" , Country = \"France\"};\nvar city2 = new City() { Name = \"Ajaccio\" , Country = \"France\"};\nvar response = await client.NewRequest(HttpVerb.Post, \"City\").\nawait client.PostRequest(\"MultiPart/Test\").\n              AsMultiPartFromDataRequest().\n              AddContent\u003cCity\u003e(city1, \"city1\", \"city1.json\").\n              AddContent\u003cCity\u003e(city2, \"city2\", \"city2.json\").\n              ExecuteAsync();\n\n\n// With 2 byte array content\nbyte[] byteArray1 = ...\nbyte[] byteArray2 = ...           \n              \nawait client.PostRequest(\"MultiPart/Test\").\n              AsMultiPartFromDataRequest().\n              AddByteArray(byteArray1, \"request\", \"request2.bin\").\n              AddByteArray(byteArray2, \"request\", \"request2.bin\")\n              ExecuteAsync();\n  \n\n// With 2 streams content        \nStream1 stream1 = ...\nStream stream2 = ...         \nawait client.PostRequest(\"MultiPart/Test\").\n              AsMultiPartFromDataRequest().\n              AddStream(stream1, \"request\", \"request2.bin\").\n              AddStream(stream2, \"request\", \"request2.bin\")\n              ExecuteAsync();\n              \n              \n// With 2 files content           \n\nvar fileInfo1 = new FileInfo(\"myTextFile1.txt\");\nvar fileInfo2 = new FileInfo(\"myTextFile2.txt\");\n\nvar response = await client.\n                PostRequest(\"City/Image/Add\").\n                AsMultiPartFromDataRequest().\n                AddFileContent(fileInfo1, \"text/plain\").\n                AddFileContent(fileInfo2, \"text/plain\").\n                ExecuteAsync\u003cResponse\u003e();\n                \n                \n// With 2 strings content   \nvar response = await client.\n                PostRequest(\"City/Image/Text\").\n                AsMultiPartFromDataRequest().\n                AddString(\"string1\", \"text/plain\").\n                AddString(\"string2\", \"text/plain\").\n                ExecuteAsync\u003cResponse\u003e();\n\n// With mixed content                  \nawait client.PostRequest(\"Files/Add\").\n              AsMultiPartFromDataRequest().\n              AddContent\u003cCity\u003e(city1, \"city1\", \"city1.json\").\n              AddByteArray(byteArray1, \"request\", \"request2.bin\").\n              AddStream(stream2, \"request\", \"request2.bin\")\n              AddString(\"string1\", \"text\", \"request.txt\")\n              ExecuteAsync();\n```\n\n\n## String, Streams and bytes array\nYou can use as content : strings, streams or byte arrays.\nIf you use these methods no serializer will be used.\n\n### String\n```cs\n// Read string response\n Stream stream = await client.\n              GetRequest(\"text\").\n              ExecuteAsStringAsync();\n              \n// Post String as content\nawait client.PostRequest(\"poetry/text\").\n            AddStringContent(stream).\n            ExecuteAsync();\n```\n\n### Streams\n```cs\n// Read stream response\n Stream stream = await client.\n              GetRequest(\"File\").\n              ExecuteAsStreamAsync();\n// Post Stream as content\nawait client.PostRequest(\"File/Add\").\n            AddStreamContent(stream).\n            ExecuteAsync();\n```\n\n### Byte array\n```cs\n// Read byte array response         \nbyte[] byteArray = await client.\n              GetRequest(\"File\").\n              ExecuteAsByteArrayAsync();\n\n// Read byte array as content\nawait client.\n            PostRequest(\"File/Add\").\n            AddByteArrayContent(byteArray).\n            ExecuteAsync();\n```\n\n\n## Error handling\nAll requests can throw 5 exceptions : \n\n* ConnectionException : thrown when the request can't reach the server\n* HttpException : thrown when the server has invalid error code\n* SerializeException : thrown when the serializer can't serialize the content\n* DeserializeException : thrown when the deserializer can't deserialize the response\n* TimeoutException : thrown when the request take too much time to be executed\n\n### Catch a specific error code\n```cs\nstring cityName = \"Paris\";\ntry\n{ \n   var response = await client.\n     GetRequest(\"City\").\n     AddQueryParameter(\"Name\", cityName).\n     ExecuteAsync\u003cCity\u003e();\n}\ncatch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)\n{\n   throw new CityNotFoundException(cityName);\n}\ncatch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)\n{\n   throw new ServerErrorException($\"{ex.Message} {ex.ReasonPhrase}\");\n}\n```\n\n### Enclapsulate HttpExceptions\nWe can setup a global handler to provide a logic to encapsulate HttpException automatically.\n\nFor example I can choose to translate all HttpException with StatusCode NotFound in a NotFoundCustomException.\n```cs\nclient.Settings.EncapsulateHttpExceptionHandler = (ex) =\u003e\n{\n   if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)\n   {\n      return new NotFoundCustomException();\n   }\n\n   return ex;\n};\n```\n\nNow if I call an API wich respond with status code NotFound it will throw automaticaly my custom exception.\n```cs\n// Call an API wich throw NotFound error\nawait client.GetRequest(\"APIWhichNotExists\").ExecuteAsync();\n```\n## ETag\nThe lib supports the Entity tag but it's not enabled by default.\n\n### Define an ETagContainer globally\nAn implementation of IETagContainer is provided. It stores all data in multiples files.\n\nTo enable it :\n```cs\nclient.Settings.ETagContainer = new ETagFileContainer(@\"C:\\ETagFolder\");\n```\n\n### Define an ETagContainer for one request\nYou can also define the ETagContainer only on specific request.\n```cs\nrequest.WithETagContainer(eTagContainer);\n```\n\n## Formatters \n\nBy default : \n * the Json is used as default Formatter.\n * Xml Formatter is added in Formatters\n\nEach formatter has a list of supported media types.\nIt allows TinyRestClient to detect which formatter will be used.\nIf no formatter is found it uses the default formatter.\n\n### Add a new formatter\nAdd a new custom formatter as default formatter.\n```cs\nbool isDefaultFormatter = true;\nvar customFormatter = new CustomFormatter();\nclient.Settings.Formatters.Add(customFormatter, isDefaultFormatter);\n```\n\n### Remove a formatter\n```cs\nvar lastFormatter = client.Settings.Formatters.First(f =\u003e f is XmlSerializer);\nclient.Settings.Formatters.Remove(lastFormatter);\n```\n\n### Json custom formatting\n\nYou can enable 3 types of formatting on JsonFormatter :\n- CamelCase (PropertyName =\u003e propertyName)\n- SnakeCase (PropertyName =\u003e property_name)\n- KebabCase (aslo known as SpinalCase) (PropertyName =\u003e property-name).\n\n```cs\n// Enable KebabCase\n  client.Settings.Formatters.OfType\u003cJsonFormatter\u003e().First().UseKebabCase();\n```\n\n\n```cs\n// Enable CamelCase\n  client.Settings.Formatters.OfType\u003cJsonFormatter\u003e().First().UseCamelCase();\n```\n\n```cs\n// Enable SnakeCase\n  client.Settings.Formatters.OfType\u003cJsonFormatter\u003e().First().UseSkakeCase();\n```\n\n### Define a specific serialize for one request\n```cs\nIFormatter serializer = new XmlFormatter();\n var response = await client.\n     PostRequest(\"City\", city, serializer).\n     ExecuteAsync();\n```\n\n### Define a specific deserializer for one request\n```cs\nIFormatter deserializer = new XmlFormatter();\n\n var response = await client.\n     GetRequest(\"City\").\n     AddQueryParameter(\"Name\", cityName).\n     ExecuteAsync\u003cCity\u003e(deserializer);\n```\n\n### Custom formatter\n\nYou can create your own serializer/deserializer by implementing IFormatter\n\nFor example the implementation of XmlFormatter is really simple : \n```cs\npublic class XmlFormatter : IFormatter\n{\n\n   public string DefaultMediaType =\u003e \"application/xml\";\n\n   public IEnumerable\u003cstring\u003e SupportedMediaTypes\n   {\n      get\n      {\n         yield return \"application/xml\";\n         yield return \"text/xml\";\n      }\n   }\n\n   public T Deserialize\u003cT\u003e(Stream stream, Encoding encoding)\n   {\n      using (var reader = new StreamReader(stream, encoding))\n      {\n         var serializer = new XmlSerializer(typeof(T));\n         return (T)serializer.Deserialize(reader);\n      }\n   }\n\n   public string Serialize\u003cT\u003e(T data, Encoding encoding)\n   {\n         if (data == default)\n         {\n             return null;\n         }\n\n         var serializer = new XmlSerializer(data.GetType());\n         using (var stringWriter = new DynamicEncodingStringWriter(encoding))\n         {\n            serializer.Serialize(stringWriter, data);\n            return stringWriter.ToString();\n         }\n      }\n   }\n```\n## Listeners \n\nYou can easily add a listener to listen all the sent requests / responses received and all exceptions.\n\nTwo listeners are provided by the lib :\n* A debug listener : which logs all requests in debug console\n* A postman listener : which allows you to export all your requests as a postman collection\n\n\n### Debug Listener\n\nTo add a Debug listener you have to call AddDebug on Listeners property\n```cs\n\nclient.Settings.Listeners.AddDebug();\n```\n\n### cURL Listener\nTo add a cURL listener you have to call AddCurl on Listeners property\n\n```cs\nclient.Settings.Listeners.AddCurl();\n```\n\nIt produce this type of output in debug window for each ExecuteAsync called :\n```cs\ncurl -X POST \"http://localhost:4242/api/PostTest/complex\"-H \"Accept: application/json\" -H \"Content-Type: application/json\" -d \"{\\\"Id\\\":42,\\\"Data\\\":\\\"DATA\\\"}\"\n```\n\n### Postman Listener\nTo add a postman listener you have to call AddPostman on Listeners property\n```cs\nPostmanListerner postmanListener = client.Settings.Listeners.AddPostman(\"nameOfCollection\");\n```\n\nWhen you want to save the postman collection you have to call SaveAsync\n```cs\nawait postmanListener.SaveAsync(new FileInfo(\"postmanCollection.json\");\n```\n\n\nIf you only want the Json of collection you can call the method GetCollectionJson \n```cs\nlistener.GetCollectionJson();\n```\n\n### Custom Listener\nYou can also create you own listener by implementing IListener.\n\n```cs\nIListener myCustomListerner = ..\nclient.Settings.Listeners.Add(myCustomListerner);\n```\n\n## Compression and Decompression\nBy default, the client supports the decompression of Gzip and deflate.\n\n\nIf the server respond with the header ContentEncoding \"gzip\" or \"deflate\" the client will decompress it automaticly.\n\n### Compression\nFor each request which posts a content you can specified the compression algorithm like below\n```cs\nvar response = await client.\n                PostRequest(\"Gzip/complex\", postRequest, compression: client.Settings.Compressions[\"gzip\"]).\n                ExecuteAsync\u003cResponse\u003e();\n```\nWarning : the server must be able to decompress your content.\n\n### Decompression\nEven if it's supported the client didn't send Accept-Encoding header automaticaly.\n\nYou can add it for gzip all request like below :\n```cs\nvar compression = client.Settings.Compressions[\"gzip\"];\ncompression.AddAcceptEncodingHeader = true;\n```\n\nYou can add it for deflate all requests like below :\n```cs\nvar compression = client.Settings.Compressions[\"deflate\"];\ncompression.AddAcceptEncodingHeader = true;\n```\n\nIf the server can compress response, it will respond with compressed content.\n\n### Custom ICompression\nYou can add your own compression / decompression algorithm :\n```cs\nclient.Settings.Add(new CustomCompression());\n```\nYour class must implement the interface ICompression.\n","funding_links":[],"categories":["Clients","Network","HTTP","C# #"],"sub_categories":[".NET Clients"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgiacomini%2FTiny.RestClient","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjgiacomini%2FTiny.RestClient","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgiacomini%2FTiny.RestClient/lists"}