Skip to content

Commit

Permalink
Doc versions and response section
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyzimarev committed Jun 25, 2024
1 parent d6c4528 commit 4713b0a
Show file tree
Hide file tree
Showing 34 changed files with 4,783 additions and 631 deletions.
54 changes: 52 additions & 2 deletions docs/docs/usage/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,30 @@ Task<RestResponse> PatchAsync(RestRequest request, CancellationToken cancellatio
Task<RestResponse> 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<TResponse>(resource)`, and `GetAsync<TResponse>(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.GetJsonAsync<TResponse>("endpoint?foo=bar", cancellationToken);
var response = await client.GetAsync<TResponse>("endpoint?foo=bar", cancellationToken);
```

:::
In v111, `GetJsonAsync<T>` is renamed to `GetAsync<T>`.
:::

You can also use a more advanced extension that uses an object to compose the resource string:

```csharp
Expand All @@ -422,7 +438,7 @@ var args = new {
foo = "bar"
};
// Will make a call to https://example.org/endpoint/123?foo=bar
var response = await client.GetJsonAsync<TResponse>("endpoint/{id}", args, cancellationToken);
var response = await client.GetAsync<TResponse>("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.
Expand All @@ -445,6 +461,40 @@ 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<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>`:
Expand Down
4 changes: 4 additions & 0 deletions docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const config: Config = {
position: "left",
label: "Documentation",
},
{
href: "/migration",
label: "Migration"
},
{
href: "/support",
label: "Support",
Expand Down
10 changes: 5 additions & 5 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.2.1",
"@docusaurus/preset-classic": "3.2.1",
"@docusaurus/core": "^3.4.0",
"@docusaurus/preset-classic": "^3.4.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.1",
"@docusaurus/tsconfig": "3.2.1",
"@docusaurus/types": "3.2.1",
"@docusaurus/module-type-aliases": "^3.4.0",
"@docusaurus/tsconfig": "^3.4.0",
"@docusaurus/types": "^3.4.0",
"typescript": "~5.2.2"
},
"browserslist": {
Expand Down
16 changes: 11 additions & 5 deletions docs/src/pages/v107.md → docs/src/pages/migration.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: RestSharp Next (v107+)
title: Migration from v106 and earlier
---

## RestSharp v107+
## New RestSharp

RestSharp got a major upgrade in v107, which contains quite a few breaking changes.

The most important change is that RestSharp stop using the legacy `HttpWebRequest` class, and uses well-known 'HttpClient' instead.
The most important change is that RestSharp stop using the legacy `HttpWebRequest` class, and uses well-known `HttpClient` instead.
This move solves lots of issues, like hanging connections due to improper `HttpClient` instance cache, updated protocols support, and many other problems.

Another big change is that `SimpleJson` is retired completely from the code base. Instead, RestSharp uses `JsonSerializer` from the `System.Text.Json` package, which is the default serializer for ASP.NET Core.
Expand All @@ -19,7 +19,7 @@ Finally, most of the interfaces are now gone.

The `IRestClient` interface is deprecated in v107, but brought back in v109. The new interface, however, has a much smaller API compared to previous versions. You will be using the `RestClient` class instance.

Most of the client options are moved to `RestClientOptions`. If you can't find the option you used to set on `IRestClient`, check the options, it's probably there.
Most of the client options are moved to `RestClientOptions`. If you can't find the option you used to set on `IRestClient`, check the options; it's probably there.

This is how you can instantiate the client using the simplest possible way:

Expand All @@ -32,13 +32,19 @@ For customizing the client, use `RestClientOptions`:
```csharp
var options = new RestClientOptions("https://api.myorg.com") {
ThrowOnAnyError = true,
Timeout = 1000
Timeout = TimeSpan.FromSeconds(1)
};
var client = new RestClient(options);
```

You can still change serializers and add default parameters to the client.

:::
Note that client options cannot be changed after the client is instantiated.
It's because the client constructor either uses those options to configure its internal `HttpClient`, `HttpMessageHandler` or for making requests.
Even though options that are used for making requests can be changed in theory, making those options mutable would make `RestClient` not thread-safe.
:::

### RestClient lifecycle

Do not instantiate `RestClient` for each HTTP call. RestSharp creates a new instance of `HttpClient` internally, and you will get lots of hanging connections, and eventually exhaust the connection pool.
Expand Down
7 changes: 7 additions & 0 deletions docs/versioned_docs/version-v110/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"label": "Advanced topics",
"position": 4,
"link": {
"type": "generated-index"
}
}
185 changes: 185 additions & 0 deletions docs/versioned_docs/version-v110/advanced/authenticators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Authenticators

RestSharp includes authenticators for basic HTTP, OAuth1 and token-based (JWT and OAuth2).

There are two ways to set the authenticator: client-wide or per-request.

Set the client-wide authenticator by assigning the `Authenticator` property of `RestClientOptions`:

```csharp
var options = new RestClientOptions("https://example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var client = new RestClient(options);
```

To set the authenticator per-request, assign the `Authenticator` property of `RestRequest`:

```csharp
var request = new RestRequest("/api/users/me") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var response = await client.ExecuteAsync(request, cancellationToken);
```

## Basic authentication

The `HttpBasicAuthenticator` allows you pass a username and password as a basic `Authorization` header using a base64 encoded string.

```csharp
var options = new RestClientOptions("https://example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var client = new RestClient(options);
```

## OAuth1

For OAuth1 authentication the `OAuth1Authenticator` class provides static methods to help generate an OAuth authenticator.
OAuth1 authenticator will add the necessary OAuth parameters to the request, including signature.

The authenticator will use `HMAC SHA1` to create a signature by default.
Each static function to create the authenticator allows you to override the default and use another method to generate the signature.

### Request token

Getting a temporary request token is the usual first step in the 3-legged OAuth1 flow.
Use `OAuth1Authenticator.ForRequestToken` function to get the request token authenticator.
This method requires a `consumerKey` and `consumerSecret` to authenticate.

```csharp
var options = new RestClientOptions("https://api.twitter.com") {
Authenticator = OAuth1Authenticator.ForRequestToken(consumerKey, consumerSecret)
};
var client = new RestClient(options);
var request = new RestRequest("oauth/request_token");
```

The response should contain the token and the token secret, which can then be used to complete the authorization process.
If you need to provide the callback URL, assign the `CallbackUrl` property of the authenticator to the callback destination.

### Access token

Getting an access token is the usual third step in the 3-legged OAuth1 flow.
This method retrieves an access token when provided `consumerKey`, `consumerSecret`, `oauthToken`, and `oauthTokenSecret`.
If you don't have a token for this call, you need to make a call to get the request token as described above.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, oauthToken, oauthTokenSecret
);
var options = new RestClientOptions("https://api.twitter.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
var request = new RestRequest("oauth/access_token");
```

If the second step in 3-leg OAuth1 flow returned a verifier value, you can use another overload of `ForAccessToken`:

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, oauthToken, oauthTokenSecret, verifier
);
```

The response should contain the access token that can be used to make calls to protected resources.

For refreshing access tokens, use one of the two overloads of `ForAccessToken` that accept `sessionHandle`.

### Protected resource

When the access token is available, use `ForProtectedResource` function to get the authenticator for accessing protected resources.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, accessToken, accessTokenSecret
);
var options = new RestClientOptions("https://api.twitter.com/1.1") {
Authenticator = authenticator
};
var client = new RestClient(options);
var request = new RestRequest("statuses/update.json", Method.Post)
.AddParameter("status", "Hello Ladies + Gentlemen, a signed OAuth request!")
.AddParameter("include_entities", "true");
```

### xAuth

xAuth is a simplified version of OAuth1. It allows sending the username and password as `x_auth_username` and `x_auth_password` request parameters and directly get the access token. xAuth is not widely supported, but RestSharp still allows using it.

Create an xAuth authenticator using `OAuth1Authenticator.ForClientAuthentication` function:

```csharp
var authenticator = OAuth1Authenticator.ForClientAuthentication(
consumerKey, consumerSecret, username, password
);
```

### 0-legged OAuth

The access token authenticator can be used in 0-legged OAuth scenarios by providing `null` for the `consumerSecret`.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, null, oauthToken, oauthTokenSecret
);
```

## OAuth2

RestSharp has two very simple authenticators to send the access token as part of the request.

`OAuth2UriQueryParameterAuthenticator` accepts the access token as the only constructor argument, and it will send the provided token as a query parameter `oauth_token`.

`OAuth2AuthorizationRequestHeaderAuthenticator` has two constructors. One only accepts a single argument, which is the access token. The other constructor also allows you to specify the token type. The authenticator will then add an `Authorization` header using the specified token type or `OAuth` as the default token type, and the token itself.

For example:

```csharp
var authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(
token, "Bearer"
);
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

The code above will tell RestSharp to send the bearer token with each request as a header. Essentially, the code above does the same as the sample for `JwtAuthenticator` below.

As those authenticators don't do much to get the token itself, you might be interested in looking at our [sample OAuth2 authenticator](../usage/example.md#authenticator), which requests the token on its own.

## JWT

The JWT authentication can be supported by using `JwtAuthenticator`. It is a very simple class that can be constructed like this:

```csharp
var authenticator = new JwtAuthenticator(myToken);
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

For each request, it will add an `Authorization` header with the value `Bearer <your token>`.

As you might need to refresh the token from, you can use the `SetBearerToken` method to update the token.

## Custom authenticator

You can write your own implementation by implementing `IAuthenticator` and
registering it with your RestClient:

```csharp
var authenticator = new SuperAuthenticator(); // implements IAuthenticator
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

The `Authenticate` method is the very first thing called upon calling `RestClient.Execute` or `RestClient.Execute<T>`.
It gets the `RestRequest` currently being executed giving you access to every part of the request data (headers, parameters, etc.)

You can find an example of a custom authenticator that fetches and uses an OAuth2 bearer token [here](../usage/example.md#authenticator).
Loading

0 comments on commit 4713b0a

Please sign in to comment.