RESTCLIENT.NET / 从源码到请求
可以运行的代码。
下面的示例使用 RestClient.Net 7.3.1 编译。页面与自动验证共用同一份源码,验证涵盖成功响应、HTTP 错误、连接失败、JSON 请求和取消操作。
从 GET 请求开始
入口方法从 JSONPlaceholder 获取文章,并输出明确的结果。克隆仓库、安装 .NET 9 SDK 后运行:
dotnet run --project Website/examples/Examples.csproj
此命令会向 JSONPlaceholder 发出真实请求。自动验证使用本地消息处理器,不会发出 HTTP 请求。
发送 JSON,保留完整结果
CreatePostAsync 将 JsonContent.Create(request) 作为 requestBody 传入。反序列化委托接收 HttpResponseMessage,因此需要读取 response.Content。成功响应转换为 Post,HTTP 错误保留文本格式的响应内容。
使用现有客户端工厂
GetUsingFactoryAsync 使用名为 posts 的客户端。在应用中通过 services.AddHttpClient("posts") 注册即可。请求使用绝对 URL,已有的处理器、认证和弹性配置仍然适用。
明确处理每一种结果
Result.Match 分别处理成功和失败,随后 HttpError.Match 明确区分 HTTP 响应错误与异常。取消令牌会传递到请求与反序列化操作;取消请求的结果是一个异常错误。
完整源码
中英文页面均展示同一个源码文件。其中的 Main 运行 GET 示例;POST 与客户端工厂方法可直接从应用调用。
using System.Net.Http.Json;
using Outcome;
using RestClient.Net;
using Urls;
namespace WebsiteExamples;
public static class RestClientExamples
{
// This runnable entry point calls JSONPlaceholder. Tests use a local handler instead.
public static async Task Main()
{
using var client = new HttpClient();
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var result = await GetPostAsync(client, 1, cancellation.Token);
Console.WriteLine(Describe(result));
}
public static Task<Result<Post, HttpError<string>>> GetPostAsync(
HttpClient client,
int id,
CancellationToken cancellationToken = default
) =>
client.GetAsync(
url: $"https://jsonplaceholder.typicode.com/posts/{id}".ToAbsoluteUrl(),
deserializeSuccess: ReadPostAsync,
deserializeError: ReadErrorAsync,
headers: new Dictionary<string, string> { ["Accept"] = "application/json" },
cancellationToken: cancellationToken
);
public static Task<Result<Post, HttpError<string>>> CreatePostAsync(
HttpClient client,
CreatePost request,
CancellationToken cancellationToken = default
) =>
client.PostAsync(
url: "https://jsonplaceholder.typicode.com/posts".ToAbsoluteUrl(),
requestBody: JsonContent.Create(request),
deserializeSuccess: ReadPostAsync,
deserializeError: ReadErrorAsync,
cancellationToken: cancellationToken
);
// Register a named client with services.AddHttpClient("posts") in your application.
public static Task<Result<Post, HttpError<string>>> GetUsingFactoryAsync(
IHttpClientFactory factory,
int id,
CancellationToken cancellationToken = default
) =>
factory.GetAsync(
clientName: "posts",
url: $"https://jsonplaceholder.typicode.com/posts/{id}".ToAbsoluteUrl(),
deserializeSuccess: ReadPostAsync,
deserializeError: ReadErrorAsync,
cancellationToken: cancellationToken
);
// Both success and failure are explicit. HTTP failures retain the response body.
public static string Describe(Result<Post, HttpError<string>> result) =>
result.Match(
onSuccess: post => $"Post {post.Id}: {post.Title}",
onError: error =>
error.Match(
onException: exception => $"Connection failed: {exception.Message}",
onErrorResponse: (body, status, headers) => $"HTTP {(int)status}: {body}"
)
);
// Deserialize<T> receives HttpResponseMessage: read its Content property.
private static async Task<Post> ReadPostAsync(
HttpResponseMessage response,
CancellationToken cancellationToken
) =>
await response.Content.ReadFromJsonAsync<Post>(cancellationToken)
?? throw new InvalidDataException("The response contained no post.");
private static Task<string> ReadErrorAsync(
HttpResponseMessage response,
CancellationToken cancellationToken
) => response.Content.ReadAsStringAsync(cancellationToken);
}
public sealed record Post(int UserId, int Id, string Title, string Body);
public sealed record CreatePost(int UserId, string Title, string Body);