Skip to content

Commit

Permalink
Moving other bits
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyzimarev committed Jun 25, 2024
1 parent adc0ce4 commit 65d9ad8
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 290 deletions.
145 changes: 0 additions & 145 deletions docs/docs/usage/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,148 +21,3 @@ Essentially, RestSharp is a wrapper around `HttpClient` that allows you to do th
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.
40 changes: 40 additions & 0 deletions docs/docs/usage/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,43 @@ One way of doing it is to use `RestClient` constructors that accept an instance

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<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.
29 changes: 29 additions & 0 deletions docs/docs/usage/execute.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,32 @@ var statusCode = client.PostJsonAsync("orders", request, cancellationToken);
```

The same two extensions also exist for `PUT` requests (`PutJsonAsync`);

## 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.

## JSON streaming

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.

40 changes: 40 additions & 0 deletions docs/docs/usage/request.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,43 @@ When you call `AddXmlBody`, it does the following for you:
Do not send XML string to `AddXmlBody`; it won't work!
:::

## 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.
Loading

0 comments on commit 65d9ad8

Please sign in to comment.