Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow put command without payload or response #31

Merged
merged 2 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ tab_width = 2
#### C# Coding Conventions ####
[*.cs]

# var preferences
csharp_style_var_elsewhere = false:suggestion
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:silent

# Expression-bodied members
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_lambdas = true:suggestion
Expand Down Expand Up @@ -272,25 +267,25 @@ dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase

# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case

dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case

# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
Expand Down
30 changes: 15 additions & 15 deletions src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/IApiException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,42 @@
namespace Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent;

/// <summary>
/// API 异常接口
/// Defines exceptions threw when doing an API call.
/// </summary>
/// <typeparam name="TException">异常类型。</typeparam>
/// <typeparam name="TException">The type of this API exception.</typeparam>
public interface IApiException<out TException>
where TException : Exception, IApiException<TException>
{
/// <summary>
/// HTTP 状态码,不适用则为 -1。
/// The HTTP status code, -1 if not applied.
/// </summary>
int StatusCode { get; }

/// <summary>
/// 错误信息。
/// The raw error message.
/// </summary>
string Message { get; }

/// <summary>
/// 显示给用户的错误信息。
/// The error message to display, can be null if such message is not available.
/// </summary>
string? UserFriendlyMessage { get; }

/// <summary>
/// 抛出异常。
/// Throw a <see cref="TException"/>.
/// </summary>
/// <param name="statusCode">HTTP 状态码,若不适用则为 -1。</param>
/// <param name="message">错误信息。</param>
/// <param name="userFriendlyMessage">给用户显示的错误信息。</param>
/// <param name="statusCode">HTTP status code, -1 if not available.</param>
/// <param name="message">The error message.</param>
/// <param name="userFriendlyMessage">The error message to display, can be null if such message is not available.</param>
[DoesNotReturn]
static abstract void Throw(int statusCode = -1, string message = "", string? userFriendlyMessage = null);

/// <summary>
/// 创建异常。
/// Create(but not throw) a <see cref="TException"/>.
/// </summary>
/// <param name="statusCode">HTTP 状态码,若不适用则为 -1。</param>
/// <param name="message">错误信息。</param>
/// <param name="userFriendlyMessage">给用户显示的错误信息。</param>
/// <returns></returns>
/// <param name="statusCode">HTTP status code, -1 if not available.</param>
/// <param name="message">The error message.</param>
/// <param name="userFriendlyMessage">The error message to display, can be null if such message is not available.</param>
/// <returns>A new instance of <see cref="TException"/>.</returns>
static abstract TException Create(int statusCode = -1, string message = "", string? userFriendlyMessage = null);
}
}
171 changes: 111 additions & 60 deletions src/Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent/ServiceAgentBase.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http.Json;

using Cnblogs.Architecture.Ddd.Infrastructure.Abstractions;

namespace Cnblogs.Architecture.Ddd.Cqrs.ServiceAgent;

/// <summary>
/// ServiceAgent 的基础类。
/// Base class for service agent.
/// </summary>
/// <typeparam name="TException">异常类型。</typeparam>
/// <typeparam name="TException">The type of exception that this service agent throws.</typeparam>
public abstract class ServiceAgentBase<TException>
where TException : Exception, IApiException<TException>
{
/// <summary>
/// 构造一个 <see cref="ServiceAgentBase{TException}"/>
/// Create a <see cref="ServiceAgentBase{TException}"/>.
/// </summary>
/// <param name="httpClient">用于访问 API 的 <see cref="HttpClient"/></param>
/// <param name="httpClient">The underlying <see cref="HttpClient"/> used to access the API.</param>
protected ServiceAgentBase(HttpClient httpClient)
{
HttpClient = httpClient;
}

/// <summary>
/// 用于访问 API 的 <see cref="HttpClient"/>
/// The underlying <see cref="HttpClient"/>.
/// </summary>
protected HttpClient HttpClient { get; }

/// <summary>
/// 发送一个 DELETE 请求。
/// Execute a command with DELETE method.
/// </summary>
/// <param name="url">目标 API 路径。</param>
/// <typeparam name="TResponse">返回结果类型。</typeparam>
/// <returns>返回结果。</returns>
/// <param name="url">The url.</param>
/// <typeparam name="TResponse">Response type.</typeparam>
/// <returns>The response.</returns>
public async Task<TResponse?> DeleteCommandAsync<TResponse>(string url)
{
try
Expand All @@ -47,9 +46,9 @@ protected ServiceAgentBase(HttpClient httpClient)
}

/// <summary>
/// 发起一个 DELETE 请求。
/// Execute a command with DELETE method.
/// </summary>
/// <param name="url">API 路径。</param>
/// <param name="url">The route of the API.</param>
public async Task DeleteCommandAsync(string url)
{
HttpResponseMessage response;
Expand All @@ -71,9 +70,9 @@ public async Task DeleteCommandAsync(string url)
}

/// <summary>
/// 发起一个 POST 请求。
/// Execute a command with POST method.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="url">The route of the API.</param>
public async Task PostCommandAsync(string url)
{
HttpResponseMessage response;
Expand All @@ -95,11 +94,11 @@ public async Task PostCommandAsync(string url)
}

/// <summary>
/// 发起一个带 Body 的 POST 请求。
/// Execute a command with POST method and payload.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="payload">请求。</param>
/// <typeparam name="TPayload">请求类型。</typeparam>
/// <param name="url">The route of the API.</param>
/// <param name="payload">The request body.</param>
/// <typeparam name="TPayload">The type of request body.</typeparam>
public async Task PostCommandAsync<TPayload>(string url, TPayload payload)
{
HttpResponseMessage response;
Expand All @@ -121,13 +120,13 @@ public async Task PostCommandAsync<TPayload>(string url, TPayload payload)
}

/// <summary>
/// 发起一个带 Body 的 POST 请求。
/// Execute a command with POST method and payload.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="payload">请求。</param>
/// <typeparam name="TResponse">返回类型。</typeparam>
/// <typeparam name="TPayload">请求类型。</typeparam>
/// <returns></returns>
/// <param name="url">The route of the API.</param>
/// <param name="payload">The request body.</param>
/// <typeparam name="TResponse">The type of response body.</typeparam>
/// <typeparam name="TPayload">The type of request body.</typeparam>
/// <returns>The response body.</returns>
public async Task<TResponse> PostCommandAsync<TResponse, TPayload>(string url, TPayload payload)
{
HttpResponseMessage response;
Expand Down Expand Up @@ -160,13 +159,63 @@ public async Task<TResponse> PostCommandAsync<TResponse, TPayload>(string url, T
}

/// <summary>
/// 发起一个 PUT 请求。
/// Execute a command with PUT method and payload.
/// </summary>
/// <param name="url">The route of API.</param>
public async Task PutCommandAsync(string url)
{
HttpResponseMessage response;
try
{
response = await HttpClient.PutAsync(url, new StringContent(string.Empty));
}
catch (Exception e)
{
ThrowApiException(HttpMethod.Put, url, e);
return;
}

if (response.IsSuccessStatusCode == false)
{
var content = await response.Content.ReadAsStringAsync();
ThrowApiException(HttpMethod.Put, response.StatusCode, url, content);
}
}

/// <summary>
/// Execute a command with PUT method and payload.
/// </summary>
/// <param name="url">The route of API.</param>
/// <param name="payload">The request body.</param>
/// <typeparam name="TPayload">The type of request body.</typeparam>
public async Task PutCommandAsync<TPayload>(string url, TPayload payload)
{
HttpResponseMessage response;
try
{
response = await HttpClient.PutAsJsonAsync(url, payload);
}
catch (Exception e)
{
ThrowApiException(HttpMethod.Put, url, payload, e);
return;
}

if (response.IsSuccessStatusCode == false)
{
var content = await response.Content.ReadAsStringAsync();
ThrowApiException(HttpMethod.Put, response.StatusCode, url, payload, content);
}
}

/// <summary>
/// Execute a command with PUT method and payload.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="payload">请求内容。</param>
/// <typeparam name="TResponse">返回结果类型。</typeparam>
/// <typeparam name="TPayload">请求类型。</typeparam>
/// <returns></returns>
/// <param name="url">The route of API.</param>
/// <param name="payload">The request body.</param>
/// <typeparam name="TResponse">The type of response body.</typeparam>
/// <typeparam name="TPayload">The type of request body.</typeparam>
/// <returns>The response body.</returns>
public async Task<TResponse> PutCommandAsync<TResponse, TPayload>(string url, TPayload payload)
{
HttpResponseMessage response;
Expand Down Expand Up @@ -199,10 +248,11 @@ public async Task<TResponse> PutCommandAsync<TResponse, TPayload>(string url, TP
}

/// <summary>
/// 获取内容。
/// Query item with GET method.
/// </summary>
/// <param name="url">路径。</param>
/// <typeparam name="T">结果类型。</typeparam>
/// <param name="url">The route of the API.</param>
/// <typeparam name="T">The type of item to get.</typeparam>
/// <returns>The query result, can be null if item does not exists or status code is 404.</returns>
public async Task<T?> GetItemAsync<T>(string url)
{
try
Expand All @@ -222,14 +272,14 @@ public async Task<TResponse> PutCommandAsync<TResponse, TPayload>(string url, TP
}

/// <summary>
/// 批量获取实体。
/// Batch get items with GET method.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="paramName">参数名称。</param>
/// <param name="ids">主键列表。</param>
/// <typeparam name="TResponse">返回类型。</typeparam>
/// <typeparam name="TId">主键类型。</typeparam>
/// <returns></returns>
/// <param name="url">The route of the API.</param>
/// <param name="paramName">The name of id field.</param>
/// <param name="ids">The id list.</param>
/// <typeparam name="TResponse">The type of the query result item.</typeparam>
/// <typeparam name="TId">The type of the id.</typeparam>
/// <returns>A list of items that contains id that in <paramref name="ids"/>, the order or count of the items are not guaranteed.</returns>
public async Task<List<TResponse>> BatchGetItemsAsync<TResponse, TId>(
string url,
string paramName,
Expand Down Expand Up @@ -260,13 +310,13 @@ public async Task<List<TResponse>> BatchGetItemsAsync<TResponse, TId>(
}

/// <summary>
/// 获取分页列表。
/// Get paged list of items based on url.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="pagingParams">页码。</param>
/// <param name="orderByString">分页大小。</param>
/// <typeparam name="TItem">实体类型。</typeparam>
/// <returns></returns>
/// <param name="url">The route of the API.</param>
/// <param name="pagingParams">The paging parameters, including page size and page index.</param>
/// <param name="orderByString">Specifies the order of items to return.</param>
/// <typeparam name="TItem">The type of items to query.</typeparam>
/// <returns>The paged list of items. An empty list is returned when there is no result.</returns>
public async Task<PagedList<TItem>> ListPagedItemsAsync<TItem>(
string url,
PagingParams? pagingParams = null,
Expand All @@ -276,13 +326,14 @@ public async Task<PagedList<TItem>> ListPagedItemsAsync<TItem>(
}

/// <summary>
/// 获取分页列表。
/// Get paged list of items based on url.
/// </summary>
/// <param name="url">路径。</param>
/// <param name="pageIndex">页码。</param>
/// <param name="pageSize">分页大小。</param>
/// <param name="orderByString">排序字符串。</param>
/// <typeparam name="TItem">实体类型。</typeparam>
/// <param name="url">The route of the API.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="orderByString">Specifies the order of items to return.</param>
/// <typeparam name="TItem">The type of items to query.</typeparam>
/// <returns>The paged list of items. An empty list is returned when there is no result.</returns>
public async Task<PagedList<TItem>> ListPagedItemsAsync<TItem>(
string url,
int? pageIndex,
Expand Down Expand Up @@ -315,14 +366,14 @@ public async Task<PagedList<TItem>> ListPagedItemsAsync<TItem>(
}

/// <summary>
/// 处理抛出异常的情况。
/// Throw exceptions.
/// </summary>
/// <param name="method">请求方法。</param>
/// <param name="statusCode">状态码,若不适用则是 -1。</param>
/// <param name="url">请求的 Url</param>
/// <param name="requestBody">请求内容。</param>
/// <param name="response">返回内容。</param>
/// <param name="e">异常。</param>
/// <param name="method">The method for this request.</param>
/// <param name="statusCode">HTTP status code, -1 if not available.</param>
/// <param name="url">The URL to request.</param>
/// <param name="requestBody">The request body.</param>
/// <param name="response">The response body.</param>
/// <param name="e">The exception.</param>
[DoesNotReturn]
protected virtual void ThrowApiException(
HttpMethod method,
Expand Down Expand Up @@ -352,4 +403,4 @@ private void ThrowApiException(

private void ThrowApiException(HttpMethod method, string url, object? body, Exception e)
=> ThrowApiException(method, -1, url, body, null, e);
}
}