diff --git a/docs/usage.md b/docs/usage.md index e6878f2ef..5b76b15e0 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -150,6 +150,30 @@ services.AddSingleton( ); ``` +### 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: @@ -166,9 +190,23 @@ 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. -### Http Header +### 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 +``` + +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. -Adds the 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. +```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. @@ -191,6 +229,14 @@ 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. + #### AddObject You can avoid calling `AddParameter` multiple times if you collect all the parameters in an object, and then use `AddObject`. @@ -241,6 +287,26 @@ var request = new RestRequest("health/{entity}/status") 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: @@ -252,6 +318,8 @@ We recommend using `AddJsonBody` or `AddXmlBody` methods instead of `AddParamete When you make a `POST`, `PUT` or `PATCH` request and added `GetOrPost` [parameters](#get-or-post), 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`. +It is not possible to add client-level default body parameters. + #### AddStringBody 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: @@ -322,6 +390,14 @@ To do so, set the `encode` argument to `false` when adding the parameter: 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. @@ -354,7 +430,7 @@ Task> ExecutePostAsync(RestRequest request, CancellationToken Task> ExecutePutAsync(RestRequest request, CancellationToken cancellationToken) ``` -All the overloads that return `RestResponse` or `RestResponse` don't throw an exception if the server returns an error. Read more about it [here](error-handling.md). +All the overloads with names starting with `Execute` don't throw an exception if the server returns an error. Read more about it [here](error-handling.md). If you just need a deserialized response, you can use one of the extensions: @@ -369,6 +445,17 @@ 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) +``` + ### JSON requests To make a simple `GET` call and get a deserialized JSON response with a pre-formed resource string, use this: @@ -474,15 +561,7 @@ One way of doing it is to use `RestClient` constructors that accept an instance - `UserAgent` will be set if the `User-Agent` header is not set on the `HttpClient` instance already. - `Expect100Continue` -Another option is to use a simple HTTP client factory. It is a static factory, which holds previously instantiated `HttpClient` instances. It can be used to create `RestClient` instances that share the same `HttpClient` instance. The cache key is the `BaseUrl` provided in the options. When you opt-in to use the factory and don't set `BaseUrl`, the `RestClient` constructor will crash. - -```csharp -var client = new RestClient(new Uri("https://example.org/api"), useClientFactory: true); -``` - -::: warning -Note that the `RestClient` constructor will not reconfigure the `HttpClient` instance if it's already in the cache. Therefore, you should not try using the factory when providing different options for the same base URL. -::: +Another option is to use a simple HTTP client factory as described [above](#simple-factory). ## Blazor support diff --git a/docs/v107/README.md b/docs/v107/README.md index 8088c5ae8..97305c749 100644 --- a/docs/v107/README.md +++ b/docs/v107/README.md @@ -224,12 +224,12 @@ Below, you can find members of `IRestClient` and `IRestRequest` with their corre | `IRestClient` member | Where is it now? | |:------------------------------------------------------------------------------------------------|:-----------------------------------| -| `CookieContainer` | `RestClient` | +| `CookieContainer` | `RestClientOptions` | | `AutomaticDecompression` | `RestClientOptions`, changed type | | `MaxRedirects` | `RestClientOptions` | | `UserAgent` | `RestClientOptions` | | `Timeout` | `RestClientOptions`, `RestRequest` | -| `Authenticator` | `RestClient` | +| `Authenticator` | `RestClientOptions` | | `BaseUrl` | `RestClientOptions` | | `Encoding` | `RestClientOptions` | | `ThrowOnDeserializationError` | `RestClientOptions` | @@ -249,12 +249,8 @@ Below, you can find members of `IRestClient` and `IRestRequest` with their corre | `ReadWriteTimeout` | Not supported | | `UseSynchronizationContext` | Not supported | | `DefaultParameters` | `RestClient` | -| `UseSerializer(Func serializerFactory)` | `RestClient` | -| `UseSerializer()` | `RestClient` | -| `Deserialize(IRestResponse response)` | `RestClient` | -| `BuildUri(IRestRequest request)` | `RestClient` | -| `UseUrlEncoder(Func encoder)` | Extension | -| `UseQueryEncoder(Func queryEncoder)` | Extension | +| `Deserialize(IRestResponse response)` | `RestSerializers` | +| `BuildUri(IRestRequest request)` | Extension | | `ExecuteAsync(IRestRequest request, CancellationToken cancellationToken)` | `RestClient` | | `ExecuteAsync(IRestRequest request, Method httpMethod, CancellationToken cancellationToken)` | Extension | | `ExecuteAsync(IRestRequest request, Method httpMethod, CancellationToken cancellationToken)` | Extension | @@ -272,7 +268,7 @@ Below, you can find members of `IRestClient` and `IRestRequest` with their corre | `ExecuteAsPost(IRestRequest request, string httpMethod)` | Deprecated | | `ExecuteAsGet(IRestRequest request, string httpMethod)` | Deprecated | | `ExecuteAsPost(IRestRequest request, string httpMethod)` | Deprecated | -| `BuildUriWithoutQueryParameters(IRestRequest request)` | Removed | +| `BuildUriWithoutQueryParameters(IRestRequest request)` | Extension | | `ConfigureWebRequest(Action configurator)` | Removed | | `AddHandler(string contentType, Func deserializerFactory)` | Removed | | `RemoveHandler(string contentType)` | Removed | diff --git a/src/RestSharp/Options/RestClientOptions.cs b/src/RestSharp/Options/RestClientOptions.cs index d5efd312e..a07daf55d 100644 --- a/src/RestSharp/Options/RestClientOptions.cs +++ b/src/RestSharp/Options/RestClientOptions.cs @@ -136,6 +136,12 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba /// public string? BaseHost { get; set; } + /// + /// Custom cookie container to be used for requests. RestSharp will not assign the container to the message handler, + /// but will fetch cookies from it and set them on the request. + /// + public CookieContainer? CookieContainer { get; set; } + /// /// Maximum request duration in milliseconds. When the request timeout is specified using , /// the lowest value between the client timeout and request timeout will be used. diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs index 76318ce8e..821f4f487 100644 --- a/src/RestSharp/RestClient.Async.cs +++ b/src/RestSharp/RestClient.Async.cs @@ -106,11 +106,16 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo // Make sure we have a cookie container if not provided in the request var cookieContainer = request.CookieContainer ??= new CookieContainer(); - var headers = new RequestHeaders(); - headers.AddHeaders(request.Parameters); - headers.AddHeaders(DefaultParameters); - headers.AddAcceptHeader(AcceptedContentTypes); - headers.AddCookieHeaders(cookieContainer, url); + var headers = new RequestHeaders() + .AddHeaders(request.Parameters) + .AddHeaders(DefaultParameters) + .AddAcceptHeader(AcceptedContentTypes) + .AddCookieHeaders(cookieContainer, url); + + if (Options.CookieContainer != null) { + headers.AddCookieHeaders(Options.CookieContainer, url); + } + message.AddHeaders(headers); if (request.OnBeforeRequest != null) await request.OnBeforeRequest(message).ConfigureAwait(false); diff --git a/src/RestSharp/RestClient.Extensions.Params.cs b/src/RestSharp/RestClient.Extensions.Params.cs index c4019f5c6..840ef74b3 100644 --- a/src/RestSharp/RestClient.Extensions.Params.cs +++ b/src/RestSharp/RestClient.Extensions.Params.cs @@ -13,14 +13,17 @@ // limitations under the License. // +using System.Net; +using System.Text; + namespace RestSharp; public static partial class RestClientExtensions { /// /// Add a parameter to use on every request made with this client instance /// - /// RestClient instance - /// Parameter to add + /// instance + /// to add /// public static IRestClient AddDefaultParameter(this IRestClient client, Parameter parameter) { client.DefaultParameters.AddParameter(parameter); @@ -31,7 +34,7 @@ public static IRestClient AddDefaultParameter(this IRestClient client, Parameter /// Adds a default HTTP parameter (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT) /// Used on every request made by this client instance /// - /// instance + /// instance /// Name of the parameter /// Value of the parameter /// This request @@ -46,7 +49,7 @@ public static IRestClient AddDefaultParameter(this IRestClient client, string na /// - RequestBody: Used by AddBody() (not recommended to use directly) /// Used on every request made by this client instance /// - /// instance + /// instance /// Name of the parameter /// Value of the parameter /// The type of parameter to add @@ -57,7 +60,7 @@ public static IRestClient AddDefaultParameter(this IRestClient client, string na /// /// Adds a default header to the RestClient. Used on every request made by this client instance. /// - /// instance + /// instance /// Name of the header to add /// Value of the header to add /// @@ -79,7 +82,7 @@ public static IRestClient AddDefaultHeaders(this IRestClient client, Dictionary< /// /// Adds a default URL segment parameter to the RestClient. Used on every request made by this client instance. /// - /// instance + /// instance /// Name of the segment to add /// Value of the segment to add /// @@ -89,7 +92,7 @@ public static IRestClient AddDefaultUrlSegment(this IRestClient client, string n /// /// Adds a default URL query parameter to the RestClient. Used on every request made by this client instance. /// - /// instance + /// instance /// Name of the query parameter to add /// Value of the query parameter to add ///