From 8db50d768be59986e81b8f88eff445d0f9b5facf Mon Sep 17 00:00:00 2001 From: xyfy Date: Fri, 4 Jun 2021 11:23:08 +0800 Subject: [PATCH 01/10] Update AutoEntityDistributedEventSelectorListExtensions.cs Add an `remove` extentions adjust the `Add` --- ...ntityDistributedEventSelectorListExtensions.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs index d2a0622f54..12bc8a4ec8 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs @@ -50,6 +50,19 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed ); } + public static bool Remove( + [NotNull] this IAutoEntityDistributedEventSelectorList selectors) where TEntity : IEntity + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Entity:" + typeof(TEntity).FullName; + if (selectors.Any(s => s.Name == selectorName)) + { + return selectors.RemoveAll(s => s.Name == selectorName).Count > 0; + } + return true; + } + /// /// Adds all entity types. /// @@ -114,4 +127,4 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed return selectors.Any(s => s.Predicate(entityType)); } } -} \ No newline at end of file +} From 15743d178516c79dec4be018f4d1d7515b53d923 Mon Sep 17 00:00:00 2001 From: Jadyn Date: Thu, 24 Jun 2021 22:40:11 +0800 Subject: [PATCH 02/10] Add FileName for RemoteStreamContent --- .../AbpRemoteStreamContentModelBinder.cs | 3 +- .../RemoteStreamContentOutputFormatter.cs | 6 +++ .../Volo/Abp/Content/IRemoteStreamContent.cs | 2 + .../Volo/Abp/Content/RemoteStreamContent.cs | 7 ++++ .../DynamicHttpProxyInterceptor.cs | 3 +- .../DynamicProxying/RequestPayloadBuilder.cs | 5 ++- .../RemoteStreamContentTestController.cs | 4 +- ...RemoteStreamContentTestController_Tests.cs | 5 ++- .../PersonAppServiceClientProxy_Tests.cs | 41 ++++++++++--------- .../TestApp/Application/PeopleAppService.cs | 12 +++--- 10 files changed, 54 insertions(+), 34 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs index da58641d7e..30161b77b1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs @@ -110,7 +110,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { postedFiles.Add(new RemoteStreamContent(file.OpenReadStream()) { - ContentType = file.ContentType + ContentType = file.ContentType, + FileName = file.FileName }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs index 188306227a..00ae71fbd2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs @@ -26,6 +26,12 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { context.HttpContext.Response.ContentType = remoteStream.ContentType; + if (!remoteStream.FileName.IsNullOrWhiteSpace()) + { + //https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#syntax + context.HttpContext.Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{remoteStream.FileName}\""); + } + using (var stream = remoteStream.GetStream()) { if (stream.CanSeek) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Content/IRemoteStreamContent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Content/IRemoteStreamContent.cs index bca259d422..0145305f09 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Content/IRemoteStreamContent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Content/IRemoteStreamContent.cs @@ -8,6 +8,8 @@ namespace Volo.Abp.Content long? ContentLength { get; } + string FileName { get; } + Stream GetStream(); } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs index f217101cea..613697dc5b 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs @@ -11,10 +11,17 @@ namespace Volo.Abp.Content _stream = stream; } + public RemoteStreamContent(Stream stream, string fileName): this(stream) + { + FileName = fileName; + } + public virtual string ContentType { get; set; } public virtual long? ContentLength => _stream.Length; + public virtual string FileName { get; set; } + public virtual Stream GetStream() { return _stream; diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 7914068bca..f9712e699d 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -114,7 +114,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying * it before we finish doing our work with the stream */ return (T)(object)new RemoteStreamContent(await responseContent.ReadAsStreamAsync()) { - ContentType = responseContent.Headers.ContentType?.ToString() + ContentType = responseContent.Headers.ContentType?.ToString(), + FileName = responseContent.Headers?.ContentDisposition?.FileName?.RemovePreFix("\"")?.RemovePostFix("\"") }; } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs index 1096fa971e..b121852c2c 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs @@ -90,8 +90,9 @@ namespace Volo.Abp.Http.Client.DynamicProxying if (!remoteStreamContent.ContentType.IsNullOrWhiteSpace()) { streamContent.Headers.ContentType = new MediaTypeHeaderValue(remoteStreamContent.ContentType); + } - formData.Add(streamContent, parameter.Name, parameter.Name); + formData.Add(streamContent, parameter.Name, remoteStreamContent.FileName ?? parameter.Name); } else if (value is IEnumerable remoteStreamContents) { @@ -107,7 +108,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying { streamContent.Headers.ContentType = new MediaTypeHeaderValue(content.ContentType); } - formData.Add(streamContent, parameter.Name, parameter.Name); + formData.Add(streamContent, parameter.Name, content.FileName ?? parameter.Name); } } else diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs index e74c9d52e8..7743e575ed 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters var memoryStream = new MemoryStream(); await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("DownloadAsync")); - return new RemoteStreamContent(memoryStream) + return new RemoteStreamContent(memoryStream, "download.rtf") { ContentType = "application/rtf" }; @@ -29,7 +29,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { using (var reader = new StreamReader(file.GetStream())) { - return await reader.ReadToEndAsync() + ":" + file.ContentType; + return await reader.ReadToEndAsync() + ":" + file.ContentType + ":" + file.FileName; } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs index 12745da732..4bc03593ff 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs @@ -15,6 +15,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { var result = await GetResponseAsync("/api/remote-stream-content-test/download"); result.Content.Headers.ContentType?.ToString().ShouldBe("application/rtf"); + result.Content.Headers.ContentDisposition?.FileName.ShouldBe("\"download.rtf\""); (await result.Content.ReadAsStringAsync()).ShouldBe("DownloadAsync"); } @@ -30,11 +31,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters var streamContent = new StreamContent(memoryStream); streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/rtf"); - requestMessage.Content = new MultipartFormDataContent {{streamContent, "file", "file"}}; + requestMessage.Content = new MultipartFormDataContent {{streamContent, "file", "upload.rtf"}}; var response = await Client.SendAsync(requestMessage); - (await response.Content.ReadAsStringAsync()).ShouldBe("UploadAsync:application/rtf"); + (await response.Content.ReadAsStringAsync()).ShouldBe("UploadAsync:application/rtf:upload.rtf"); } } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index 026373d568..1b36bb50c4 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -49,7 +49,7 @@ namespace Volo.Abp.Http.DynamicProxying { var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()); people.TotalCount.ShouldBeGreaterThan(0); - people.Items.Count.ShouldBe((int) people.TotalCount); + people.Items.Count.ShouldBe((int)people.TotalCount); } [Fact] @@ -62,7 +62,7 @@ namespace Volo.Abp.Http.DynamicProxying { id1, id2 - }, new[] {"name1", "name2"}); + }, new[] { "name1", "name2" }); @params.ShouldContain(id1.ToString("N")); @params.ShouldContain(id2.ToString("N")); @@ -87,10 +87,10 @@ namespace Volo.Abp.Http.DynamicProxying var uniquePersonName = Guid.NewGuid().ToString(); var person = await _peopleAppService.CreateAsync(new PersonDto - { - Name = uniquePersonName, - Age = 42 - } + { + Name = uniquePersonName, + Age = 42 + } ); person.ShouldNotBeNull(); @@ -108,9 +108,9 @@ namespace Volo.Abp.Http.DynamicProxying await Assert.ThrowsAsync(async () => { var person = await _peopleAppService.CreateAsync(new PersonDto - { - Age = 42 - } + { + Age = 42 + } ); }); } @@ -180,6 +180,7 @@ namespace Volo.Abp.Http.DynamicProxying { var result = await _peopleAppService.DownloadAsync(); + result.FileName.ShouldBe("download.rtf"); result.ContentType.ShouldBe("application/rtf"); using (var reader = new StreamReader(result.GetStream())) { @@ -194,11 +195,11 @@ namespace Volo.Abp.Http.DynamicProxying var memoryStream = new MemoryStream(); await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("UploadAsync")); memoryStream.Position = 0; - var result = await _peopleAppService.UploadAsync(new RemoteStreamContent(memoryStream) + var result = await _peopleAppService.UploadAsync(new RemoteStreamContent(memoryStream, "upload.rtf") { ContentType = "application/rtf" }); - result.ShouldBe("UploadAsync:application/rtf"); + result.ShouldBe("UploadAsync:application/rtf:upload.rtf"); } [Fact] @@ -214,17 +215,17 @@ namespace Volo.Abp.Http.DynamicProxying var result = await _peopleAppService.UploadMultipleAsync(new List() { - new RemoteStreamContent(memoryStream) + new RemoteStreamContent(memoryStream,"File1.rtf") { ContentType = "application/rtf" }, - new RemoteStreamContent(memoryStream2) + new RemoteStreamContent(memoryStream2,"File2.rtf") { ContentType = "application/rtf2" } }); - result.ShouldBe("File1:application/rtfFile2:application/rtf2"); + result.ShouldBe("File1:application/rtf:File1.rtfFile2:application/rtf2:File2.rtf"); } [Fact] @@ -236,12 +237,12 @@ namespace Volo.Abp.Http.DynamicProxying var result = await _peopleAppService.CreateFileAsync(new CreateFileInput() { Name = "123.rtf", - Content = new RemoteStreamContent(memoryStream) + Content = new RemoteStreamContent(memoryStream, "create.rtf") { ContentType = "application/rtf" } }); - result.ShouldBe("123.rtf:CreateFileAsync:application/rtf"); + result.ShouldBe("123.rtf:CreateFileAsync:application/rtf:create.rtf"); } [Fact] @@ -264,12 +265,12 @@ namespace Volo.Abp.Http.DynamicProxying Name = "123.rtf", Contents = new List() { - new RemoteStreamContent(memoryStream) + new RemoteStreamContent(memoryStream,"1-1.rtf") { ContentType = "application/rtf" }, - new RemoteStreamContent(memoryStream2) + new RemoteStreamContent(memoryStream2,"1-2.rtf") { ContentType = "application/rtf2" } @@ -277,13 +278,13 @@ namespace Volo.Abp.Http.DynamicProxying Inner = new CreateFileInput() { Name = "789.rtf", - Content = new RemoteStreamContent(memoryStream3) + Content = new RemoteStreamContent(memoryStream3, "i-789.rtf") { ContentType = "application/rtf3" } } }); - result.ShouldBe("123.rtf:File1:application/rtf123.rtf:File2:application/rtf2789.rtf:File3:application/rtf3"); + result.ShouldBe("123.rtf:File1:application/rtf:1-1.rtf123.rtf:File2:application/rtf2:1-2.rtf789.rtf:File3:application/rtf3:i-789.rtf"); } } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index edd6a3b93f..f3dfef2891 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -73,7 +73,7 @@ namespace Volo.Abp.TestApp.Application var memoryStream = new MemoryStream(); await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("DownloadAsync")); - return new RemoteStreamContent(memoryStream) + return new RemoteStreamContent(memoryStream, "download.rtf") { ContentType = "application/rtf" }; @@ -83,7 +83,7 @@ namespace Volo.Abp.TestApp.Application { using (var reader = new StreamReader(streamContent.GetStream())) { - return await reader.ReadToEndAsync() + ":" + streamContent.ContentType; + return await reader.ReadToEndAsync() + ":" + streamContent.ContentType + ":" + streamContent.FileName; } } @@ -94,7 +94,7 @@ namespace Volo.Abp.TestApp.Application { using (var reader = new StreamReader(content.GetStream())) { - str += await reader.ReadToEndAsync() + ":" + content.ContentType; + str += await reader.ReadToEndAsync() + ":" + content.ContentType + ":" + content.FileName; } } @@ -105,7 +105,7 @@ namespace Volo.Abp.TestApp.Application { using (var reader = new StreamReader(input.Content.GetStream())) { - return input.Name + ":" + await reader.ReadToEndAsync() + ":" + input.Content.ContentType; + return input.Name + ":" + await reader.ReadToEndAsync() + ":" + input.Content.ContentType + ":" + input.Content.FileName; } } @@ -116,13 +116,13 @@ namespace Volo.Abp.TestApp.Application { using (var reader = new StreamReader(content.GetStream())) { - str += input.Name + ":" + await reader.ReadToEndAsync() + ":" + content.ContentType; + str += input.Name + ":" + await reader.ReadToEndAsync() + ":" + content.ContentType + ":" + content.FileName; } } using (var reader = new StreamReader(input.Inner.Content.GetStream())) { - str += input.Inner.Name + ":" + await reader.ReadToEndAsync() + ":" + input.Inner.Content.ContentType; + str += input.Inner.Name + ":" + await reader.ReadToEndAsync() + ":" + input.Inner.Content.ContentType + ":" + input.Inner.Content.FileName; } return str; From 534fe8f52245f36bd9a4aeddd6dac19403fef0d2 Mon Sep 17 00:00:00 2001 From: Jadyn Date: Fri, 25 Jun 2021 09:48:27 +0800 Subject: [PATCH 03/10] Use FileNameStar for RemoteStreamContent FileName --- .../RemoteStreamContentOutputFormatter.cs | 2 +- .../DynamicProxying/DynamicHttpProxyInterceptor.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs index 00ae71fbd2..bf9cdac23f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs @@ -29,7 +29,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters if (!remoteStream.FileName.IsNullOrWhiteSpace()) { //https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#syntax - context.HttpContext.Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{remoteStream.FileName}\""); + context.HttpContext.Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{remoteStream.FileName}\"; filename*=UTF-8''{Uri.EscapeDataString(remoteStream.FileName)}"); } using (var stream = remoteStream.GetStream()) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index f9712e699d..57d1d6c68b 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -57,7 +57,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying ICancellationTokenProvider cancellationTokenProvider, ICorrelationIdProvider correlationIdProvider, IOptions correlationIdOptions, - ICurrentTenant currentTenant, + ICurrentTenant currentTenant, IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider) { CancellationTokenProvider = cancellationTokenProvider; @@ -115,7 +115,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying return (T)(object)new RemoteStreamContent(await responseContent.ReadAsStreamAsync()) { ContentType = responseContent.Headers.ContentType?.ToString(), - FileName = responseContent.Headers?.ContentDisposition?.FileName?.RemovePreFix("\"")?.RemovePostFix("\"") + FileName = responseContent.Headers?.ContentDisposition?.FileNameStar ?? + responseContent.Headers?.ContentDisposition?.FileName?.RemovePreFix("\"")?.RemovePostFix("\"") }; } @@ -278,7 +279,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying throw new AbpRemoteCallException(errorResponse.Error) { - HttpStatusCode = (int) response.StatusCode + HttpStatusCode = (int)response.StatusCode }; } @@ -290,7 +291,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying } ) { - HttpStatusCode = (int) response.StatusCode + HttpStatusCode = (int)response.StatusCode }; } From e9a4a9c70f77d2967f69d27bcb9cdbb44e0665ab Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 25 Jun 2021 14:08:21 +0800 Subject: [PATCH 04/10] Allowing the developer to handle ajax error. --- npm/packs/jquery/src/abp.jquery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/packs/jquery/src/abp.jquery.js b/npm/packs/jquery/src/abp.jquery.js index 6f8f5b20ce..81ebf1e2e1 100644 --- a/npm/packs/jquery/src/abp.jquery.js +++ b/npm/packs/jquery/src/abp.jquery.js @@ -106,7 +106,7 @@ var abp = abp || {}; $dfd.resolve(data); userOptions.success && userOptions.success(data); }).fail(function (jqXHR) { - if(jqXHR.status === 0 || jqXHR.statusText === 'abort') { + if(jqXHR.statusText === 'abort') { //ajax request is abort, ignore error handle. return; } From c69447d73238d86443b86ed9e5f2d64a1fad1457 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Jun 2021 08:35:14 +0800 Subject: [PATCH 05/10] Use ContentDispositionHeaderValue. --- .../ContentFormatters/RemoteStreamContentOutputFormatter.cs | 5 +++-- .../RemoteStreamContentTestController_Tests.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs index bf9cdac23f..35e0922f36 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs @@ -28,8 +28,9 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters if (!remoteStream.FileName.IsNullOrWhiteSpace()) { - //https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#syntax - context.HttpContext.Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{remoteStream.FileName}\"; filename*=UTF-8''{Uri.EscapeDataString(remoteStream.FileName)}"); + var contentDisposition = new ContentDispositionHeaderValue("attachment"); + contentDisposition.SetHttpFileName(remoteStream.FileName); + context.HttpContext.Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString(); } using (var stream = remoteStream.GetStream()) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs index 4bc03593ff..8f42a5cb37 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { var result = await GetResponseAsync("/api/remote-stream-content-test/download"); result.Content.Headers.ContentType?.ToString().ShouldBe("application/rtf"); - result.Content.Headers.ContentDisposition?.FileName.ShouldBe("\"download.rtf\""); + result.Content.Headers.ContentDisposition?.FileName.ShouldBe("download.rtf"); (await result.Content.ReadAsStringAsync()).ShouldBe("DownloadAsync"); } From f3c77764a5263065ccdd0dfebe9770ae47499bc7 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Jun 2021 09:14:22 +0800 Subject: [PATCH 06/10] Add RemoveQuotes method. --- .../DynamicHttpProxyInterceptor.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 57d1d6c68b..d10be82096 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Volo.Abp.Content; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -115,8 +116,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying return (T)(object)new RemoteStreamContent(await responseContent.ReadAsStreamAsync()) { ContentType = responseContent.Headers.ContentType?.ToString(), - FileName = responseContent.Headers?.ContentDisposition?.FileNameStar ?? - responseContent.Headers?.ContentDisposition?.FileName?.RemovePreFix("\"")?.RemovePostFix("\"") + FileName = responseContent.Headers?.ContentDisposition?.FileNameStar ?? + RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString() }; } @@ -295,6 +296,16 @@ namespace Volo.Abp.Http.Client.DynamicProxying }; } + protected virtual StringSegment RemoveQuotes(StringSegment input) + { + if (!StringSegment.IsNullOrEmpty(input) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"') + { + input = input.Subsegment(1, input.Length - 2); + } + + return input; + } + protected virtual CancellationToken GetCancellationToken() { return CancellationTokenProvider.Token; From 49058c9eec6bd29bb314dae4f37de6f108c5979d Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Jun 2021 09:21:26 +0800 Subject: [PATCH 07/10] Use application/octet-stream as default value of ContentType. --- .../src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs index 613697dc5b..602c1aec21 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs @@ -14,6 +14,7 @@ namespace Volo.Abp.Content public RemoteStreamContent(Stream stream, string fileName): this(stream) { FileName = fileName; + ContentType = "application/octet-stream"; } public virtual string ContentType { get; set; } From 8863a4b7fbf0ac00a16c1dced8b0037c82b95d73 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Jun 2021 09:32:23 +0800 Subject: [PATCH 08/10] Format code. --- .../Volo/Abp/Content/RemoteStreamContent.cs | 5 +++-- .../DynamicProxying/DynamicHttpProxyInterceptor.cs | 4 ++-- .../PersonAppServiceClientProxy_Tests.cs | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs index 602c1aec21..f24b4d83bd 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Content/RemoteStreamContent.cs @@ -11,7 +11,8 @@ namespace Volo.Abp.Content _stream = stream; } - public RemoteStreamContent(Stream stream, string fileName): this(stream) + public RemoteStreamContent(Stream stream, string fileName) + : this(stream) { FileName = fileName; ContentType = "application/octet-stream"; @@ -19,7 +20,7 @@ namespace Volo.Abp.Content public virtual string ContentType { get; set; } - public virtual long? ContentLength => _stream.Length; + public virtual long? ContentLength => GetStream().Length; public virtual string FileName { get; set; } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index d10be82096..b94a5a339d 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -280,7 +280,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying throw new AbpRemoteCallException(errorResponse.Error) { - HttpStatusCode = (int)response.StatusCode + HttpStatusCode = (int) response.StatusCode }; } @@ -292,7 +292,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying } ) { - HttpStatusCode = (int)response.StatusCode + HttpStatusCode = (int) response.StatusCode }; } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index 1b36bb50c4..d92e1ad5d1 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -49,7 +49,7 @@ namespace Volo.Abp.Http.DynamicProxying { var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()); people.TotalCount.ShouldBeGreaterThan(0); - people.Items.Count.ShouldBe((int)people.TotalCount); + people.Items.Count.ShouldBe((int) people.TotalCount); } [Fact] @@ -215,12 +215,12 @@ namespace Volo.Abp.Http.DynamicProxying var result = await _peopleAppService.UploadMultipleAsync(new List() { - new RemoteStreamContent(memoryStream,"File1.rtf") + new RemoteStreamContent(memoryStream, "File1.rtf") { ContentType = "application/rtf" }, - new RemoteStreamContent(memoryStream2,"File2.rtf") + new RemoteStreamContent(memoryStream2, "File2.rtf") { ContentType = "application/rtf2" } @@ -265,12 +265,12 @@ namespace Volo.Abp.Http.DynamicProxying Name = "123.rtf", Contents = new List() { - new RemoteStreamContent(memoryStream,"1-1.rtf") + new RemoteStreamContent(memoryStream, "1-1.rtf") { ContentType = "application/rtf" }, - new RemoteStreamContent(memoryStream2,"1-2.rtf") + new RemoteStreamContent(memoryStream2, "1-2.rtf") { ContentType = "application/rtf2" } From a2497d5072e4eb9cd0c271b2c4f69dabfb1ce715 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Jun 2021 10:39:04 +0800 Subject: [PATCH 09/10] Minor change. --- ...yDistributedEventSelectorListExtensions.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs index 12bc8a4ec8..a9247bc28b 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/AutoEntityDistributedEventSelectorListExtensions.cs @@ -26,7 +26,7 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed ) ); } - + /// /// Adds a specific entity type and the types derived from that entity type. /// @@ -49,18 +49,18 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed ) ); } - - public static bool Remove( - [NotNull] this IAutoEntityDistributedEventSelectorList selectors) where TEntity : IEntity + + /// + /// Remove a specific entity type and the types derived from that entity type. + /// + /// Type of the entity + public static void Remove([NotNull] this IAutoEntityDistributedEventSelectorList selectors) + where TEntity : IEntity { Check.NotNull(selectors, nameof(selectors)); var selectorName = "Entity:" + typeof(TEntity).FullName; - if (selectors.Any(s => s.Name == selectorName)) - { - return selectors.RemoveAll(s => s.Name == selectorName).Count > 0; - } - return true; + selectors.RemoveAll(s => s.Name == selectorName); } /// @@ -69,7 +69,7 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed public static void AddAll([NotNull] this IAutoEntityDistributedEventSelectorList selectors) { Check.NotNull(selectors, nameof(selectors)); - + if (selectors.Any(s => s.Name == AllEntitiesSelectorName)) { return; @@ -85,11 +85,11 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed public static void Add( [NotNull] this IAutoEntityDistributedEventSelectorList selectors, - string selectorName, + string selectorName, Func predicate) { Check.NotNull(selectors, nameof(selectors)); - + if (selectors.Any(s => s.Name == selectorName)) { throw new AbpException($"There is already a selector added before with the same name: {selectorName}"); @@ -102,7 +102,7 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed ) ); } - + public static void Add( [NotNull] this IAutoEntityDistributedEventSelectorList selectors, Func predicate) @@ -116,11 +116,11 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed { Check.NotNull(selectors, nameof(selectors)); Check.NotNull(name, nameof(name)); - + return selectors.RemoveAll(s => s.Name == name).Count > 0; } - + public static bool IsMatch([NotNull] this IAutoEntityDistributedEventSelectorList selectors, Type entityType) { Check.NotNull(selectors, nameof(selectors)); From 0abe394d53f4fb15029e485fc16e1fce73991850 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 28 Jun 2021 11:15:51 +0800 Subject: [PATCH 10/10] Added exception handling section to event bus document --- docs/en/Distributed-Event-Bus.md | 53 +++++++++++++++++++++++++- docs/en/Local-Event-Bus.md | 48 ++++++++++++++++++++++- docs/zh-Hans/Distributed-Event-Bus.md | 55 ++++++++++++++++++++++++++- docs/zh-Hans/Local-Event-Bus.md | 48 ++++++++++++++++++++++- 4 files changed, 199 insertions(+), 5 deletions(-) diff --git a/docs/en/Distributed-Event-Bus.md b/docs/en/Distributed-Event-Bus.md index 02216c7ac8..05ef16a59e 100644 --- a/docs/en/Distributed-Event-Bus.md +++ b/docs/en/Distributed-Event-Bus.md @@ -4,7 +4,7 @@ Distributed Event bus system allows to **publish** and **subscribe** to events t ## Providers -Distributed event bus system provides an **abstraction** that can be implemented by any vendor/provider. There are two providers implemented out of the box: +Distributed event bus system provides an **abstraction** that can be implemented by any vendor/provider. There are four providers implemented out of the box: * `LocalDistributedEventBus` is the default implementation that implements the distributed event bus to work as in-process. Yes! The **default implementation works just like the [local event bus](Local-Event-Bus.md)**, if you don't configure a real distributed provider. * `RabbitMqDistributedEventBus` implements the distributed event bus with the [RabbitMQ](https://www.rabbitmq.com/). See the [RabbitMQ integration document](Distributed-Event-Bus-RabbitMQ-Integration.md) to learn how to configure it. @@ -302,3 +302,54 @@ namespace AbpDemo ```` This example uses the `AutoMap` attribute of the AutoMapper to configure the mapping. You could create a profile class instead. Please refer to the AutoMapper document for more options. + +## Exception Handling + +ABP provides exception handling and retries when an exception occurs, it will move to the dead letter queue after the retry fails. + +Enable exception handling: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.EnabledErrorHandle = true; + options.UseRetryStrategy(); + }); +} +``` + +* `EnabledErrorHandle` is used to enable exception handing. +* `UseRetryStrategy` is used to enable retry. + +When an exception occurs, it will retry every three seconds up to the maximum number of retries(default is 3) and move to dead letter queue, you can change the number of retries, retry interval and dead letter queue name: + +```csharp +PreConfigure(options => +{ + options.DeadLetterName = "dead_queue"; + options.UseRetryStrategy(retryStrategyOptions => + { + retryStrategyOptions.IntervalMillisecond = 0; + retryStrategyOptions.MaxRetryAttempts = 1; + }); +}); +``` + +### Error Handle Selector + +By default all event types will be exception handling, you can use `ErrorHandleSelector` of `AbpEventBusOptions` to change it: + +```csharp +PreConfigure(options => +{ + options.ErrorHandleSelector = type => type == typeof(MyExceptionHandleEventData); +}); +``` + +`options.ErrorHandleSelector` actually a list of type predicate. You can write a lambda expression to define your filter. + +### Customize Exception Handling + +ABP defines the `IEventErrorHandler` interface and implemented by the provider, you can replace it via [dependency injection](Dependency-Injection.md) \ No newline at end of file diff --git a/docs/en/Local-Event-Bus.md b/docs/en/Local-Event-Bus.md index d937a1caff..82c839a996 100644 --- a/docs/en/Local-Event-Bus.md +++ b/docs/en/Local-Event-Bus.md @@ -158,7 +158,53 @@ If you perform **database operations** and use the [repositories](Repositories.m > The handler class must be registered to the dependency injection (DI). The sample above uses the `ITransientDependency` to accomplish it. See the [DI document](Dependency-Injection.md) for more options. -## Transaction & Exception Behavior +## Exception Handling + +ABP provides exception handling and retries when an exception occurs. + +Enable exception handling: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.UseRetryStrategy(); + }); +} +``` + +When an exception occurs, it will retry every three seconds up to the maximum number of retries(default is 3) and throw the original exception, you can change the number of retries and the retry interval: + +```csharp +PreConfigure(options => +{ + options.UseRetryStrategy(retryStrategyOptions => + { + retryStrategyOptions.IntervalMillisecond = 0; + retryStrategyOptions.MaxRetryAttempts = 1; + }); +}); +``` + +### Error Handle Selector + +By default all event types will be exception handling, you can use `ErrorHandleSelector` of `AbpEventBusOptions` to change it: + +```csharp +PreConfigure(options => +{ + options.ErrorHandleSelector = type => type == typeof(MyExceptionHandleEventData); +}); +``` + +`options.ErrorHandleSelector` actually a list of type predicate. You can write a lambda expression to define your filter. + +### Customize Exception Handling + +ABP defines the `IEventErrorHandler` interface and implemented by `LocalEventErrorHandler`, you can replace it via [dependency injection](Dependency-Injection.md) + +### Transaction & Exception Behavior When an event published, subscribed event handlers are immediately executed. So; diff --git a/docs/zh-Hans/Distributed-Event-Bus.md b/docs/zh-Hans/Distributed-Event-Bus.md index 4b4203911b..bd086c3d83 100644 --- a/docs/zh-Hans/Distributed-Event-Bus.md +++ b/docs/zh-Hans/Distributed-Event-Bus.md @@ -4,7 +4,7 @@ ## 提供程序 -分布式事件总线系统提供了一个可以被任何提供程序实现的**抽象**. 有两种开箱即用的提供程序: +分布式事件总线系统提供了一个可以被任何提供程序实现的**抽象**. 有四种开箱即用的提供程序: * `LocalDistributedEventBus` 是默认实现,实现作为进程内工作的分布式事件总线. 是的!如果没有配置真正的分布式提供程序,**默认实现的工作方式与[本地事件总线](Local-Event-Bus.md)一样**. * `RabbitMqDistributedEventBus` 通过[RabbitMQ](https://www.rabbitmq.com/)实现分布式事件总线. 请参阅[RabbitMQ集成文档](Distributed-Event-Bus-RabbitMQ-Integration.md)了解如何配置它. @@ -299,4 +299,55 @@ namespace AbpDemo } ```` -此示例使用AutoMapper的 `AutoMap` 属性配置的映射. 你可以创建一个配置文件类代替. 请参阅AutoMapper文档了解更多选项. \ No newline at end of file +此示例使用AutoMapper的 `AutoMap` 属性配置的映射. 你可以创建一个配置文件类代替. 请参阅AutoMapper文档了解更多选项. + +## 异常处理 + +ABP提供了异常处理, 它会进行重试并且重试失败后移动到死信队列. + +启用异常处理: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.EnabledErrorHandle = true; + options.UseRetryStrategy(); + }); +} +``` + +* `EnabledErrorHandle` 用于启用异常处理. +* `UseRetryStrategy` 用于启用重试. + +当一个异常抛出,它会每3秒重试一次直到最大重试次数(默认是3)并且移动到错误队列, 你可以更改重试次数,重试间隔和死信队列名称: + +```csharp +PreConfigure(options => +{ + options.DeadLetterName = "dead_queue"; + options.UseRetryStrategy(retryStrategyOptions => + { + retryStrategyOptions.IntervalMillisecond = 0; + retryStrategyOptions.MaxRetryAttempts = 1; + }); +}); +``` + +### 错误处理选择器 + +默认所有的事件类型都会被处理, 你可以使用 `AbpEventBusOptions` 的 `ErrorHandleSelector` 来更改它: + +```csharp +PreConfigure(options => +{ + options.ErrorHandleSelector = type => type == typeof(MyExceptionHandleEventData); +}); +``` + +`options.ErrorHandleSelector` 实际上是一个类型类型谓词列表. 你可以编写lambda来定义你的过滤. + +### 自定义异常处理 + +ABP定义了 `IEventErrorHandler` 接口并且由提供程序实现, 你可以通过[依赖注入](Dependency-Injection.md)替换它. \ No newline at end of file diff --git a/docs/zh-Hans/Local-Event-Bus.md b/docs/zh-Hans/Local-Event-Bus.md index 3a955e90d7..3a2944d32b 100644 --- a/docs/zh-Hans/Local-Event-Bus.md +++ b/docs/zh-Hans/Local-Event-Bus.md @@ -158,7 +158,53 @@ namespace AbpDemo 如果您执行**数据库操作**并在事件处理程序中使用[仓储](Repositories.md),那么您可能需要创建一个[工作单元](Unit-Of-Work.md),因为一些存储库方法需要在**活动的工作单元**中工作. 确保处理方法设置为 `virtual`,并为该方法添加一个 `[UnitOfWork]` attribute. 或者手动使用 `IUnitOfWorkManager` 创建一个工作单元范围. -## 事务和异常行为 +## 异常处理 + +ABP提供了异常处理, 当异常发现时它会进行重试. + +启用异常处理: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.UseRetryStrategy(); + }); +} +``` + +当一个异常抛出,它会每3秒重试一次直到最大重试次数(默认是3)并且移动抛出原始异常, 你可以更改重试次数和重试间隔和: + +```csharp +PreConfigure(options => +{ + options.UseRetryStrategy(retryStrategyOptions => + { + retryStrategyOptions.IntervalMillisecond = 0; + retryStrategyOptions.MaxRetryAttempts = 1; + }); +}); +``` + +### 错误处理选择器 + +默认所有的事件类型都会被处理, 你可以使用 `AbpEventBusOptions` 的 `ErrorHandleSelector` 来更改它: + +```csharp +PreConfigure(options => +{ + options.ErrorHandleSelector = type => type == typeof(MyExceptionHandleEventData); +}); +``` + +`options.ErrorHandleSelector` 实际上是一个类型类型谓词列表. 你可以编写lambda来定义你的过滤. + +### 自定义异常处理 + +ABP定义了 `IEventErrorHandler` 接口并且由 `LocalEventErrorHandler` 实现, 你可以通过[依赖注入](Dependency-Injection.md)替换它. + +### 事务和异常行为 当一个事件发布,订阅的事件处理程序将立即执行.所以;