Browse Source

Add JSON converter and update documents.

pull/14164/head
maliming 4 years ago
parent
commit
cc92a2fe59
No known key found for this signature in database GPG Key ID: 96224957E51C89E
  1. 38
      docs/en/Dapr/Index.md
  2. 40
      docs/zh-Hans/Dapr/Index.md
  3. 18
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs
  4. 40
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprEventsController.cs
  5. 44
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestConverter.cs
  6. 33
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestConverterFactory.cs
  7. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestJsonNamingPolicy.cs
  8. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpDaprSubscriptionRequest.cs

38
docs/en/Dapr/Index.md

@ -62,27 +62,6 @@ Alternatively, you can configure the options in the `Dapr` section of your `apps
### Injecting DaprClient
ABP registers the `DaprClient` class to the [dependency injection](../Dependency-Injection.md) system. So, you can inject and use it whenever you need:
````csharp
public class MyService : ITransientDependency
{
private readonly DaprClient _daprClient;
public MyService(DaprClient daprClient)
{
_daprClient = daprClient;
}
public async Task DoItAsync()
{
// TODO: Use the injected _daprClient object
}
}
````
Injecting `DaprClient` is the recommended way of using it in your application code. When you inject it, the `IAbpDaprClientFactory` service is used to create it, which is explained in the next section.
### IAbpDaprClientFactory
`IAbpDaprClientFactory` can be used to create `DaprClient` or `HttpClient` objects to perform operations on Dapr. It uses `AbpDaprOptions`, so you can configure the settings in a central place.
@ -209,7 +188,7 @@ ABP provides the following endpoints to receive events from Dapr:
* `dapr/subscribe`: Dapr uses this endpoint to get a list of subscriptions from the application. ABP automatically returns all the subscriptions for your distributed event handler classes and custom controller actions with the `Topic` attribute.
* `api/abp/dapr/event`: The unified endpoint to receive all the events from Dapr. ABP dispatches the events to your event handlers based on the topic name.
> **Since ABP provides the standard `dapr/subscribe` endpoint, you should not manually call the `app.MapSubscribeHandler()` method of Dapr.** You can use the `app.UseCloudEvents()` middleware in your ASP.NET Core pipeline if you want to support the [CloudEvents](https://cloudevents.io/) standard.
> **Since ABP will call `MapSubscribeHandler` internally, you should not manually call it anymore.** You can use the `app.UseCloudEvents()` middleware in your ASP.NET Core pipeline if you want to support the [CloudEvents](https://cloudevents.io/) standard.
### Usage
@ -270,15 +249,18 @@ In addition to ABP's standard distributed event bus system, you can also use Dap
````csharp
public class MyService : ITransientDependency
{
private readonly DaprClient _daprClient;
private readonly IAbpDaprClientFactory _daprClientFactory;
public MyService(DaprClient daprClient)
public MyService(IAbpDaprClientFactory daprClientFactory)
{
_daprClient = daprClient;
_daprClientFactory = daprClientFactory;
}
public async Task DoItAsync()
{
// Create a DaprClient object with default options
DaprClient _daprClient = await _daprClientFactory.CreateAsync();
await _daprClient.PublishEventAsync(
"pubsub", // pubsub name
"StockChanged", // topic name
@ -299,8 +281,7 @@ public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync(
[FromBody] StockCountChangedEto model)
public async Task<IActionResult> TestRouteAsync([FromBody] AbpDaprSubscriptionRequest<StockCountChangedEto> model)
{
HttpContext.ValidateDaprAppApiToken();
@ -430,8 +411,7 @@ public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync(
[FromBody] StockCountChangedEto model)
public async Task<IActionResult> TestRouteAsync([FromBody] AbpDaprSubscriptionRequest<StockCountChangedEto> model)
{
// Validate the App API token!
HttpContext.ValidateDaprAppApiToken();

40
docs/zh-Hans/Dapr/Index.md

@ -62,27 +62,6 @@ Configure<AbpDaprOptions>(options =>
### 注入DaprClient
ABP 将 `DaprClient` 类注册到 [依赖注入](../Dependency-Injection.md) 系统中.因此,你可以在需要时注入并使用它:
````csharp
public class MyService : ITransientDependency
{
private readonly DaprClient _daprClient;
public MyService(DaprClient daprClient)
{
_daprClient = daprClient;
}
public async Task DoItAsync()
{
// TODO: Use the injected _daprClient object
}
}
````
注入 `DaprClient` 是在应用程序代码中使用它的推荐方法.当你注入它时,将使用 `IAbpDaprClientFactory` 服务创建它,这会在下一节中将进行说明.
### IAbpDaprClientFactory
`IAbpDaprClientFactory` 可用于创建 `DaprClient` 或 `HttpClient` 对象来执行对 Dapr 的操作.它使用 `AbpDaprOptions`,因此你可以配置设置.
@ -209,7 +188,7 @@ ABP提供了以下端点来接收来自Dapr的事件:
* `dapr/subscribe`: Dapr使用此端点从应用程序获取订阅列表.ABP会自动返回所有分布式事件处理程序类和具有`Topic`属性的自定义控制器操作的订阅.
* `api/abp/dapr/event`: 用于接收来自Dapr的所有事件的统一端点.ABP根据主题名称将事件分派给您的事件处理程序.
> **由于ABP提供了标准的`dapr/subscribe`端点,所以你不应该手动调用Dapr的`app.MapSubscribeHandler()`方法.** 如果你想支持[CloudEvents](https://cloudevents.io/)标准,你可以在你的ASP.NET Core管道中使用`app.UseCloudEvents()`中间件.
> **由于ABP会在内部调用`MapSubscribeHandler` 方法,所以你不应该手动调用了.** 如果你想支持[CloudEvents](https://cloudevents.io/)标准,你可以在你的ASP.NET Core管道中使用`app.UseCloudEvents()`中间件.
### 用法
@ -270,16 +249,19 @@ public class MyHandler :
````csharp
public class MyService : ITransientDependency
{
private readonly DaprClient _daprClient;
private readonly IAbpDaprClientFactory _daprClientFactory;
public MyService(DaprClient daprClient)
public MyService(IAbpDaprClientFactory daprClientFactory)
{
_daprClient = daprClient;
_daprClientFactory = daprClientFactory;
}
public async Task DoItAsync()
{
await _daprClient.PublishEventAsync(
// Create a DaprClient object with default options
DaprClient daprClient = await _daprClientFactory.CreateAsync();
await daprClient.PublishEventAsync(
"pubsub", // pubsub name
"StockChanged", // topic name
new StockCountChangedEto // event data
@ -299,8 +281,7 @@ public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync(
[FromBody] StockCountChangedEto model)
public async Task<IActionResult> TestRouteAsync([FromBody] AbpDaprSubscriptionRequest<StockCountChangedEto> model)
{
HttpContext.ValidateDaprAppApiToken();
@ -430,8 +411,7 @@ public class MyController : AbpController
{
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> TestRouteAsync(
[FromBody] StockCountChangedEto model)
public async Task<IActionResult> TestRouteAsync([FromBody] AbpDaprSubscriptionRequest<StockCountChangedEto> model)
{
// Validate the App API token!
HttpContext.ValidateDaprAppApiToken();

18
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/AbpAspNetCoreMvcDaprEventBusModule.cs

@ -1,11 +1,14 @@
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using Dapr;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Json;
using Volo.Abp.Dapr;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Dapr;
@ -22,6 +25,12 @@ public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddOptions<JsonOptions>()
.Configure<IServiceProvider>((options, serviceProvider) =>
{
options.JsonSerializerOptions.Converters.Add(new AbpDaprSubscriptionRequestConverterFactory(serviceProvider.GetRequiredService<IDaprSerializer>()));
});
var subscribeOptions = context.Services.ExecutePreConfiguredActions<AbpSubscribeOptions>();
Configure<AbpEndpointRouterOptions>(options =>
@ -38,6 +47,13 @@ public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
if (subscriptions.Any(x => x.PubsubName == daprEventBusOptions.PubSubName && x.Topic == eventName))
{
// Controllers with a [Topic] attribute can replace built-in event handlers.
continue;
}
subscriptions.Add(new AbpSubscription()
{
PubsubName = daprEventBusOptions.PubSubName,

40
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprEventsController.cs

@ -1,8 +1,10 @@
using System.Text.Json;
using System.Collections.Concurrent;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Json;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
@ -14,20 +16,36 @@ namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
public class AbpAspNetCoreMvcDaprEventsController : AbpController
{
[HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
public virtual async Task<IActionResult> EventsAsync()
public virtual async Task<IActionResult> EventAsync()
{
await HttpContext.ValidateDaprAppApiTokenAsync();
var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body);
var request = JsonSerializer.Deserialize<AbpAspNetCoreMvcDaprSubscriptionRequest>(bodyJsonDocument.RootElement.GetRawText(),
HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions);
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
var daprSerializer = HttpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var request = (await JsonDocument.ParseAsync(HttpContext.Request.Body)).Deserialize<AbpDaprSubscriptionRequest<object>>(CreateJsonSerializerOptions(daprSerializer));
if (request != null && request.Data is JsonElement jsonElement)
{
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
var eventData = daprSerializer.Deserialize(jsonElement.GetRawText(), distributedEventBus.GetEventType(request.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
return Ok();
}
Logger.LogError("Invalid Dapr event request.");
return BadRequest();
}
var eventData = daprSerializer.Deserialize(bodyJsonDocument.RootElement.GetProperty("data").GetRawText(), distributedEventBus.GetEventType(request.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
private static readonly ConcurrentDictionary<string, JsonSerializerOptions> JsonSerializerOptionsCache = new ConcurrentDictionary<string, JsonSerializerOptions>();
return Ok();
protected virtual JsonSerializerOptions CreateJsonSerializerOptions(IDaprSerializer daprSerializer)
{
return JsonSerializerOptionsCache.GetOrAdd(nameof(AbpAspNetCoreMvcDaprEventsController), _ =>
{
var settings = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNamingPolicy = new AbpDaprSubscriptionRequestJsonNamingPolicy()
};
settings.Converters.Add(new AbpDaprSubscriptionRequestConverterFactory(daprSerializer));
return settings;
});
}
}

44
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestConverter.cs

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Json;
public class AbpDaprSubscriptionRequestConverter<T> : JsonConverter<AbpDaprSubscriptionRequest<T>>
where T : class
{
private JsonSerializerOptions _readJsonSerializerOptions;
private readonly IDaprSerializer _daprSerializer;
public AbpDaprSubscriptionRequestConverter(IDaprSerializer daprSerializer)
{
_daprSerializer = daprSerializer;
}
public override AbpDaprSubscriptionRequest<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
_readJsonSerializerOptions ??= CreateJsonSerializerOptions(options);
var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
var obj = JsonSerializer.Deserialize<AbpDaprSubscriptionRequest<T>>(rootElement.GetRawText(), _readJsonSerializerOptions);
obj.Data = _daprSerializer.Deserialize(rootElement.GetProperty("data").GetRawText(), typeof(T)).As<T>();
return obj;
}
public override void Write(Utf8JsonWriter writer, AbpDaprSubscriptionRequest<T> value, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
private JsonSerializerOptions CreateJsonSerializerOptions(JsonSerializerOptions options)
{
var newOptions = new JsonSerializerOptions(options);
newOptions.Converters.RemoveAll(x => x == this || x.GetType() == typeof(AbpDaprSubscriptionRequestConverterFactory));
newOptions.PropertyNamingPolicy = new AbpDaprSubscriptionRequestJsonNamingPolicy();
return newOptions;
}
}

33
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestConverterFactory.cs

@ -0,0 +1,33 @@
using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Json;
public class AbpDaprSubscriptionRequestConverterFactory : JsonConverterFactory
{
private readonly IDaprSerializer _daprSerializer;
public AbpDaprSubscriptionRequestConverterFactory(IDaprSerializer daprSerializer)
{
_daprSerializer = daprSerializer;
}
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.GetGenericTypeDefinition() == typeof(AbpDaprSubscriptionRequest<>);
}
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return (JsonConverter)Activator.CreateInstance(
typeof(AbpDaprSubscriptionRequestConverter<>).MakeGenericType(typeToConvert.GetGenericArguments()[0]),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
new object[] { _daprSerializer },
culture: null)!;
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Json/AbpDaprSubscriptionRequestJsonNamingPolicy.cs

@ -0,0 +1,11 @@
using System.Text.Json;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Json;
public class AbpDaprSubscriptionRequestJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToLower();
}
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpAspNetCoreMvcDaprSubscriptionRequest.cs → framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Models/AbpDaprSubscriptionRequest.cs

@ -1,8 +1,11 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionRequest
public class AbpDaprSubscriptionRequest<T>
where T : class
{
public string PubSubName { get; set; }
public string Topic { get; set; }
public T Data { get; set; }
}
Loading…
Cancel
Save