From c6c8b2f85b679d65d0de5cc44700fc167f045e81 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Tue, 25 Jun 2024 17:28:23 +0200 Subject: [PATCH] Arrange versioned docs --- docs/docs/changelog.md | 32 +- docs/docusaurus.config.ts | 203 +++--- docs/versioned_docs/version-v110/changelog.md | 35 +- .../version-v111/advanced/configuration.md | 4 +- docs/versioned_docs/version-v111/changelog.md | 26 +- docs/versioned_docs/version-v111/intro.md | 2 +- .../version-v111/usage/basics.md | 182 +++++ .../version-v111/usage/client.md | 60 ++ .../version-v111/usage/example.md | 8 +- .../version-v111/usage/execute.md | 143 ++++ .../version-v111/usage/request.md | 291 ++++++++ .../version-v111/usage/usage.md | 620 ------------------ 12 files changed, 825 insertions(+), 781 deletions(-) create mode 100644 docs/versioned_docs/version-v111/usage/basics.md create mode 100644 docs/versioned_docs/version-v111/usage/client.md create mode 100644 docs/versioned_docs/version-v111/usage/execute.md create mode 100644 docs/versioned_docs/version-v111/usage/request.md delete mode 100644 docs/versioned_docs/version-v111/usage/usage.md diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md index 93790fb34..74a7f0552 100644 --- a/docs/docs/changelog.md +++ b/docs/docs/changelog.md @@ -1,37 +1,11 @@ --- title: What's new -description: List of changes per RestSharp version. +description: List of changes for the current major version sidebar_position: 2 --- # Changelog -This changelog is only maintained since v111. For release notes of previous versions, please check the [Releases page](https://github.com/restsharp/RestSharp/releases) in RestSharp GitHub repository. +For release notes of previous versions, please check the [Releases page](https://github.com/restsharp/RestSharp/releases) in RestSharp GitHub repository. -Only the most important or breaking changes are listed there. All other changes can be found in each release on GitHub. - -## v111.3 - -New extensions: -* `RestResponse.GetHeader` for getting one response header value -* `RestResponse.GetHeaders` for getting a collection of header values -* `IRestClient.(Execute)Get(Async)` with string resource instead of request object -* `IRestClient.(Execute)Delete(Async)` with string resource instead of request object - -## v111.2 - -* `Execute` extensions that were accidentally removed from v111 are back -* Several authenticators got renamed by unintentional refactoring, that change has also been reverted. - -## v111.0 - -> The package for v111.0 is listed as unsupported on NuGet as it has API changes that weren't planned. Use the patched version v111.2 or later. - -* Added [interceptors](advanced/interceptors.md). -* As interceptors provide a better way to interject the request and response execution flow, request properties `OnBeforeRequest`, `OnBeforeDeserialization` and `OnAfterRequest` are marked obsolete and will be removed in future versions. -* **Breaking change.** Client option `MaxTimeout` renamed to `Timeout` and changed type to `Timespan` for clarity. It doesn't configure the `HttpClient` timeout anymore. Instead, the same method is used for client and request level timeouts with cancellation tokens. -* **Breaking change.** Request option `Timeout` changed type to `Timespan` for clarity. -* Added .NET 8 target. -* Support uploading files as content without multipart form. -* Added `CacheControl` options to client and requests. -* Allow using `AddJsonBody` to serialize top-level strings. +Changes between major versions are documented in the documentation for each version on this website. \ No newline at end of file diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 0aa88604f..65bfe7570 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -3,108 +3,119 @@ import type * as Preset from "@docusaurus/preset-classic"; import {themes} from "prism-react-renderer"; const config: Config = { - title: "RestSharp", - tagline: "Simple REST and HTTP API Client for .NET", - favicon: "img/favicon.ico", - url: "https://restsharp.dev", - baseUrl: "/", - onBrokenLinks: "throw", - onBrokenMarkdownLinks: "warn", - i18n: { - defaultLocale: "en", - locales: ["en"], - }, - - presets: [ - [ - "classic", - { - docs: { - sidebarPath: "./sidebars.ts", - }, - theme: { - customCss: "./src/css/custom.css", - }, - } satisfies Preset.Options, - ], - ], - - themeConfig: { - navbar: { - title: "RestSharp", - logo: { - alt: "RestSharp Logo", - src: "img/restsharp.png", - }, - items: [ - { - type: "docSidebar", - sidebarId: "tutorialSidebar", - position: "left", - label: "Documentation", - }, - { - href: "/migration", - label: "Migration" - }, - { - href: "/support", - label: "Support", - }, - { - href: 'https://github.com/RestSharp/RestSharp', - label: "GitHub", - position: "right", - }, - ], + title: "RestSharp", + tagline: "Simple REST and HTTP API Client for .NET", + favicon: "img/favicon.ico", + url: "https://restsharp.dev", + baseUrl: "/", + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", + i18n: { + defaultLocale: "en", + locales: ["en"], }, - footer: { - style: "dark", - links: [ - { - title: "Docs", - items: [ - { - label: "Documentation", - to: "/docs/intro", - }, - ], - }, - { - title: "Community", - items: [ - { - label: "Stack Overflow", - href: "https://stackoverflow.com/questions/tagged/restsharp", + + presets: [[ + "classic", { + docs: { + editUrl: "https://github.com/RestSharp/RestSharp/tree/dev/docs", + sidebarPath: "./sidebars.ts", + includeCurrentVersion: true, + versions: { + "v111": { + label: "v111" + }, + "v110": { + label: "v110" + } + } }, - { - label: "Discord", - href: "https://discordapp.com/invite/docusaurus", + theme: { + customCss: "./src/css/custom.css", }, - { - label: "Twitter", - href: "https://twitter.com/RestSharp", + } satisfies Preset.Options, + ]], + + themeConfig: { + navbar: { + title: "RestSharp", + logo: { + alt: "RestSharp Logo", + src: "img/restsharp.png", }, - ], + items: [ + { + type: "docSidebar", + sidebarId: "tutorialSidebar", + position: "left", + label: "Documentation", + }, + { + href: "/migration", + label: "Migration from v106" + }, + { + href: "/support", + label: "Support", + }, + { + type: "docsVersionDropdown", + position: "right", + }, + { + href: 'https://github.com/RestSharp/RestSharp', + label: "GitHub", + position: "right", + }, + ], }, - { - title: "More", - items: [ - { - label: "GitHub", - href: "https://github.com/RestSharp/RestSharp", - }, - ], + footer: { + style: "dark", + links: [ + { + title: "Docs", + items: [ + { + label: "Documentation", + to: "/docs/intro", + }, + ], + }, + { + title: "Community", + items: [ + { + label: "Stack Overflow", + href: "https://stackoverflow.com/questions/tagged/restsharp", + }, + { + label: "Discord", + href: "https://discordapp.com/invite/docusaurus", + }, + { + label: "Twitter", + href: "https://twitter.com/RestSharp", + }, + ], + }, + { + title: "More", + items: [ + { + label: "GitHub", + href: "https://github.com/RestSharp/RestSharp", + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} .NET Foundation. Built with Docusaurus.`, }, - ], - copyright: `Copyright © ${new Date().getFullYear()} .NET Foundation. Built with Docusaurus.`, - }, - prism: { - theme: themes.vsLight, - darkTheme: themes.vsDark, - additionalLanguages: ['csharp'], - }, - } satisfies Preset.ThemeConfig, + prism: { + theme: themes.vsLight, + darkTheme: themes.vsDark, + additionalLanguages: ['csharp'], + }, + } satisfies Preset.ThemeConfig, }; export default config; diff --git a/docs/versioned_docs/version-v110/changelog.md b/docs/versioned_docs/version-v110/changelog.md index 93790fb34..aff67b9e1 100644 --- a/docs/versioned_docs/version-v110/changelog.md +++ b/docs/versioned_docs/version-v110/changelog.md @@ -1,37 +1,24 @@ --- title: What's new -description: List of changes per RestSharp version. +description: List of changes for the current major version sidebar_position: 2 --- # Changelog -This changelog is only maintained since v111. For release notes of previous versions, please check the [Releases page](https://github.com/restsharp/RestSharp/releases) in RestSharp GitHub repository. +For release notes of previous versions, please check the [Releases page](https://github.com/restsharp/RestSharp/releases) in RestSharp GitHub repository. -Only the most important or breaking changes are listed there. All other changes can be found in each release on GitHub. +## What's Changed +* Added default parameters to the request. They got missing somehow. +* Consider the boundary quotes request option value. +* Made `BuildUrl` an extension so it can be used publicly. +* Added client-level cookie container. -## v111.3 +## Breaking change -New extensions: -* `RestResponse.GetHeader` for getting one response header value -* `RestResponse.GetHeaders` for getting a collection of header values -* `IRestClient.(Execute)Get(Async)` with string resource instead of request object -* `IRestClient.(Execute)Delete(Async)` with string resource instead of request object +The `IRestClient` interface signature is different, so any non-standard implementations need to adopt the changes. -## v111.2 +To keep `DefaultParameters` thread-safe, it got a new type `DefaultParameters`, and request property `Parameters` has a dedicated type `RequestParameter`. Code-wise the change is non-breaking as the signatures are the same, but v110 is not binary compatible with previous versions. The difference is that `DefaultParameters` collection wraps all its mutations in a lock. -* `Execute` extensions that were accidentally removed from v111 are back -* Several authenticators got renamed by unintentional refactoring, that change has also been reverted. +**Full Changelog**: https://github.com/restsharp/RestSharp/compare/109.0.1...110.0.0 -## v111.0 - -> The package for v111.0 is listed as unsupported on NuGet as it has API changes that weren't planned. Use the patched version v111.2 or later. - -* Added [interceptors](advanced/interceptors.md). -* As interceptors provide a better way to interject the request and response execution flow, request properties `OnBeforeRequest`, `OnBeforeDeserialization` and `OnAfterRequest` are marked obsolete and will be removed in future versions. -* **Breaking change.** Client option `MaxTimeout` renamed to `Timeout` and changed type to `Timespan` for clarity. It doesn't configure the `HttpClient` timeout anymore. Instead, the same method is used for client and request level timeouts with cancellation tokens. -* **Breaking change.** Request option `Timeout` changed type to `Timespan` for clarity. -* Added .NET 8 target. -* Support uploading files as content without multipart form. -* Added `CacheControl` options to client and requests. -* Allow using `AddJsonBody` to serialize top-level strings. diff --git a/docs/versioned_docs/version-v111/advanced/configuration.md b/docs/versioned_docs/version-v111/advanced/configuration.md index 95d506fc8..3c5341276 100644 --- a/docs/versioned_docs/version-v111/advanced/configuration.md +++ b/docs/versioned_docs/version-v111/advanced/configuration.md @@ -28,7 +28,7 @@ Constructor parameters are: | options | Client options | Yes | | configureDefaultHeaders | Function to configure headers. Allows to configure default headers for `HttpClient`. Most of the time you'd prefer using `client.AddDefaultHeader` instead. | No | | configureSerialization | Function to configure client serializers with non-default options or to use a different serializer ([learn more](serialization.md)) | No | -| useClientFactory | Instructs the client to use `SimpleFactory` ([learn more](../usage/usage.md#simple-factory)) to get an `HttpClient` instance | No | +| useClientFactory | Instructs the client to use `SimpleFactory` ([learn more](../usage/basics#simple-factory)) to get an `HttpClient` instance | No | Here's an example of how to create a client using client options: @@ -227,4 +227,4 @@ Client options apply to all requests made by the client. Sometimes, you want to | `AdvancedResponseWriter` | Allows custom handling of the response. The function gets an instance of `HttpResponseMessage` and an instance of `RestRequest`. It must return an instance of `RestResponse`, so it effectively overrides RestSharp default functionality for creating responses. | | `Interceptors` | Allows adding interceptors to the request. Both client-level and request-level interceptors will be called. | -The table below contains all configuration properties of `RestRequest`. To learn more about adding request parameters, check the [usage page](../usage/usage.md#create-a-request) section about creating requests with parameters. +The table below contains all configuration properties of `RestRequest`. To learn more about adding request parameters, check the [usage page](../usage/basics#create-a-request) section about creating requests with parameters. diff --git a/docs/versioned_docs/version-v111/changelog.md b/docs/versioned_docs/version-v111/changelog.md index 93790fb34..3fc2ca63f 100644 --- a/docs/versioned_docs/version-v111/changelog.md +++ b/docs/versioned_docs/version-v111/changelog.md @@ -1,6 +1,6 @@ --- title: What's new -description: List of changes per RestSharp version. +description: List of changes for the current major version sidebar_position: 2 --- @@ -15,23 +15,35 @@ Only the most important or breaking changes are listed there. All other changes New extensions: * `RestResponse.GetHeader` for getting one response header value * `RestResponse.GetHeaders` for getting a collection of header values -* `IRestClient.(Execute)Get(Async)` with string resource instead of request object -* `IRestClient.(Execute)Delete(Async)` with string resource instead of request object +* `IRestClient.(Execute)Get(Async)` with string resource instead of a request object +* `IRestClient.(Execute)Delete(Async)` with string resource instead of a request object + +[Full changelog](https://github.com/restsharp/RestSharp/releases/tag/111.3.0) ## v111.2 * `Execute` extensions that were accidentally removed from v111 are back * Several authenticators got renamed by unintentional refactoring, that change has also been reverted. +[Full changelog](https://github.com/restsharp/RestSharp/releases/tag/111.2.0) + ## v111.0 -> The package for v111.0 is listed as unsupported on NuGet as it has API changes that weren't planned. Use the patched version v111.2 or later. +:::warning +Package for v111.0 and v111.1 are listed as unsupported on NuGet as there are API changes that weren't planned. +Use the patched version v111.2 or later. +::: +### Breaking changes +* Client option `MaxTimeout` renamed to `Timeout` and changed type to `Timespan` for clarity. It doesn't configure the `HttpClient` timeout anymore. Instead, the same method is used for client and request level timeouts with cancellation tokens. +* Request option `Timeout` changed type to `Timespan` for clarity. + +### New features * Added [interceptors](advanced/interceptors.md). * As interceptors provide a better way to interject the request and response execution flow, request properties `OnBeforeRequest`, `OnBeforeDeserialization` and `OnAfterRequest` are marked obsolete and will be removed in future versions. -* **Breaking change.** Client option `MaxTimeout` renamed to `Timeout` and changed type to `Timespan` for clarity. It doesn't configure the `HttpClient` timeout anymore. Instead, the same method is used for client and request level timeouts with cancellation tokens. -* **Breaking change.** Request option `Timeout` changed type to `Timespan` for clarity. * Added .NET 8 target. -* Support uploading files as content without multipart form. +* Support for uploading files as content without multipart form. * Added `CacheControl` options to client and requests. * Allow using `AddJsonBody` to serialize top-level strings. + +[Full changelog](https://github.com/restsharp/RestSharp/releases/tag/111.0.0) \ No newline at end of file diff --git a/docs/versioned_docs/version-v111/intro.md b/docs/versioned_docs/version-v111/intro.md index fd64f02e9..a63d4223e 100644 --- a/docs/versioned_docs/version-v111/intro.md +++ b/docs/versioned_docs/version-v111/intro.md @@ -74,7 +74,7 @@ var client = new RestClient(options); var timeline = await client.GetJsonAsync("statuses/home_timeline.json", cancellationToken); ``` -Read [here](usage/usage.md#json-requests) about making JSON calls without preparing a request object. +Read [here](usage/basics#json-requests) about making JSON calls without preparing a request object. ### Content type diff --git a/docs/versioned_docs/version-v111/usage/basics.md b/docs/versioned_docs/version-v111/usage/basics.md new file mode 100644 index 000000000..0c0005462 --- /dev/null +++ b/docs/versioned_docs/version-v111/usage/basics.md @@ -0,0 +1,182 @@ +--- +sidebar_position: 2 +--- + +# RestSharp basics + +This page describes some of the essential properties and features of RestSharp. + +## What RestSharp does + +Essentially, RestSharp is a wrapper around `HttpClient` that allows you to do the following: +- Add default parameters of any kind (not just headers) to the client, once +- Add parameters of any kind to each request (query, URL segment, form, attachment, serialized body, header) in a straightforward way +- Serialize the payload to JSON or XML if necessary +- Set the correct content headers (content type, disposition, length, etc.) +- Handle the remote endpoint response +- Deserialize the response from JSON or XML if necessary + +## API client + +The best way to call an external HTTP API is to create a typed client, which encapsulates RestSharp calls and doesn't expose the `RestClient` instance in public. + +You can find an example of a Twitter API client on the [Example](example.md) page. + + +## Handling responses + +All `Execute{Method}Async` functions return an instance of `RestResponse`. Similarly, `Execute{Method}Async` return a generic instance of `RestResponse` where `T` is the response object type. + +Response object contains the following properties: + +| Property | Type | Description | +|--------------------------|-----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| `Request` | `RestRequest` | Request instance that was used to get the response. | +| `ContentType` | `string?` | Response content type. `Null` if response has no content. | +| `ContentLength` | `long?` | Response content length. `Null` if response has no content. | +| `ContentEncoding` | `ICollection` | Content encoding collection. Empty if response has no content. | +| `Content` | `string?` | Response content as string. `Null` if response has no content. | +| `IsSuccessfulStatusCode` | `bool` | Indicates if response was successful, so no errors were reported by the server. Note that `404` response code means success. | +| `ResponseStatus` | `None`, `Completed`, `Error`, `TimedOut`, `Aborted` | Response completion status. Note that completed responses might still return errors. | +| `IsSuccessful` | `bool` | `True` when `IsSuccessfulStatusCode` is `true` and `ResponseStatus` is `Completed`. | +| `StatusDescription` | `string?` | Response status description, if available. | +| `RawBytes` | `byte[]?` | Response content as byte array. `Null` if response has no content. | +| `ResponseUri` | `Uri?` | URI of the response, which might be different from request URI in case of redirects. | +| `Server` | `string?` | Server header value of the response. | +| `Cookies` | `CookieCollection?` | Collection of cookies received with the response, if any. | +| `Headers` | Collection of `HeaderParameter` | Response headers. | +| `ContentHeaders` | Collection of `HeaderParameter` | Response content headers. | +| `ErrorMessage` | `string?` | Transport or another non-HTTP error generated while attempting request. | +| `ErrorException` | `Exception?` | Exception thrown when executing the request, if any. | +| `Version` | `Version?` | HTTP protocol version of the request. | +| `RootElement` | `string?` | Root element of the serialized response content, only works if deserializer supports it. | + +In addition, `RestResponse` has one additional property: + +| Property | Type | Description | +|----------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Data` | `T?` | Deserialized response object. `Null` if there's no content in the response, deserializer failed to understand the response content, or if request failed. | + +### JSON streaming APIs + +For HTTP API endpoints that stream the response data (like [Twitter search stream](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream)) you can use RestSharp with `StreamJsonAsync`, which returns an `IAsyncEnumerable`: + +```csharp +public async IAsyncEnumerable SearchStream( + [EnumeratorCancellation] CancellationToken cancellationToken = default +) { + var response = _client.StreamJsonAsync>( + "tweets/search/stream", cancellationToken + ); + + await foreach (var item in response.WithCancellation(cancellationToken)) { + yield return item.Data; + } +} +``` + +The main limitation of this function is that it expects each JSON object to be returned as a single line. It is unable to parse the response by combining multiple lines into a JSON string. + +### Uploading files + +To add a file to the request you can use the `RestRequest` function called `AddFile`. The main function accepts the `FileParameter` argument: + +```csharp +request.AddFile(fileParameter); +``` + +You can instantiate the file parameter using `FileParameter.Create` that accepts a bytes array, or `FileParameter.FromFile`, which will load the file from disk. + +There are also extension functions that wrap the creation of `FileParameter` inside: + +```csharp +// Adds a file from disk +AddFile(parameterName, filePath, contentType); + +// Adds an array of bytes +AddFile(parameterName, bytes, fileName, contentType); + +// Adds a stream returned by the getFile function +AddFile(parameterName, getFile, fileName, contentType); +``` + +Remember that `AddFile` will set all the necessary headers, so please don't try to set content headers manually. + +You can also provide file upload options to the `AddFile` call. The options are: +- `DisableFilenameEncoding` (default `false`): if set to `true`, RestSharp will not encode the file name in the `Content-Disposition` header +- `DisableFilenameStar` (default `true`): if set to `true`, RestSharp will not add the `filename*` parameter to the `Content-Disposition` header + +Example of using the options: + +```csharp +var options = new FileParameterOptions { + DisableFilenameEncoding = true, + DisableFilenameStar = false +}; +request.AddFile("file", filePath, options: options); +``` + +The options specified in the snippet above usually help when you upload files with non-ASCII characters in their names. + +### Downloading binary data + +There are two functions that allow you to download binary data from the remote API. + +First, there's `DownloadDataAsync`, which returns `Task`. This function allows you to open a stream reader and asynchronously stream large responses to memory or disk. + + +## Reusing HttpClient + +RestSharp uses `HttpClient` internally to make HTTP requests. It's possible to reuse the same `HttpClient` instance for multiple `RestClient` instances. This is useful when you want to share the same connection pool between multiple `RestClient` instances. + +One way of doing it is to use `RestClient` constructors that accept an instance of `HttpClient` or `HttpMessageHandler` as an argument. Note that in that case not all the options provided via `RestClientOptions` will be used. Here is the list of options that will work: + +- `BaseAddress` will be used to set the base address of the `HttpClient` instance if base address is not set there already. +- `MaxTimeout` +- `UserAgent` will be added to the `RestClient.DefaultParameters` list as a HTTP header. This will be added to each request made by the `RestClient`, and the `HttpClient` instance will not be modified. This is to allow the `HttpClient` instance to be reused for scenarios where different `User-Agent` headers are required. +- `Expect100Continue` + +Another option is to use a simple HTTP client factory as described [above](#simple-factory). + +## Blazor support + +Inside a Blazor webassembly app, you can make requests to external API endpoints. Microsoft examples show how to do it with `HttpClient`, and it's also possible to use RestSharp for the same purpose. + +You need to remember that webassembly has some platform-specific limitations. Therefore, you won't be able to instantiate `RestClient` using all of its constructors. In fact, you can only use `RestClient` constructors that accept `HttpClient` or `HttpMessageHandler` as an argument. If you use the default parameterless constructor, it will call the option-based constructor with default options. The options-based constructor will attempt to create an `HttpMessageHandler` instance using the options provided, and it will fail with Blazor, as some of those options throw thw "Unsupported platform" exception. + +Here is an example how to register the `RestClient` instance globally as a singleton: + +```csharp +builder.Services.AddSingleton(new RestClient(new HttpClient())); +``` + +Then, on a page you can inject the instance: + +```html +@page "/fetchdata" +@using RestSharp +@inject RestClient _restClient +``` + +And then use it: + +```csharp +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() { + forecasts = await _restClient.GetJsonAsync("http://localhost:5104/weather"); + } + + public class WeatherForecast { + public DateTime Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} +``` + +In this case, the call will be made to a WebAPI server hosted at `http://localhost:5104/weather`. Remember that if the WebAPI server is not hosting the webassembly itself, it needs to have a CORS policy configured to allow the webassembly origin to access the API endpoint from the browser. diff --git a/docs/versioned_docs/version-v111/usage/client.md b/docs/versioned_docs/version-v111/usage/client.md new file mode 100644 index 000000000..a33423e3f --- /dev/null +++ b/docs/versioned_docs/version-v111/usage/client.md @@ -0,0 +1,60 @@ +--- +sidebar_position: 3 +title: Creating the client +--- + +## Constructors + +A RestSharp client can be instantiated by one of its constructors. Two most commonly used constructors are: + +#### Only specify the base URL + +You can create an instance of `RestClient` with only a single parameter: the base URL. Even that isn't required as base URL can be left empty. In that case, you'd need to specify the absolute path for each call. When the base URL is set, you can use both relative and absolute path. + +```csharp +// Creates a client with default options to call a given base URL +var client = new RestClient("https://localhost:5000"); +``` + +#### Provide client options + +The most common way to create a client is to use the constructor with options. The options object has the type of `RestClientOptions`. +Here's an example of how to create a client using the same base path as in the previous sample, but with a couple additional settings: + +```csharp +// Creates a client using the options object +var options = new RestClientOptions("https://localhost:5000") { + MaxTimeout = 1000 +}; +var client = new RestClient(options); +``` + +#### Advanced configuration + +RestSharp can be configured with more tweaks, including default request options, how it should handle responses, how serialization works, etc. You can also provide your own instance of `HttpClient` or `HttpMessageHandler`. + +Read more about the advanced configuration of RestSharp on a [dedicated page](../advanced/configuration.md). + +## Simple factory + +Another way to create the client instance is to use a simple client factory. The factory will use the `BaseUrl` property of the client options to cache `HttpClient` instances. Every distinct base URL will get its own `HttpClient` instance. Other options don't affect the caching. Therefore, if you use different options for the same base URL, you'll get the same `HttpClient` instance, which will not be configured with the new options. Options that aren't applied _after_ the first client instance is created are: + +* `Credentials` +* `UseDefaultCredentials` +* `AutomaticDecompression` +* `PreAuthenticate` +* `FollowRedirects` +* `RemoteCertificateValidationCallback` +* `ClientCertificates` +* `MaxRedirects` +* `MaxTimeout` +* `UserAgent` +* `Expect100Continue` + +Constructor parameters to configure the `HttpMessageHandler` and default `HttpClient` headers configuration are also ignored for the cached instance as the factory only configures the handler once. + +You need to set the `useClientFactory` parameter to `true` in the `RestClient` constructor to enable the factory. + +```csharp +var client = new RestClient("https://api.twitter.com/2", true); +``` diff --git a/docs/versioned_docs/version-v111/usage/example.md b/docs/versioned_docs/version-v111/usage/example.md index 458ed57bb..6182d8ff3 100644 --- a/docs/versioned_docs/version-v111/usage/example.md +++ b/docs/versioned_docs/version-v111/usage/example.md @@ -1,3 +1,7 @@ +--- +sidebar_position: 1 +--- + # Example RestSharp works best as the foundation for a proxy class for your API. Each API would most probably require different settings for `RestClient`. Hence, a dedicated API class (and its interface) gives you sound isolation between different `RestClient` instances and make them testable. @@ -46,7 +50,7 @@ public class TwitterClient : ITwitterClient, IDisposable { } public async Task GetUser(string user) { - var response = await _client.GetJsonAsync>( + var response = await _client.GetAsync>( "users/by/username/{user}", new { user } ); @@ -139,7 +143,7 @@ Here we add a POST parameter `grant_type` with `client_credentials` as its value The POST request will use the `application/x-www-form-urlencoded` content type by default. -:::note +::: note Sample code provided on this page is a production code. For example, the authenticator might produce undesired side effect when multiple requests are made at the same time when the token hasn't been obtained yet. It can be solved rather than simply using semaphores or synchronized invocation. ::: diff --git a/docs/versioned_docs/version-v111/usage/execute.md b/docs/versioned_docs/version-v111/usage/execute.md new file mode 100644 index 000000000..daba09106 --- /dev/null +++ b/docs/versioned_docs/version-v111/usage/execute.md @@ -0,0 +1,143 @@ +--- +sidebar_position: 5 +title: Making calls +--- + +## Executing requests + +Once you've added all the parameters to your `RestRequest`, you are ready to make a request. + +`RestClient` has a single function for this: + +```csharp +public async Task ExecuteAsync( + RestRequest request, + CancellationToken cancellationToken = default +) +``` + +You can also avoid setting the request method upfront and use one of the overloads: + +```csharp +Task ExecuteGetAsync(RestRequest request, CancellationToken cancellationToken) +Task ExecutePostAsync(RestRequest request, CancellationToken cancellationToken) +Task ExecutePutAsync(RestRequest request, CancellationToken cancellationToken) +Task ExecuteDeleteAsync(RestRequest request, CancellationToken cancellationToken) +Task ExecuteHeadAsync(RestRequest request, CancellationToken cancellationToken) +Task ExecuteOptionsAsync(RestRequest request, CancellationToken cancellationToken) +``` + +When using any of those methods, you will get the response content as string in `response.Content`. + +RestSharp can deserialize the response for you. To use that feature, use one of the generic overloads: + +```csharp +Task> ExecuteAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecuteGetAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecutePostAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecutePutAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecuteDeleteAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecuteHeadAsync(RestRequest request, CancellationToken cancellationToken) +Task> ExecuteOptionsAsync(RestRequest request, CancellationToken cancellationToken) +``` + +:::note Beware of errors +All the overloads with names starting with `Execute` don't throw an exception if the server returns an error. Read more about it [here](../advanced/error-handling.md). +It allows you to inspect responses and handle remote server errors gracefully. Overloads without `Execute` prefix throw exceptions in case of any error, so you'd need to ensure to handle exceptions properly. +::: + +If you just need a deserialized response, you can use one of the extensions: + +```csharp +Task GetAsync(RestRequest request, CancellationToken cancellationToken) +Task PostAsync(RestRequest request, CancellationToken cancellationToken) +Task PutAsync(RestRequest request, CancellationToken cancellationToken) +Task PatchAsync(RestRequest request, CancellationToken cancellationToken) +Task DeleteAsync(RestRequest request, CancellationToken cancellationToken) +Task HeadAsync(RestRequest request, CancellationToken cancellationToken) +Task OptionsAsync(RestRequest request, CancellationToken cancellationToken) +``` + +Those extensions will throw an exception if the server returns an error, as there's no other way to float the error back to the caller. + +The `IRestClient` interface also has extensions for making requests without deserialization, which throw an exception if the server returns an error even if the client is configured to not throw exceptions. + +```csharp +Task GetAsync(RestRequest request, CancellationToken cancellationToken) +Task PostAsync(RestRequest request, CancellationToken cancellationToken) +Task PutAsync(RestRequest request, CancellationToken cancellationToken) +Task PatchAsync(RestRequest request, CancellationToken cancellationToken) +Task DeleteAsync(RestRequest request, CancellationToken cancellationToken) +Task HeadAsync(RestRequest request, CancellationToken cancellationToken) +Task OptionsAsync(RestRequest request, CancellationToken cancellationToken) +``` + +### Sync calls + +The preferred way for making requests is to execute them asynchronously as HTTP calls are IO-bound operations. +If you are unable to make async calls, all the functions about have sync overloads, which have the same names without `Async` suffix. +For example, for making a sync `GET` call you can use `ExecuteGet(request)` or `Get`, etc. + +## Requests without body + +Some HTTP methods don't suppose to be used with request body. For those methods, RestSharp supports making simplified calls without using `RestRequest`. All you need is to provide the resource path as a string. + +For example, you can make a `DELETE` call like this: + +```csharp +var response = await client.ExecuteDeleteAsync($"order/delete/{orderId}", cancellationToken); +``` + +Similarly, you can make `GET` calls with or without deserialization of the response using `ExecuteGetAsync(resource)`, `GetAsync(resource)`, `ExecuteGetAsync(resource)`, and `GetAsync(resource)` (see below). + +## JSON requests + +RestSharp provides an easier API for making calls to endpoints that accept and return JSON. + +### GET calls + +To make a simple `GET` call and get a deserialized JSON response with a pre-formed resource string, use this: + +```csharp +var response = await client.GetAsync("endpoint?foo=bar", cancellationToken); +``` + +:::note +In v111, `GetJsonAsync` is renamed to `GetAsync`. +::: + +You can also use a more advanced extension that uses an object to compose the resource string: + +```csharp +var client = new RestClient("https://example.org"); +var args = new { + id = "123", + foo = "bar" +}; +// Will make a call to https://example.org/endpoint/123?foo=bar +var response = await client.GetAsync("endpoint/{id}", args, cancellationToken); +``` + +It will search for the URL segment parameters matching any of the object properties and replace them with values. All the other properties will be used as query parameters. + +One note about `GetAsync` is that it will deserialize the response with any supported content type, not only JSON. + +### POST calls + +Similar things are available for `POST` requests. + +```csharp +var request = new CreateOrder("123", "foo", 10100); +// Will post the request object as JSON to "orders" and returns a +// JSON response deserialized to OrderCreated +var result = client.PostJsonAsync("orders", request, cancellationToken); +``` + +```csharp +var request = new CreateOrder("123", "foo", 10100); +// Will post the request object as JSON to "orders" and returns a +// status code, not expecting any response body +var statusCode = client.PostJsonAsync("orders", request, cancellationToken); +``` + +The same two extensions also exist for `PUT` requests (`PutJsonAsync`); diff --git a/docs/versioned_docs/version-v111/usage/request.md b/docs/versioned_docs/version-v111/usage/request.md new file mode 100644 index 000000000..0feb7ee22 --- /dev/null +++ b/docs/versioned_docs/version-v111/usage/request.md @@ -0,0 +1,291 @@ +--- +sidebar_position: 4 +title: Preparing requests +--- + +## Create a request + +Before making a request using `RestClient`, you need to create a request instance: + +```csharp +var request = new RestRequest(resource); // resource is the sub-path of the client base path +``` + +The default request type is `GET` and you can override it by setting the `Method` property. You can also set the method using the constructor overload: + +```csharp +var request = new RestRequest(resource, Method.Post); +``` + +After you've created a `RestRequest`, you can add parameters to it. Below, you can find all the parameter types supported by RestSharp. + +## Request headers + +Adds the header parameter as an HTTP header that is sent along with the request. The header name is the parameter's name and the header value is the value. + +You can use one of the following request methods to add a header parameter: + +```csharp +AddHeader(string name, string value); +AddHeader(string name, T value); // value will be converted to string +AddOrUpdateHeader(string name, string value); // replaces the header if it already exists +``` + +For example: + +```csharp +var request = new RestRequest("/path").AddHeader("X-Key", someKey); +``` + +You can also add header parameters to the client, and they will be added to every request made by the client. This is useful for adding authentication headers, for example. + +```csharp +client.AddDefaultHeader(string name, string value); +``` + +:::warning Avoid setting Content-Type header +RestSharp will use the correct content type by default. Avoid adding the `Content-Type` header manually to your requests unless you are absolutely sure it is required. You can add a custom content type to the [body parameter](#request-body) itself. +::: + +## Get or Post parameters + +The default RestSharp parameter type is `GetOrPostParameter`. You can add `GetOrPost` parameter to the request using the `AddParameter` function: + +```csharp +request + .AddParameter("name1", "value1") + .AddParameter("name2", "value2"); +``` + +`GetOrPost` behaves differently based on the HTTP method. If you execute a `GET` call, RestSharp will append the parameters to the URL in the form `url?name1=value1&name2=value2`. + +On a `POST` or `PUT` requests, it depends on whether you have files attached to a request. +If not, the parameters will be sent as the body of the request in the form `name1=value1&name2=value2`. Also, the request will be sent as `application/x-www-form-urlencoded`. + +In both cases, name and value will automatically be URL-encoded, unless specified otherwise: + +```csharp +request.AddParameter("name", "Væ üé", false); // don't encode the value +``` + +If you have files, RestSharp will send a `multipart/form-data` request. Your parameters will be part of this request in the form: + +``` +Content-Disposition: form-data; name="parameterName" + +ParameterValue +``` + +You can also add `GetOrPost` parameter as a default parameter to the client. This will add the parameter to every request made by the client. + +```csharp +client.AddDefaultParameter("foo", "bar"); +``` + +It will work the same way as request parameters, except that it will be added to every request. + +## Query string + +`QueryString` works like `GetOrPost`, except that it always appends the parameters to the url in the form `url?name1=value1&name2=value2`, regardless of the request method. + +Example: + +```csharp +var client = new RestClient("https://search.me"); +var request = new RestRequest("search") + .AddParameter("foo", "bar"); +var response = await client.GetAsync(request); +``` + +It will send a `GET` request to `https://search.me/search?foo=bar`. + +For `POST`-style requests you need to add the query string parameter explicitly: + +```csharp +request.AddQueryParameter("foo", "bar"); +``` + +In some cases, you might need to prevent RestSharp from encoding the query string parameter. +To do so, set the `encode` argument to `false` when adding the parameter: + +```csharp +request.AddQueryParameter("foo", "bar/fox", false); +``` + +You can also add a query string parameter as a default parameter to the client. This will add the parameter to every request made by the client. + +```csharp +client.AddDefaultQueryParameter("foo", "bar"); +``` + +The line above will result in all the requests made by that client instance to have `foo=bar` in the query string for all the requests made by that client. + +## Using AddObject + +You can avoid calling `AddParameter` multiple times if you collect all the parameters in an object, and then use `AddObject`. +For example, this code: + +```csharp +var params = new { + status = 1, + priority = "high", + ids = new [] { "123", "456" } +}; +request.AddObject(params); +``` + +is equivalent to: + +```csharp +request.AddParameter("status", 1); +request.AddParameter("priority", "high"); +request.AddParameter("ids", "123,456"); +``` + +Remember that `AddObject` only works if your properties have primitive types. It also works with collections of primitive types as shown above. + +If you need to override the property name or format, you can do it using the `RequestProperty` attribute. For example: + +```csharp +public class RequestModel { + // override the name and the format + [RequestProperty(Name = "from_date", Format = "d")] + public DateTime FromDate { get; set; } +} + +// add it to the request +request.AddObject(new RequestModel { FromDate = DateTime.Now }); +``` + +In this case, the request will get a GET or POST parameter named `from_date` and its value would be the current date in short date format. + +## Using AddObjectStatic + +Request function `AddObjectStatic(...)` allows using pre-compiled expressions for getting property values. Compared to `AddObject` that uses reflections for each call, `AddObjectStatic` caches functions to retrieve properties from an object of type `T`, so it works much faster. + +You can instruct `AddObjectStatic` to use custom parameter names and formats, as well as supply the list of properties than need to be used as parameters. The last option could be useful if the type `T` has properties that don't need to be sent with HTTP call. + +To use custom parameter name or format, use the `RequestProperty` attribute. For example: + +```csharp +class TestObject { + [RequestProperty(Name = "some_data")] + public string SomeData { get; set; } + + [RequestProperty(Format = "d")] + public DateTime SomeDate { get; set; } + + [RequestProperty(Name = "dates", Format = "d")] + public DateTime[] DatesArray { get; set; } + + public int Plain { get; set; } + public DateTime[] PlainArray { get; set; } +} +``` + +## URL segment parameter + +Unlike `GetOrPost`, URL segment parameter replaces placeholder values in the request URL: + +```csharp +var request = new RestRequest("health/{entity}/status") + .AddUrlSegment("entity", "s2"); +``` + +When the request executes, RestSharp will try to match any `{placeholder}` with a parameter of that name (without the `{}`) and replace it with the value. So the above code results in `health/s2/status` being the URL. + +You can also add `UrlSegment` parameter as a default parameter to the client. This will add the parameter to every request made by the client. + +```csharp +client.AddDefaultUrlSegment("foo", "bar"); +``` + +## Cookies + +You can add cookies to a request using the `AddCookie` method: + +```csharp +request.AddCookie("foo", "bar"); +``` + +RestSharp will add cookies from the request as cookie headers and then extract the matching cookies from the response. You can observe and extract response cookies using the `RestResponse.Cookies` properties, which has the `CookieCollection` type. + +However, the usage of a default URL segment parameter is questionable as you can just include the parameter value to the base URL of the client. There is, however, a `CookieContainer` instance on the request level. You can either assign the pre-populated container to `request.CookieContainer`, or let the container be created by the request when you call `AddCookie`. Still, the container is only used to extract all the cookies from it and create cookie headers for the request instead of using the container directly. It's because the cookie container is normally configured on the `HttpClientHandler` level and cookies are shared between requests made by the same client. In most of the cases this behaviour can be harmful. + +If your use case requires sharing cookies between requests made by the client instance, you can use the client-level `CookieContainer`, which you must provide as the options' property. You can add cookies to the container using the container API. No response cookies, however, would be auto-added to the container, but you can do it in code by getting cookies from the `Cookes` property of the response and adding them to the client-level container available via `IRestClient.Options.CookieContainer` property. + +## Request Body + +RestSharp supports multiple ways to add a request body: +- `AddJsonBody` for JSON payloads +- `AddXmlBody` for XML payloads +- `AddStringBody` for pre-serialized payloads + +We recommend using `AddJsonBody` or `AddXmlBody` methods instead of `AddParameter` with type `BodyParameter`. Those methods will set the proper request type and do the serialization work for you. + +When you make a `POST`, `PUT` or `PATCH` request and added `GetOrPost` [parameters](#get-or-post-parameters), RestSharp will send them as a URL-encoded form request body by default. When a request also has files, it will send a `multipart/form-data` request. You can also instruct RestSharp to send the body as `multipart/form-data` by setting the `AlwaysMultipartFormData` property to `true`. + +You can specify a custom body content type if necessary. The `contentType` argument is available in all the overloads that add a request body. + +It is not possible to add client-level default body parameters. + +### String body + +If you have a pre-serialized payload like a JSON string, you can use `AddStringBody` to add it as a body parameter. You need to specify the content type, so the remote endpoint knows what to do with the request body. For example: + +```csharp +const json = "{ data: { foo: \"bar\" } }"; +request.AddStringBody(json, ContentType.Json); +``` + +### JSON body + +When you call `AddJsonBody`, it does the following for you: + +- Instructs the RestClient to serialize the object parameter as JSON when making a request +- Sets the content type to `application/json` +- Sets the internal data type of the request body to `DataType.Json` + +Here is the example: + +```csharp +var param = new MyClass { IntData = 1, StringData = "test123" }; +request.AddJsonBody(param); +``` + +It is possible to override the default content type by supplying the `contentType` argument. For example: + +```csharp +request.AddJsonBody(param, "text/x-json"); +``` + +If you use a pre-serialized string with `AddJsonBody`, it will be sent as-is. The `AddJsonBody` will detect if the parameter is a string and will add it as a string body with JSON content type. +Essentially, it means that top-level strings won't be serialized as JSON when you use `AddJsonBody`. To overcome this issue, you can use an overload of `AddJsonBody`, which allows you to tell RestSharp to serialize the string as JSON: + +```csharp +const string payload = @" +""requestBody"": { + ""content"": { + ""application/json"": { + ""schema"": { + ""type"": ""string"" + } + } + } +},"; +request.AddJsonBody(payload, forceSerialize: true); // the string will be serialized +request.AddJsonBody(payload); // the string will NOT be serialized and will be sent as-is +``` + +### XML body + +When you call `AddXmlBody`, it does the following for you: + +- Instructs the RestClient to serialize the object parameter as XML when making a request +- Sets the content type to `application/xml` +- Sets the internal data type of the request body to `DataType.Xml` + +:::warning +Do not send XML string to `AddXmlBody`; it won't work! +::: + diff --git a/docs/versioned_docs/version-v111/usage/usage.md b/docs/versioned_docs/version-v111/usage/usage.md deleted file mode 100644 index d88a15f1f..000000000 --- a/docs/versioned_docs/version-v111/usage/usage.md +++ /dev/null @@ -1,620 +0,0 @@ ---- -sidebar_position: 2 ---- - -# RestSharp basics - -This page describes some of the essential properties and features of RestSharp. - -## What RestSharp does - -Essentially, RestSharp is a wrapper around `HttpClient` that allows you to do the following: -- Add default parameters of any kind (not just headers) to the client, once -- Add parameters of any kind to each request (query, URL segment, form, attachment, serialized body, header) in a straightforward way -- Serialize the payload to JSON or XML if necessary -- Set the correct content headers (content type, disposition, length, etc.) -- Handle the remote endpoint response -- Deserialize the response from JSON or XML if necessary - -## API client - -The best way to call an external HTTP API is to create a typed client, which encapsulates RestSharp calls and doesn't expose the `RestClient` instance in public. - -You can find an example of a Twitter API client on the [Example](example.md) page. - -## Creating the client - -A RestSharp client can be instantiated by one of the constructors: - -```csharp -// Creates a client with default options to call a given base URL -var client = new RestClient("https://localhost:5000"); - -// Creates a client using the options object -var options = new RestClientOptions("https://localhost:5000") { - MaxTimeout = 1000 -}; -var client = new RestClient(options); -``` - -### Simple factory - -Another way to create the client instance is to use a simple client factory. The factory will use the `BaseUrl` property of the client options to cache `HttpClient` instances. Every distinct base URL will get its own `HttpClient` instance. Other options don't affect the caching. Therefore, if you use different options for the same base URL, you'll get the same `HttpClient` instance, which will not be configured with the new options. Options that aren't applied _after_ the first client instance is created are: - -* `Credentials` -* `UseDefaultCredentials` -* `AutomaticDecompression` -* `PreAuthenticate` -* `FollowRedirects` -* `RemoteCertificateValidationCallback` -* `ClientCertificates` -* `MaxRedirects` -* `MaxTimeout` -* `UserAgent` -* `Expect100Continue` - -Constructor parameters to configure the `HttpMessageHandler` and default `HttpClient` headers configuration are also ignored for the cached instance as the factory only configures the handler once. - -You need to set the `useClientFactory` parameter to `true` in the `RestClient` constructor to enable the factory. - -```csharp -var client = new RestClient("https://api.twitter.com/2", true); -``` - -## Create a request - -Before making a request using `RestClient`, you need to create a request instance: - -```csharp -var request = new RestRequest(resource); // resource is the sub-path of the client base path -``` - -The default request type is `GET` and you can override it by setting the `Method` property. You can also set the method using the constructor overload: - -```csharp -var request = new RestRequest(resource, Method.Post); -``` - -After you've created a `RestRequest`, you can add parameters to it. Below, you can find all the parameter types supported by RestSharp. - -### Adding headers - -Adds the header parameter as an HTTP header that is sent along with the request. The header name is the parameter's name and the header value is the value. - -You can use one of the following request methods to add a header parameter: - -```csharp -AddHeader(string name, string value); -AddHeader(string name, T value); // value will be converted to string -AddOrUpdateHeader(string name, string value); // replaces the header if it already exists -``` - -For example: - -```csharp -var request = new RestRequest("/path").AddHeader("X-Key", someKey); -``` - -You can also add header parameters to the client, and they will be added to every request made by the client. This is useful for adding authentication headers, for example. - -```csharp -client.AddDefaultHeader(string name, string value); -``` - -::: warning Content-Type -RestSharp will use the correct content type by default. Avoid adding the `Content-Type` header manually to your requests unless you are absolutely sure it is required. You can add a custom content type to the [body parameter](#request-body) itself. -::: - -### Get or Post parameters - -The default RestSharp parameter type is `GetOrPostParameter`. You can add `GetOrPost` parameter to the request using the `AddParameter` function: - -```csharp -request - .AddParameter("name1", "value1") - .AddParameter("name2", "value2"); -``` - -`GetOrPost` behaves differently based on the HTTP method. If you execute a `GET` call, RestSharp will append the parameters to the URL in the form `url?name1=value1&name2=value2`. - -On a `POST` or `PUT` requests, it depends on whether you have files attached to a request. -If not, the parameters will be sent as the body of the request in the form `name1=value1&name2=value2`. Also, the request will be sent as `application/x-www-form-urlencoded`. - -In both cases, name and value will automatically be URL-encoded, unless specified otherwise: - -```csharp -request.AddParameter("name", "Væ üé", false); // don't encode the value -``` - -If you have files, RestSharp will send a `multipart/form-data` request. Your parameters will be part of this request in the form: - -``` -Content-Disposition: form-data; name="parameterName" - -ParameterValue -``` - -You can also add `GetOrPost` parameter as a default parameter to the client. This will add the parameter to every request made by the client. - -```csharp -client.AddDefaultParameter("foo", "bar"); -``` - -It will work the same way as request parameters, except that it will be added to every request. - -### Using AddObject - -You can avoid calling `AddParameter` multiple times if you collect all the parameters in an object, and then use `AddObject`. -For example, this code: - -```csharp -var params = new { - status = 1, - priority = "high", - ids = new [] { "123", "456" } -}; -request.AddObject(params); -``` - -is equivalent to: - -```csharp -request.AddParameter("status", 1); -request.AddParameter("priority", "high"); -request.AddParameter("ids", "123,456"); -``` - -Remember that `AddObject` only works if your properties have primitive types. It also works with collections of primitive types as shown above. - -If you need to override the property name or format, you can do it using the `RequestProperty` attribute. For example: - -```csharp -public class RequestModel { - // override the name and the format - [RequestProperty(Name = "from_date", Format = "d")] - public DateTime FromDate { get; set; } -} - -// add it to the request -request.AddObject(new RequestModel { FromDate = DateTime.Now }); -``` - -In this case, the request will get a GET or POST parameter named `from_date` and its value would be the current date in short date format. - -### Using AddObjectStatic - -Request function `AddObjectStatic(...)` allows using pre-compiled expressions for getting property values. Compared to `AddObject` that uses reflections for each call, `AddObjectStatic` caches functions to retrieve properties from an object of type `T`, so it works much faster. - -You can instruct `AddObjectStatic` to use custom parameter names and formats, as well as supply the list of properties than need to be used as parameters. The last option could be useful if the type `T` has properties that don't need to be sent with HTTP call. - -To use custom parameter name or format, use the `RequestProperty` attribute. For example: - -```csharp -class TestObject { - [RequestProperty(Name = "some_data")] - public string SomeData { get; set; } - - [RequestProperty(Format = "d")] - public DateTime SomeDate { get; set; } - - [RequestProperty(Name = "dates", Format = "d")] - public DateTime[] DatesArray { get; set; } - - public int Plain { get; set; } - public DateTime[] PlainArray { get; set; } -} -``` - -### URL segment parameter - -Unlike `GetOrPost`, URL segment parameter replaces placeholder values in the request URL: - -```csharp -var request = new RestRequest("health/{entity}/status") - .AddUrlSegment("entity", "s2"); -``` - -When the request executes, RestSharp will try to match any `{placeholder}` with a parameter of that name (without the `{}`) and replace it with the value. So the above code results in `health/s2/status` being the URL. - -You can also add `UrlSegment` parameter as a default parameter to the client. This will add the parameter to every request made by the client. - -```csharp -client.AddDefaultUrlSegment("foo", "bar"); -``` - -### Cookies - -You can add cookies to a request using the `AddCookie` method: - -```csharp -request.AddCookie("foo", "bar"); -``` - -RestSharp will add cookies from the request as cookie headers and then extract the matching cookies from the response. You can observe and extract response cookies using the `RestResponse.Cookies` properties, which has the `CookieCollection` type. - -However, the usage of a default URL segment parameter is questionable as you can just include the parameter value to the base URL of the client. There is, however, a `CookieContainer` instance on the request level. You can either assign the pre-populated container to `request.CookieContainer`, or let the container be created by the request when you call `AddCookie`. Still, the container is only used to extract all the cookies from it and create cookie headers for the request instead of using the container directly. It's because the cookie container is normally configured on the `HttpClientHandler` level and cookies are shared between requests made by the same client. In most of the cases this behaviour can be harmful. - -If your use case requires sharing cookies between requests made by the client instance, you can use the client-level `CookieContainer`, which you must provide as the options' property. You can add cookies to the container using the container API. No response cookies, however, would be auto-added to the container, but you can do it in code by getting cookies from the `Cookes` property of the response and adding them to the client-level container available via `IRestClient.Options.CookieContainer` property. - -### Request Body - -RestSharp supports multiple ways to add a request body: -- `AddJsonBody` for JSON payloads -- `AddXmlBody` for XML payloads -- `AddStringBody` for pre-serialized payloads - -We recommend using `AddJsonBody` or `AddXmlBody` methods instead of `AddParameter` with type `BodyParameter`. Those methods will set the proper request type and do the serialization work for you. - -When you make a `POST`, `PUT` or `PATCH` request and added `GetOrPost` [parameters](#get-or-post-parameters), RestSharp will send them as a URL-encoded form request body by default. When a request also has files, it will send a `multipart/form-data` request. You can also instruct RestSharp to send the body as `multipart/form-data` by setting the `AlwaysMultipartFormData` property to `true`. - -You can specify a custom body content type if necessary. The `contentType` argument is available in all the overloads that add a request body. - -It is not possible to add client-level default body parameters. - -#### String body - -If you have a pre-serialized payload like a JSON string, you can use `AddStringBody` to add it as a body parameter. You need to specify the content type, so the remote endpoint knows what to do with the request body. For example: - -```csharp -const json = "{ data: { foo: \"bar\" } }"; -request.AddStringBody(json, ContentType.Json); -``` - -#### JSON body - -When you call `AddJsonBody`, it does the following for you: - -- Instructs the RestClient to serialize the object parameter as JSON when making a request -- Sets the content type to `application/json` -- Sets the internal data type of the request body to `DataType.Json` - -Here is the example: - -```csharp -var param = new MyClass { IntData = 1, StringData = "test123" }; -request.AddJsonBody(param); -``` - -It is possible to override the default content type by supplying the `contentType` argument. For example: - -```csharp -request.AddJsonBody(param, "text/x-json"); -``` - -If you use a pre-serialized string with `AddJsonBody`, it will be sent as-is. The `AddJsonBody` will detect if the parameter is a string and will add it as a string body with JSON content type. -Essentially, it means that top-level strings won't be serialized as JSON when you use `AddJsonBody`. To overcome this issue, you can use an overload of `AddJsonBody`, which allows you to tell RestSharp to serialize the string as JSON: - -```csharp -const string payload = @" -""requestBody"": { - ""content"": { - ""application/json"": { - ""schema"": { - ""type"": ""string"" - } - } - } -},"; -request.AddJsonBody(payload, forceSerialize: true); // the string will be serialized -request.AddJsonBody(payload); // the string will NOT be serialized and will be sent as-is -``` - -#### XML body - -When you call `AddXmlBody`, it does the following for you: - -- Instructs the RestClient to serialize the object parameter as XML when making a request -- Sets the content type to `application/xml` -- Sets the internal data type of the request body to `DataType.Xml` - -:::warning -Do not send XML string to `AddXmlBody`; it won't work! -::: - -### Query string - -`QueryString` works like `GetOrPost`, except that it always appends the parameters to the url in the form `url?name1=value1&name2=value2`, regardless of the request method. - -Example: - -```csharp -var client = new RestClient("https://search.me"); -var request = new RestRequest("search") - .AddParameter("foo", "bar"); -var response = await client.GetAsync(request); -``` - -It will send a `GET` request to `https://search.me/search?foo=bar`. - -For `POST`-style requests you need to add the query string parameter explicitly: - -```csharp -request.AddQueryParameter("foo", "bar"); -``` - -In some cases, you might need to prevent RestSharp from encoding the query string parameter. -To do so, set the `encode` argument to `false` when adding the parameter: - -```csharp -request.AddQueryParameter("foo", "bar/fox", false); -``` - -You can also add a query string parameter as a default parameter to the client. This will add the parameter to every request made by the client. - -```csharp -client.AddDefaultQueryParameter("foo", "bar"); -``` - -The line above will result in all the requests made by that client instance to have `foo=bar` in the query string for all the requests made by that client. - -## Making a call - -Once you've added all the parameters to your `RestRequest`, you are ready to make a request. - -`RestClient` has a single function for this: - -```csharp -public async Task ExecuteAsync( - RestRequest request, - CancellationToken cancellationToken = default -) -``` - -You can also avoid setting the request method upfront and use one of the overloads: - -```csharp -Task ExecuteGetAsync(RestRequest request, CancellationToken cancellationToken) -Task ExecutePostAsync(RestRequest request, CancellationToken cancellationToken) -Task ExecutePutAsync(RestRequest request, CancellationToken cancellationToken) -``` - -When using any of those methods, you will get the response content as string in `response.Content`. - -RestSharp can deserialize the response for you. To use that feature, use one of the generic overloads: - -```csharp -Task> ExecuteAsync(RestRequest request, CancellationToken cancellationToken) -Task> ExecuteGetAsync(RestRequest request, CancellationToken cancellationToken) -Task> ExecutePostAsync(RestRequest request, CancellationToken cancellationToken) -Task> ExecutePutAsync(RestRequest request, CancellationToken cancellationToken) -``` - -All the overloads with names starting with `Execute` don't throw an exception if the server returns an error. Read more about it [here](../advanced/error-handling.md). - -If you just need a deserialized response, you can use one of the extensions: - -```csharp -Task GetAsync(RestRequest request, CancellationToken cancellationToken) -Task PostAsync(RestRequest request, CancellationToken cancellationToken) -Task PutAsync(RestRequest request, CancellationToken cancellationToken) -Task HeadAsync(RestRequest request, CancellationToken cancellationToken) -Task PatchAsync(RestRequest request, CancellationToken cancellationToken) -Task DeleteAsync(RestRequest request, CancellationToken cancellationToken) -``` - -Those extensions will throw an exception if the server returns an error, as there's no other way to float the error back to the caller. - -The `IRestClient` interface also has extensions for making requests without deserialization, which throw an exception if the server returns an error even if the client is configured to not throw exceptions. - -```csharp -Task GetAsync(RestRequest request, CancellationToken cancellationToken) -Task PostAsync(RestRequest request, CancellationToken cancellationToken) -Task PutAsync(RestRequest request, CancellationToken cancellationToken) -Task HeadAsync(RestRequest request, CancellationToken cancellationToken) -Task PatchAsync(RestRequest request, CancellationToken cancellationToken) -Task DeleteAsync(RestRequest request, CancellationToken cancellationToken) -``` - -### Requests without body - -Some HTTP methods don't suppose to be used with request body. For those methods, RestSharp supports making simplified calls without using `RestRequest`. All you need is to provide the resource path as a string. - -For example, you can make a `DELETE` call like this: - -```csharp -var response = await client.ExecuteDeleteAsync($"order/delete/{orderId}", cancellationToken); -``` - -Similarly, you can make `GET` calls with or without deserialization of the response using `ExecuteGetAsync(resource)`, `GetAsync(resource)`, `ExecuteGetAsync(resource)`, and `GetAsync(resource)` (see below). - -### JSON requests - -To make a simple `GET` call and get a deserialized JSON response with a pre-formed resource string, use this: - -```csharp -var response = await client.GetAsync("endpoint?foo=bar", cancellationToken); -``` - -::: -In v111, `GetJsonAsync` is renamed to `GetAsync`. -::: - -You can also use a more advanced extension that uses an object to compose the resource string: - -```csharp -var client = new RestClient("https://example.org"); -var args = new { - id = "123", - foo = "bar" -}; -// Will make a call to https://example.org/endpoint/123?foo=bar -var response = await client.GetAsync("endpoint/{id}", args, cancellationToken); -``` - -It will search for the URL segment parameters matching any of the object properties and replace them with values. All the other properties will be used as query parameters. - -Similar things are available for `POST` requests. - -```csharp -var request = new CreateOrder("123", "foo", 10100); -// Will post the request object as JSON to "orders" and returns a -// JSON response deserialized to OrderCreated -var result = client.PostJsonAsync("orders", request, cancellationToken); -``` - -```csharp -var request = new CreateOrder("123", "foo", 10100); -// Will post the request object as JSON to "orders" and returns a -// status code, not expecting any response body -var statusCode = client.PostJsonAsync("orders", request, cancellationToken); -``` - -The same two extensions also exist for `PUT` requests (`PutJsonAsync`); - -## Handling responses - -All `Execute{Method}Async` functions return an instance of `RestResponse`. Similarly, `Execute{Method}Async` return a generic instance of `RestResponse` where `T` is the response object type. - -Response object contains the following properties: - -| Property | Type | Description | -|--------------------------|-----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| `Request` | `RestRequest` | Request instance that was used to get the response. | -| `ContentType` | `string?` | Response content type. `Null` if response has no content. | -| `ContentLength` | `long?` | Response content length. `Null` if response has no content. | -| `ContentEncoding` | `ICollection` | Content encoding collection. Empty if response has no content. | -| `Content` | `string?` | Response content as string. `Null` if response has no content. | -| `IsSuccessfulStatusCode` | `bool` | Indicates if response was successful, so no errors were reported by the server. Note that `404` response code means success. | -| `ResponseStatus` | `None`, `Completed`, `Error`, `TimedOut`, `Aborted` | Response completion status. Note that completed responses might still return errors. | -| `IsSuccessful` | `bool` | `True` when `IsSuccessfulStatusCode` is `true` and `ResponseStatus` is `Completed`. | -| `StatusDescription` | `string?` | Response status description, if available. | -| `RawBytes` | `byte[]?` | Response content as byte array. `Null` if response has no content. | -| `ResponseUri` | `Uri?` | URI of the response, which might be different from request URI in case of redirects. | -| `Server` | `string?` | Server header value of the response. | -| `Cookies` | `CookieCollection?` | Collection of cookies received with the response, if any. | -| `Headers` | Collection of `HeaderParameter` | Response headers. | -| `ContentHeaders` | Collection of `HeaderParameter` | Response content headers. | -| `ErrorMessage` | `string?` | Transport or another non-HTTP error generated while attempting request. | -| `ErrorException` | `Exception?` | Exception thrown when executing the request, if any. | -| `Version` | `Version?` | HTTP protocol version of the request. | -| `RootElement` | `string?` | Root element of the serialized response content, only works if deserializer supports it. | - -In addition, `RestResponse` has one additional property: - -| Property | Type | Description | -|----------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Data` | `T?` | Deserialized response object. `Null` if there's no content in the response, deserializer failed to understand the response content, or if request failed. | - -### JSON streaming APIs - -For HTTP API endpoints that stream the response data (like [Twitter search stream](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream)) you can use RestSharp with `StreamJsonAsync`, which returns an `IAsyncEnumerable`: - -```csharp -public async IAsyncEnumerable SearchStream( - [EnumeratorCancellation] CancellationToken cancellationToken = default -) { - var response = _client.StreamJsonAsync>( - "tweets/search/stream", cancellationToken - ); - - await foreach (var item in response.WithCancellation(cancellationToken)) { - yield return item.Data; - } -} -``` - -The main limitation of this function is that it expects each JSON object to be returned as a single line. It is unable to parse the response by combining multiple lines into a JSON string. - -### Uploading files - -To add a file to the request you can use the `RestRequest` function called `AddFile`. The main function accepts the `FileParameter` argument: - -```csharp -request.AddFile(fileParameter); -``` - -You can instantiate the file parameter using `FileParameter.Create` that accepts a bytes array, or `FileParameter.FromFile`, which will load the file from disk. - -There are also extension functions that wrap the creation of `FileParameter` inside: - -```csharp -// Adds a file from disk -AddFile(parameterName, filePath, contentType); - -// Adds an array of bytes -AddFile(parameterName, bytes, fileName, contentType); - -// Adds a stream returned by the getFile function -AddFile(parameterName, getFile, fileName, contentType); -``` - -Remember that `AddFile` will set all the necessary headers, so please don't try to set content headers manually. - -You can also provide file upload options to the `AddFile` call. The options are: -- `DisableFilenameEncoding` (default `false`): if set to `true`, RestSharp will not encode the file name in the `Content-Disposition` header -- `DisableFilenameStar` (default `true`): if set to `true`, RestSharp will not add the `filename*` parameter to the `Content-Disposition` header - -Example of using the options: - -```csharp -var options = new FileParameterOptions { - DisableFilenameEncoding = true, - DisableFilenameStar = false -}; -request.AddFile("file", filePath, options: options); -``` - -The options specified in the snippet above usually help when you upload files with non-ASCII characters in their names. - -### Downloading binary data - -There are two functions that allow you to download binary data from the remote API. - -First, there's `DownloadDataAsync`, which returns `Task`. This function allows you to open a stream reader and asynchronously stream large responses to memory or disk. - - -## Reusing HttpClient - -RestSharp uses `HttpClient` internally to make HTTP requests. It's possible to reuse the same `HttpClient` instance for multiple `RestClient` instances. This is useful when you want to share the same connection pool between multiple `RestClient` instances. - -One way of doing it is to use `RestClient` constructors that accept an instance of `HttpClient` or `HttpMessageHandler` as an argument. Note that in that case not all the options provided via `RestClientOptions` will be used. Here is the list of options that will work: - -- `BaseAddress` will be used to set the base address of the `HttpClient` instance if base address is not set there already. -- `MaxTimeout` -- `UserAgent` will be added to the `RestClient.DefaultParameters` list as a HTTP header. This will be added to each request made by the `RestClient`, and the `HttpClient` instance will not be modified. This is to allow the `HttpClient` instance to be reused for scenarios where different `User-Agent` headers are required. -- `Expect100Continue` - -Another option is to use a simple HTTP client factory as described [above](#simple-factory). - -## Blazor support - -Inside a Blazor webassembly app, you can make requests to external API endpoints. Microsoft examples show how to do it with `HttpClient`, and it's also possible to use RestSharp for the same purpose. - -You need to remember that webassembly has some platform-specific limitations. Therefore, you won't be able to instantiate `RestClient` using all of its constructors. In fact, you can only use `RestClient` constructors that accept `HttpClient` or `HttpMessageHandler` as an argument. If you use the default parameterless constructor, it will call the option-based constructor with default options. The options-based constructor will attempt to create an `HttpMessageHandler` instance using the options provided, and it will fail with Blazor, as some of those options throw thw "Unsupported platform" exception. - -Here is an example how to register the `RestClient` instance globally as a singleton: - -```csharp -builder.Services.AddSingleton(new RestClient(new HttpClient())); -``` - -Then, on a page you can inject the instance: - -```html -@page "/fetchdata" -@using RestSharp -@inject RestClient _restClient -``` - -And then use it: - -```csharp -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() { - forecasts = await _restClient.GetJsonAsync("http://localhost:5104/weather"); - } - - public class WeatherForecast { - public DateTime Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} -``` - -In this case, the call will be made to a WebAPI server hosted at `http://localhost:5104/weather`. Remember that if the WebAPI server is not hosting the webassembly itself, it needs to have a CORS policy configured to allow the webassembly origin to access the API endpoint from the browser.