Skip to content

Commit

Permalink
Fixing more broken links
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyzimarev committed Jun 25, 2024
1 parent e169671 commit adc0ce4
Show file tree
Hide file tree
Showing 9 changed files with 688 additions and 628 deletions.
4 changes: 2 additions & 2 deletions docs/docs/advanced/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/client.md#simple-factory)) to get an `HttpClient` instance | No |

Here's an example of how to create a client using client options:

Expand Down Expand Up @@ -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/request.md) page about creating requests with parameters.
2 changes: 1 addition & 1 deletion docs/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ var client = new RestClient(options);
var timeline = await client.GetJsonAsync<HomeTimeline>("statuses/home_timeline.json", cancellationToken);
```

Read [here](usage/usage.md#json-requests) about making JSON calls without preparing a request object.
Read [here](usage/execute.md#json-requests) about making JSON calls without preparing a request object.

### Content type

Expand Down
168 changes: 168 additions & 0 deletions docs/docs/usage/basics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
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<T>` return a generic instance of `RestResponse<T>` 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<string>` | 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<T>` 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<T>`, which returns an `IAsyncEnumerable<T>`:

```csharp
public async IAsyncEnumerable<SearchResponse> SearchStream(
[EnumeratorCancellation] CancellationToken cancellationToken = default
) {
var response = _client.StreamJsonAsync<TwitterSingleObject<SearchResponse>>(
"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<byte[]`. It will read the binary response to the end, and return the whole binary content as a byte array. It works well for downloading smaller files.

For larger responses, you can use `DownloadStreamAsync` that returns `Task<Stream>`. This function allows you to open a stream reader and asynchronously stream large responses to memory or disk.

## 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<WeatherForecast[]>("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.
74 changes: 74 additions & 0 deletions docs/docs/usage/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
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);
```

## 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` is be used to set the base address of the `HttpClient` instance if base address is not set there already.
- `MaxTimeout` is used to cancel the call using the cancellation token source, so
- `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).

8 changes: 6 additions & 2 deletions docs/docs/usage/example.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -46,7 +50,7 @@ public class TwitterClient : ITwitterClient, IDisposable {
}

public async Task<TwitterUser> GetUser(string user) {
var response = await _client.GetJsonAsync<TwitterSingleObject<TwitterUser>>(
var response = await _client.GetAsync<TwitterSingleObject<TwitterUser>>(
"users/by/username/{user}",
new { user }
);
Expand Down Expand Up @@ -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.
:::

Expand Down
Loading

0 comments on commit adc0ce4

Please sign in to comment.