├── .gitattributes
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── AspNetScaffolding3.DemoApi
├── AspNetScaffolding3.DemoApi.csproj
├── Controllers
│ ├── CustomerController.cs
│ ├── PersonController.cs
│ └── ProxyController.cs
├── DOCS.md
├── Entities
│ └── Customer.cs
├── Models
│ ├── Address.cs
│ ├── CustomerRequest.cs
│ └── Person.cs
├── Profiles
│ └── CustomerProfile.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── appsettings.Development.json
├── appsettings.json
└── wwwroot
│ └── done.html
├── AspNetScaffolding3.DemoWorker
├── AspNetScaffolding3.DemoWorker.csproj
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── Workers
│ └── WorkerRunner.cs
├── appsettings.Development.json
└── appsettings.json
├── AspNetScaffolding3.sln
├── AspNetScaffolding3
├── Api.cs
├── AspNetScaffolding3.csproj
├── Controllers
│ ├── BaseController.cs
│ └── HomeController.cs
├── Extensions
│ ├── AccountId
│ │ ├── AccountId.cs
│ │ ├── AccountIdMiddleware.cs
│ │ └── AccountIdService.cs
│ ├── Cache
│ │ ├── CacheService.cs
│ │ ├── CacheSettings.cs
│ │ └── LockerFactory.cs
│ ├── Configuration
│ │ └── ConfigurationExtension.cs
│ ├── Cors
│ │ ├── CorsMiddleware.cs
│ │ └── CorsService.cs
│ ├── CultureInfo
│ │ └── CultureInfoMiddleware.cs
│ ├── Docs
│ │ ├── DocsMiddleware.cs
│ │ ├── DocsService.cs
│ │ ├── DocsSettings.cs
│ │ └── QueryAndPathCaseOperationFilter.cs
│ ├── ExceptionHandler
│ │ └── ExceptionHandlerMiddleware.cs
│ ├── GracefullShutdown
│ │ ├── GracefullShutdownExtensions.cs
│ │ ├── GracefullShutdownMiddleware.cs
│ │ ├── GracefullShutdownState.cs
│ │ ├── IRequestsCountProvider.cs
│ │ └── ShutdownSettings.cs
│ ├── Healthcheck
│ │ ├── HealthcheckMiddleware.cs
│ │ ├── HealthcheckService.cs
│ │ └── HealthcheckSettings.cs
│ ├── JsonFieldSelector
│ │ └── JsonFieldSelectorMiddleware.cs
│ ├── JsonSerializer
│ │ ├── CaseUtility.cs
│ │ ├── JsonSerializerEnum.cs
│ │ └── JsonSerializerService.cs
│ ├── Logger
│ │ ├── Formatters
│ │ │ ├── SnakeCaseJsonValueFormatter.cs
│ │ │ └── SnakeCaseRenderedCompactJsonFormatter.cs
│ │ ├── ISimpleLogger.cs
│ │ ├── LoggerBuilderExtension.cs
│ │ ├── LoggerService.cs
│ │ ├── LoggerSettings.cs
│ │ ├── SimpleLogger.cs
│ │ └── StaticSimpleLogger.cs
│ ├── Mapper
│ │ ├── MapperExtension.cs
│ │ └── MapperService.cs
│ ├── Mvc
│ │ └── MvcBuilderExtension.cs
│ ├── QueryFormatter
│ │ ├── PathFormatterSettings.cs
│ │ └── QueryFormatterSettings.cs
│ ├── Queue
│ │ ├── ChannelFactory.cs
│ │ ├── Interfaces
│ │ │ ├── IQueueClient.cs
│ │ │ └── IQueueProcessor.cs
│ │ ├── QueueClient.cs
│ │ ├── QueueHealthcheck.cs
│ │ ├── QueueProcessor.cs
│ │ ├── QueueService.cs
│ │ └── QueueSettings.cs
│ ├── RequestKey
│ │ ├── RequestKey.cs
│ │ ├── RequestKeyMiddleware.cs
│ │ └── RequestKeyService.cs
│ ├── RequestLimit
│ │ ├── CustomRateLimitConfiguration.cs
│ │ ├── IpRateLimitingAdditional.cs
│ │ ├── IpRateLimitingService.cs
│ │ ├── RateLimitingAdditional.cs
│ │ └── UrlResourceRateLimitContributor.cs
│ ├── RoutePrefix
│ │ ├── IgnoreRoutePrefixAttribute.cs
│ │ ├── RoutePrefixAttribute.cs
│ │ └── RoutePrefixExtensions.cs
│ ├── StreamExt
│ │ └── StreamExtension.cs
│ ├── TimeElapsed
│ │ ├── TimeElapsedMiddleware.cs
│ │ └── TimeElapsedService.cs
│ └── Worker
│ │ ├── BaseWorkerRunner.cs
│ │ ├── Interface
│ │ └── IWorkerRunner.cs
│ │ ├── WorkerService.cs
│ │ └── WorkerSettings.cs
├── Models
│ ├── ApiBasicConfiguration.cs
│ ├── ApiSettings.cs
│ ├── DatabaseSettings.cs
│ └── ExceptionContainer.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── Utilities
│ ├── CQRSExtensions.cs
│ ├── EnvironmentUtility.cs
│ ├── ObjectMappingExtension.cs
│ ├── PipelineRExtension.cs
│ └── RestsharpEasyExtension.cs
└── appsettings.json
├── AspNetScaffolding3Tests
├── AspNetScaffolding3Tests.csproj
└── Extensions
│ └── Logger
│ └── LoggerSettingsTest.cs
├── LICENSE
├── README.md
├── README.md.bak
├── Template
└── src
│ ├── AspNetScaffoldingTemplate.Api
│ ├── AspNetScaffoldingTemplate.Api.csproj
│ ├── Docs.md
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ └── appsettings.json
│ ├── AspNetScaffoldingTemplate.Core.Tests
│ ├── AspNetScaffoldingTemplate.Core.Tests.csproj
│ └── UnitTest1.cs
│ ├── AspNetScaffoldingTemplate.Core
│ ├── AspNetScaffoldingTemplate.Core.csproj
│ ├── Models
│ │ └── Person
│ │ │ ├── Composition
│ │ │ ├── Address.cs
│ │ │ └── PersonType.cs
│ │ │ ├── Person.cs
│ │ │ └── Validator
│ │ │ └── CreatePersonValidator.cs
│ └── Services
│ │ ├── Interfaces
│ │ └── IPersonService.cs
│ │ └── PersonService.cs
│ ├── AspNetScaffoldingTemplate.sln
│ ├── AspNetScaffoldingTemplate
│ ├── AspNetScaffoldingAdditionalData.Designer.cs
│ ├── AspNetScaffoldingAdditionalData.cs
│ ├── AspNetScaffoldingAdditionalData.resx
│ ├── AspNetScaffoldingTemplate.csproj
│ ├── ProjectTemplates
│ │ └── AspNetScaffolding3.zip
│ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resx
│ ├── Wizard.cs
│ ├── aspnetscaffolding-wizard.snk
│ ├── icon.png
│ ├── logo.png
│ └── source.extension.vsixmanifest
│ └── TemplateRelease
│ ├── .gitattributes
│ ├── .github
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
│ ├── .gitignore
│ ├── AspNetScaffolding3.vstemplate
│ ├── AspNetScaffolding3.zip
│ ├── AspNetScaffoldingTemplate.Api
│ ├── AspNetScaffoldingTemplate.Api.csproj
│ ├── AspNetScaffoldingTemplate.Api.vstemplate
│ ├── Docs.md
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── __PreviewImage.png
│ ├── __TemplateIcon.png
│ └── appsettings.json
│ ├── AspNetScaffoldingTemplate.Core.Tests
│ ├── AspNetScaffoldingTemplate.Core.Tests.csproj
│ ├── AspNetScaffoldingTemplate.Core.Tests.vstemplate
│ ├── UnitTest1.cs
│ ├── __PreviewImage.png
│ └── __TemplateIcon.png
│ ├── AspNetScaffoldingTemplate.Core
│ ├── AspNetScaffoldingTemplate.Core.csproj
│ ├── AspNetScaffoldingTemplate.Core.vstemplate
│ ├── Models
│ │ └── Person
│ │ │ ├── Composition
│ │ │ ├── Address.cs
│ │ │ └── PersonType.cs
│ │ │ ├── Person.cs
│ │ │ └── Validator
│ │ │ └── CreatePersonValidator.cs
│ ├── Services
│ │ ├── Interfaces
│ │ │ └── IPersonService.cs
│ │ └── PersonService.cs
│ ├── __PreviewImage.png
│ └── __TemplateIcon.png
│ ├── README.md
│ ├── __PreviewImage.png
│ ├── __TemplateIcon.png
│ └── devops
│ ├── Dockerfile
│ ├── azure-pipelines.yml
│ └── docker-compose.yml
└── azure-pipelines.yml
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | First off, thanks for taking the time to contribute!
4 |
5 | ### How can I contribute?
6 |
7 | * Fork this project;
8 | * Make your changes / new implementatios;
9 | * Use the pattern for git commts;
10 | * Make sure that the acceptance criteria are met (tests, docs, etc);
11 | * Create a pull request;
12 |
13 | ### Pull Requests
14 |
15 | Template [PULLREQUEST-TEMPLATE](PULLREQUEST-TEMPLATE.md)
16 |
17 | ### Git Commit Messages
18 |
19 | * Use the present tense ("Adds feature" not "Added feature")
20 | * Limit the first line to 72 characters or less
21 | * Reference issues and pull requests liberally
22 | * Consider starting the commit message with an applicable emoji:
23 | * :art: `:art:` when improving the format/structure of the code
24 | * :racehorse: `:racehorse:` when improving performance
25 | * :non-potable_water: `:non-potable_water:` when plugging memory leaks
26 | * :memo: `:memo:` when writing docs
27 | * :penguin: `:penguin:` when fixing something on Linux
28 | * :apple: `:apple:` when fixing something on Mac OS
29 | * :checkered_flag: `:checkered_flag:` when fixing something on Windows
30 | * :bug: `:bug:` when fixing a bug
31 | * :fire: `:fire:` when removing code or files
32 | * :green_heart: `:green_heart:` when fixing the CI build
33 | * :white_check_mark: `:white_check_mark:` when adding tests
34 | * :lock: `:lock:` when dealing with security
35 | * :arrow_up: `:arrow_up:` when upgrading dependencies
36 | * :arrow_down: `:arrow_down:` when downgrading dependencies
37 | * :shirt: `:shirt:` when removing linter warnings
38 | * :bulb: `:bulb:` new idea
39 | * :construction: `:construction:` work in progress
40 | * :heavy_plus_sign: `:heavy_plus_sign:` when adding features
41 | * :heavy_minus_sign: `:heavy_minus_sign:` when removing features
42 | * :speaker: `:mute:` when adding logging
43 | * :mute: `:mute:` when reducing logging
44 | * :facepunch: `:facepunch:` when resolve conflict
45 | * :wrench: `:wrench:` when modify Web.config
46 |
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ```
2 | Please use the following template to submit your issue.
3 | Following this template will allow us to quickly investigate and help you with your issue.
4 | Please be aware that issues which do not conform to this template may be closed.
5 |
6 | DO NOT FORGET TO REMOVE THIS BLOCK
7 | ```
8 |
9 | ### Status
10 |
11 | BUG REPORT / TASK
12 |
13 | ### Checklist
14 |
15 | Add checklist if this is a task
16 |
17 | - [x] Add slack integration
18 | - [_] Support xyz
19 |
20 | ### Steps
21 |
22 | 1. First step
23 | 2. Second step
24 | 3. Third step
25 |
26 | ### Expected behaviour
27 |
28 | How do you think the program should work? Add screenshots and code blocks if necessary.
29 |
30 | ### Actual behaviour
31 |
32 | How does the program work in its current state?
33 |
34 | ### Environment
35 |
36 | You may write here the specifications like the version of the project, services, operating system, or hardware if applicable.
37 |
38 | ### Logs / Stack trace
39 |
40 | ```
41 | Insert your log/stack trace here
42 | ```
43 |
44 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ```
2 | This is a guide to use this Pull Request Template.
3 |
4 | # Title
5 | [feature] Implements the crazy powerful transaction search
6 | [hotfix] Fixes login with e-mail address
7 |
8 | Add a gif that expresses your reaction to the implemented code, make it fun
9 |
10 | DO NOT FORGET TO REMOVE THIS BLOCK
11 | ```
12 | 
13 |
14 | ### Status
15 |
16 | READY / IN DEVELOPMENT
17 |
18 | ### Whats?
19 |
20 | Describe in an objective way what has been done.
21 |
22 | ### Why?
23 |
24 | Why do you need this implementation/fix?
25 |
26 | ### How?
27 |
28 | How did you solve the problem? What are the main flows? Any technical information regarding infrastructure or architecture?
29 |
30 | ### Attachments (if appropriate)
31 |
32 | Add additional informations like screenshots, issue link, zendesk ticket link, jira task link, etc
33 |
34 | ### Definition of Done:
35 | - [ ] Increases API documentation
36 | - [ ] Implements integration tests
37 | - [ ] Implements unit tests
38 | - [ ] Is there appropriate logging included?
39 | - [ ] Does this add new dependencies?
40 | - [ ] Does need add new version in changelog?
41 | - [ ] Does need update readme, contributing, etc?
42 | - [ ] Does need change in CI server?
43 | - [ ] Will this feature require a new piece of infrastructure be implemented?
44 | - [ ] Does this PR require a blog post? If so, ensure marketing has signed off on content.
45 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/AspNetScaffolding3.DemoApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Always
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Controllers/PersonController.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.DemoApi.Models;
2 | using AspNetScaffolding.Extensions.Cache;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.Extensions.Caching.Distributed;
5 | using Mundipagg;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Net;
9 | using System.Threading.Tasks;
10 | using WebApi.Models.Exceptions;
11 | using WebApi.Models.Response;
12 |
13 | namespace AspNetScaffolding.Controllers
14 | {
15 | public class PersonController : BaseController
16 | {
17 | private IDistributedCache DistributedCache { get; set; }
18 |
19 | private ILocker Locker { get; set; }
20 |
21 | public PersonController(IDistributedCache cache, ILocker locker, IMundipaggApiClient client)
22 | {
23 | this.Locker = locker;
24 | this.DistributedCache = cache;
25 | }
26 |
27 | [HttpGet("persons/{id}")]
28 | [ProducesResponseType(typeof(Person), 200)]
29 | [ProducesResponseType(typeof(ErrorsResponse), 400)]
30 | [ProducesResponseType(404)]
31 | [ProducesResponseType(500)]
32 | public async Task Get()
33 | {
34 | var person = new Person
35 | {
36 | FirstName = "John",
37 | LastName = "Doe",
38 | Birthdate = DateTime.UtcNow.AddMonths(30),
39 | Email = "john.doe@email.com",
40 | Type = PersonType.PhysicalPerson
41 | };
42 |
43 | return Ok(person);
44 |
45 | var options = new DistributedCacheEntryOptions
46 | {
47 | AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
48 | };
49 |
50 | await this.DistributedCache.SetStringAsync("123123", "teste", options);
51 |
52 | var locker = await this.Locker.GetDistributedLockerAsync("teste", 600);
53 |
54 | if (!locker.IsAcquired)
55 | {
56 | throw new ConflictException();
57 | }
58 |
59 | var apiResponse = new ApiResponse
60 | {
61 | Content = person,
62 | StatusCode = HttpStatusCode.Accepted,
63 | Headers = new Dictionary
64 | {
65 | { "SomeHeader", "Test Get" }
66 | }
67 | };
68 |
69 | return CreateJsonResponse(apiResponse);
70 | }
71 |
72 | [HttpPost("persons")]
73 | [ProducesResponseType(typeof(Person), 200)]
74 | [ProducesResponseType(typeof(ErrorsResponse), 400)]
75 | [ProducesResponseType(404)]
76 | [ProducesResponseType(500)]
77 | public IActionResult Create([FromBody]Person person)
78 | {
79 | Validate(person);
80 |
81 | var apiResponse = new ApiResponse
82 | {
83 | Content = person,
84 | StatusCode = HttpStatusCode.Created,
85 | Headers = new Dictionary
86 | {
87 | { "SomeHeader", "Test Created" }
88 | }
89 | };
90 |
91 | return CreateJsonResponse(apiResponse);
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Controllers/ProxyController.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Controllers;
2 | using AspNetScaffolding.Extensions.RoutePrefix;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace AspNetScaffolding3.DemoApi.Controllers
6 | {
7 | [IgnoreRoutePrefix]
8 | public class ProxyController : BaseController
9 | {
10 | [HttpGet, HttpPost, HttpPut, HttpPatch, HttpDelete, HttpHead, HttpOptions]
11 | [ProducesResponseType(200)]
12 | [ProducesResponseType(201)]
13 | [ProducesResponseType(401)]
14 | [ProducesResponseType(409)]
15 | [ProducesResponseType(412)]
16 | [ProducesResponseType(422)]
17 | [ProducesResponseType(500)]
18 | public IActionResult HandleAllRequests()
19 | {
20 | return Ok();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/DOCS.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | Lorem ipsum
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Entities/Customer.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.DemoApi.Entities
2 | {
3 | public class Customer
4 | {
5 |
6 | public Customer(string customerId, string otherProp)
7 | {
8 | CustomerId = customerId;
9 | OtherProp = otherProp;
10 | }
11 |
12 | public string CustomerId { get; private set; }
13 |
14 | public string OtherProp { get; private set; }
15 |
16 |
17 | public string OnlyResponse { get; private set; } = "test";
18 |
19 |
20 | public string OnlyRequest { get; private set; } = "test";
21 |
22 | public SubTest SubTest { get; set; } = new SubTest();
23 |
24 | public Test TestTest { get; set; }
25 | }
26 |
27 | public enum Test
28 | {
29 | TEst_TESt,
30 | TESTE_TEST,
31 |
32 | }
33 |
34 | public class SubTest
35 | {
36 | public string OnlyResponse { get; private set; } = "test";
37 |
38 | public string OnlyRequest { get; private set; } = "test";
39 | }
40 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Models/Address.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace AspNetScaffolding.DemoApi.Models
4 | {
5 | public class Address
6 | {
7 | public string Line1 { get; set; }
8 |
9 | public string Line2 { get; set; }
10 |
11 | public string CityCode { get; set; }
12 | }
13 |
14 | public class AddressValidator : AbstractValidator
15 | {
16 | public AddressValidator()
17 | {
18 | RuleFor(p => p.Line1).NotEmpty().Length(3, 30);
19 | RuleFor(p => p.Line2).NotEmpty().Length(3, 30);
20 | RuleFor(p => p.CityCode).NotEmpty().Length(2);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Models/CustomerRequest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using System;
3 |
4 | namespace AspNetScaffolding.DemoApi.Models
5 | {
6 | public class CustomerRequest
7 | {
8 | [FromRoute] public string CustomerId { get; set; }
9 |
10 | [FromQuery] public string ServiceId { get; set; }
11 | }
12 |
13 | public class CustomerRequest2
14 | {
15 | [FromRoute] public string CustomerId { get; set; }
16 |
17 | [FromBody] public string OtherProp { get; set; }
18 |
19 | [FromBody] public DateTime DateTest { get; set; }
20 | }
21 |
22 | public class CustomerRequest3
23 | {
24 | public string CustomerId { get; set; }
25 |
26 | public string ServiceId { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Models/Person.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Newtonsoft.Json;
3 | using PackUtils.Converters;
4 | using System;
5 |
6 | namespace AspNetScaffolding.DemoApi.Models
7 | {
8 | public class Person
9 | {
10 | public string FirstName { get; set; }
11 |
12 | public string LastName { get; set; }
13 |
14 | [JsonConverter(typeof(DateConverter))]
15 | public DateTime Birthdate { get; set; }
16 |
17 | public DateTime Now { get; set; } = DateTime.UtcNow;
18 |
19 | public string Email { get; set; }
20 |
21 | public Address Address { get; set; }
22 |
23 | public PersonType Type { get; set; }
24 | }
25 |
26 | public enum PersonType
27 | {
28 | Undefined,
29 | PhysicalPerson,
30 | LegalEntity
31 | }
32 |
33 | public class PersonValidator : AbstractValidator
34 | {
35 | public PersonValidator()
36 | {
37 | RuleFor(p => p.FirstName).NotEmpty().Length(3, 30);
38 | RuleFor(p => p.LastName).NotEmpty().Length(3, 30);
39 | RuleFor(p => p.Email).NotEmpty().EmailAddress();
40 | RuleFor(p => p.Address).NotNull().SetValidator(new AddressValidator());
41 | RuleFor(p => p.Type).Must(p => p != PersonType.Undefined);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Profiles/CustomerProfile.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.DemoApi.Entities;
2 | using AspNetScaffolding.DemoApi.Models;
3 |
4 | namespace AspNetScaffolding.DemoApi.Profiles
5 | {
6 | public class CustomerProfile : AutoMapper.Profile
7 | {
8 | public CustomerProfile()
9 | {
10 | CreateMap();
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Program.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.RequestKey;
2 | using AspNetScaffolding.Models;
3 | using AspNetScaffolding3.DemoApi.Controllers;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.AspNetCore.Routing.Constraints;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Mundipagg;
8 | using System;
9 | using System.Reflection;
10 |
11 | namespace AspNetScaffolding.DemoApi
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | var config = new ApiBasicConfiguration
18 | {
19 | ApiName = "My AspNet Scaffolding",
20 | ApiPort = 8700,
21 | EnvironmentVariablesPrefix = "Prefix_",
22 | ConfigureHealthcheck = ConfigureHealthcheck,
23 | ConfigureServices = ConfigureServices,
24 | Configure = Configure,
25 | ConfigureAfter = ConfigureAfter,
26 | AutoRegisterAssemblies = new Assembly[]
27 | { Assembly.GetExecutingAssembly() }
28 | };
29 |
30 | Api.Run(config);
31 | }
32 |
33 | public static void ConfigureHealthcheck(IHealthChecksBuilder builder, IServiceProvider provider)
34 | {
35 | // add health check configuration
36 | builder.AddUrlGroup(new Uri("https://www.google.com"), "google");
37 | //builder.AddMongoDb("mongodb://localhost:27017");
38 | }
39 |
40 | public static void ConfigureServices(IServiceCollection services)
41 | {
42 | var apiUrl = "https://api.mundipagg.com/core/v1";
43 |
44 | services.AddScoped(provider =>
45 | {
46 | return new MundipaggApiClient(null, provider.GetService().Value, apiUrl, 120000);
47 | });
48 |
49 | // add services
50 | //services.AddSingleton();
51 | }
52 |
53 | public static void Configure(IApplicationBuilder app)
54 | {
55 | // customize your app
56 | //app.UseAuthentication();
57 | }
58 |
59 | public static void ConfigureAfter(IApplicationBuilder app)
60 | {
61 | var prefix = Api.ApiSettings.GetPathPrefixConsideringVersion();
62 |
63 | var controllerName = nameof(ProxyController).Replace("Controller", "");
64 | var actionName = nameof(ProxyController.HandleAllRequests);
65 |
66 | var method = "POST";
67 | var path = prefix + "/transactions/{id}";
68 | var resourceName = "transactions.create";
69 |
70 | app.UseEndpoints(endpoints =>
71 | {
72 | endpoints.MapControllerRoute(resourceName, path,
73 | defaults: new
74 | {
75 | controller = controllerName,
76 | action = actionName,
77 | resource = resourceName
78 | },
79 | constraints: new
80 | {
81 | httpMethod = new HttpMethodRouteConstraint(method)
82 | });
83 | endpoints.MapControllerRoute("xpto", prefix + "/xpto",
84 | defaults: new
85 | {
86 | controller = controllerName,
87 | action = actionName,
88 | resource = "xpto"
89 | },
90 | constraints: new
91 | {
92 | httpMethod = new HttpMethodRouteConstraint("GET")
93 | });
94 | });
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ApiSettings": {
3 | "AppUrl": "http://localhost:5855",
4 | "JsonSerializer": "Snakecase",
5 | "PathPrefix": "myapp/{version}",
6 | "UseStaticFiles": true,
7 | "StaticFilesPath": "assets",
8 | "Domain": "MyDomain",
9 | "Application": "MyApp",
10 | "Version": "v1",
11 | "BuildVersion": "1.0.0",
12 | "SupportedCultures": [ "en-US", "pt-BR", "es-ES" ],
13 | "UseOriginalEnumValue": true,
14 | "RequestKeyProperty": "RequestKey",
15 | "AccountIdProperty": "AccountId",
16 | "TimezoneHeader": "Timezone",
17 | "TimezoneDefault": "America/Sao_Paulo",
18 | "TimeElapsedProperty": "X-Internal-Time",
19 | "JsonFieldSelectorProperty": "fields"
20 | },
21 | "ShutdownSettings": {
22 | "ShutdownTimeoutInSeconds": 10,
23 | "Enabled": true,
24 | "Redirect": false
25 | },
26 | "HealthcheckSettings": {
27 | "Enabled": true,
28 | "Path": "healthcheck",
29 | "LogEnabled": false
30 | },
31 | "DbSettings": {
32 | "ConnectionString": "mongodb://user:pass@localhost:27017/DatabaseName",
33 | "Name": "DatabaseName"
34 | },
35 | "CacheSettings": {
36 | "Enabled": true,
37 | "UseRedis": false,
38 | "UseLocker": false,
39 | "TimeoutInMs": 1000,
40 | "Ssl": false,
41 | "Password": "RedisAuth",
42 | "Host": "localhost",
43 | "Port": 6379,
44 | "LockerPrefix": "app-locker-",
45 | "LockerTtlInSeconds": 100,
46 | "LockerDb": 0,
47 | "CachePrefix": "app-cache-",
48 | "CacheTtlInSeconds": 900,
49 | "CacheDb": 0
50 | },
51 | "QueueSettings": {
52 | "Enabled": false,
53 | "RetryTTL": 20000,
54 | "RetryTTLFactor": 2.0,
55 | "RetryCount": 5,
56 | "QueueConnectionString": "amqp://guest:guest@localhost:5672/VHost",
57 | "VHostApi": "http://guest:guest@localhost:15672/api/queues/VHost",
58 | "QueueName": "my-queue",
59 | "ExchangeToSubscribe": "main.topic",
60 | "EventsToSubscribe": "event.something.created,event.other.#",
61 | "MaxThreads": 200,
62 | "AutoAckOnSuccess": true
63 | },
64 | "IpRateLimiting": {
65 | "Enabled": false,
66 | "EnableEndpointRateLimiting": false,
67 | "StackBlockedRequests": false,
68 | "RealIpHeader": "X-Real-IP",
69 | "ClientIdHeader": "X-ClientId",
70 | "HttpStatusCode": 429,
71 | "IpWhitelist": [],
72 | "EndpointWhitelist": [],
73 | "ClientWhitelist": [],
74 | "GeneralRules": [
75 | {
76 | "Endpoint": "*",
77 | "Period": "1m",
78 | "Limit": 5
79 | },
80 | {
81 | "Endpoint": "*",
82 | "Period": "1h",
83 | "Limit": 1000
84 | }
85 | ]
86 | },
87 | "LogSettings": {
88 | "DebugEnabled": false,
89 | "TitlePrefix": "[{Application}] ",
90 | "JsonBlacklistRequest": [ "*password", "*card.number", "*creditcardnumber", "*cvv", "*only_request" ],
91 | "JsonBlacklistResponse": [ "*password", "*card.number", "*creditcardnumber", "*cvv", "*only_response" ],
92 | "HeaderBlacklist": [ "Authorization" ],
93 | "QueryStringBlacklist": [ "customer_key" ],
94 | "SeqOptions": {
95 | "Enabled": false,
96 | "MinimumLevel": "Verbose",
97 | "Url": "http://localhost:5341",
98 | "ApiKey": "XXXX"
99 | },
100 | "NewRelicOptions": {
101 | "Enabled": false,
102 | "AppName": "Verbose",
103 | "LicenseKey": "XXXX"
104 | },
105 | "SplunkOptions": {
106 | "Enabled": false,
107 | "MinimumLevel": "Verbose",
108 | "Url": "http://localhost:8088/services/collector",
109 | "Token": "XXXX",
110 | "Index": "my.index",
111 | "Application": "MyApp :Ds",
112 | "ProcessName": "Domain.App",
113 | "Company": "MyCompany",
114 | "ProductVersion": "1.0.0",
115 | "SourceType": "_json"
116 | },
117 | "DataDogOptions": {
118 | "Enabled": true,
119 | "MinimumLevel": "Verbose",
120 | "ApiKey": "xxx",
121 | "Service": null,
122 | "Source": null,
123 | "Host": null,
124 | "Tags": []
125 | },
126 | "ConsoleOptions": {
127 | "Enabled": true,
128 | "MinimumLevel": "Verbose"
129 | }
130 | },
131 | "DocsSettings": {
132 | "Enabled": true,
133 | "Title": "MyApp API Reference",
134 | "AuthorName": "Thiago Barradas",
135 | "AuthorEmail": "th.barradas@gmail.com",
136 | "PathToReadme": "DOCS.md",
137 | "IgnoredEnums": [ "none", "undefined" ]
138 | }
139 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoApi/wwwroot/done.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | teste
10 |
11 |
12 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/AspNetScaffolding3.DemoWorker.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using AspNetScaffolding;
3 | using AspNetScaffolding.Models;
4 |
5 | namespace AspNetScaffolding3.DemoWorker
6 | {
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | var config = new ApiBasicConfiguration
12 | {
13 | ApiName = "Demo Worker",
14 | ApiPort = 8701,
15 | EnvironmentVariablesPrefix = "APP_",
16 | ConfigureHealthcheck = Startup.ConfigureHealthcheck,
17 | ConfigureServices = Startup.ConfigureServices,
18 | Configure = Startup.Configure,
19 | AutoRegisterAssemblies = new Assembly[]
20 | { Assembly.GetExecutingAssembly() }
21 | };
22 |
23 | Api.Run(config);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:57050",
8 | "sslPort": 44385
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "weatherforecast",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "AspNetScaffolding3.DemoWorker": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "weatherforecast",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using AspNetScaffolding.Extensions.Queue;
4 | using AspNetScaffolding3.DemoWorker.Workers;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace AspNetScaffolding3.DemoWorker
9 | {
10 | [ExcludeFromCodeCoverage]
11 | public static class Startup
12 | {
13 | public static void ConfigureHealthcheck(IHealthChecksBuilder builder, IServiceProvider provider)
14 | {
15 | QueueHealthcheck.AddRabbitMqAutomatic(builder, provider);
16 | }
17 |
18 | public static void ConfigureServices(IServiceCollection services)
19 | {
20 | services
21 | .SetupServicesConfiguration()
22 | .SetupQueueConfiguration();
23 | }
24 |
25 | public static void Configure(IApplicationBuilder app)
26 | {
27 | }
28 |
29 | private static IServiceCollection SetupServicesConfiguration(this IServiceCollection services)
30 | {
31 | return services;
32 | }
33 |
34 | private static IServiceCollection SetupQueueConfiguration(this IServiceCollection services)
35 | {
36 | #region -- Workers --
37 |
38 | services.SetupWorker();
39 |
40 | #endregion
41 |
42 | return services;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/Workers/WorkerRunner.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using AspNetScaffolding.Extensions.Logger;
3 | using AspNetScaffolding.Extensions.Queue.Interfaces;
4 | using AspNetScaffolding.Extensions.RequestKey;
5 | using AspNetScaffolding.Extensions.Worker;
6 |
7 |
8 | namespace AspNetScaffolding3.DemoWorker.Workers
9 | {
10 | public class WorkerRunner : BaseWorkerRunner
11 | {
12 | public WorkerRunner(IQueueProcessor queueProcessor) : base(queueProcessor)
13 | {
14 | this.InitFunction(ExecuteAsync);
15 | }
16 |
17 | public override async Task ExecuteAsync(string message, int retryCount, ulong deliveryTag, string requestKey)
18 | {
19 | var simpleLogger = new SimpleLogger(new RequestKey(requestKey));
20 |
21 | var teste = new
22 | {
23 | test = "1",
24 | test_1 = "123",
25 | password = "hellomyfriend",
26 | creditCard = "123456788911"
27 | };
28 |
29 | simpleLogger
30 | .Info(nameof(WorkerRunner), "CreateTransact", "Generate cryptogram", teste);
31 |
32 | return true;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3.DemoWorker/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetScaffolding3.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29418.71
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6BC787A5-75A6-455E-8F77-5B27EC826F05}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetScaffolding3", "AspNetScaffolding3\AspNetScaffolding3.csproj", "{C62F17AB-7EA4-4549-9C78-0618CEDC2C1F}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetScaffolding3.DemoApi", "AspNetScaffolding3.DemoApi\AspNetScaffolding3.DemoApi.csproj", "{7852D13E-020D-47CF-97A2-6E84F43AD41D}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{6EF09181-69F5-4FB8-82DF-89E096007B85}"
13 | ProjectSection(SolutionItems) = preProject
14 | .gitattributes = .gitattributes
15 | .gitignore = .gitignore
16 | EndProjectSection
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{472E49FE-D82F-4E41-AC5D-519C9D291CC6}"
19 | ProjectSection(SolutionItems) = preProject
20 | .github\CONTRIBUTING.md = .github\CONTRIBUTING.md
21 | .github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
22 | LICENSE = LICENSE
23 | .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
24 | README.md = README.md
25 | EndProjectSection
26 | EndProject
27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "devops", "devops", "{F554D092-6DFC-49FB-894C-2582917C1393}"
28 | ProjectSection(SolutionItems) = preProject
29 | azure-pipelines.yml = azure-pipelines.yml
30 | EndProjectSection
31 | EndProject
32 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetScaffolding3Tests", "AspNetScaffolding3Tests\AspNetScaffolding3Tests.csproj", "{BB3602B0-49B1-43F3-A574-A10199F18204}"
33 | EndProject
34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetScaffolding3.DemoWorker", "AspNetScaffolding3.DemoWorker\AspNetScaffolding3.DemoWorker.csproj", "{72F91E52-9B22-4EAB-B4D1-417872978836}"
35 | EndProject
36 | Global
37 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
38 | Debug|Any CPU = Debug|Any CPU
39 | Release|Any CPU = Release|Any CPU
40 | EndGlobalSection
41 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
42 | {C62F17AB-7EA4-4549-9C78-0618CEDC2C1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {C62F17AB-7EA4-4549-9C78-0618CEDC2C1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {C62F17AB-7EA4-4549-9C78-0618CEDC2C1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {C62F17AB-7EA4-4549-9C78-0618CEDC2C1F}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {7852D13E-020D-47CF-97A2-6E84F43AD41D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {7852D13E-020D-47CF-97A2-6E84F43AD41D}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {7852D13E-020D-47CF-97A2-6E84F43AD41D}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {7852D13E-020D-47CF-97A2-6E84F43AD41D}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {BB3602B0-49B1-43F3-A574-A10199F18204}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {BB3602B0-49B1-43F3-A574-A10199F18204}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {BB3602B0-49B1-43F3-A574-A10199F18204}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {BB3602B0-49B1-43F3-A574-A10199F18204}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {72F91E52-9B22-4EAB-B4D1-417872978836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {72F91E52-9B22-4EAB-B4D1-417872978836}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {72F91E52-9B22-4EAB-B4D1-417872978836}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {72F91E52-9B22-4EAB-B4D1-417872978836}.Release|Any CPU.Build.0 = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(NestedProjects) = preSolution
63 | {6EF09181-69F5-4FB8-82DF-89E096007B85} = {6BC787A5-75A6-455E-8F77-5B27EC826F05}
64 | {472E49FE-D82F-4E41-AC5D-519C9D291CC6} = {6BC787A5-75A6-455E-8F77-5B27EC826F05}
65 | {F554D092-6DFC-49FB-894C-2582917C1393} = {6BC787A5-75A6-455E-8F77-5B27EC826F05}
66 | EndGlobalSection
67 | GlobalSection(ExtensibilityGlobals) = postSolution
68 | SolutionGuid = {B3368CDA-F6C0-4F7C-936A-18E5A37F6D28}
69 | EndGlobalSection
70 | EndGlobal
71 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Api.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.Cache;
2 | using AspNetScaffolding.Extensions.Docs;
3 | using AspNetScaffolding.Extensions.GracefullShutdown;
4 | using AspNetScaffolding.Extensions.Healthcheck;
5 | using AspNetScaffolding.Extensions.Logger;
6 | using AspNetScaffolding.Extensions.Queue;
7 | using AspNetScaffolding.Extensions.RequestLimit;
8 | using AspNetScaffolding.Extensions.Worker;
9 | using AspNetScaffolding.Models;
10 | using Microsoft.AspNetCore.Hosting;
11 | using Microsoft.Extensions.Configuration;
12 | using System;
13 | using System.IO;
14 | using AspNetScaffolding3.Extensions.RequestLimit;
15 |
16 | namespace AspNetScaffolding
17 | {
18 | public static class Api
19 | {
20 | public static ApiBasicConfiguration ApiBasicConfiguration { get; set; } = new ApiBasicConfiguration();
21 |
22 | public static IConfigurationRoot ConfigurationRoot { get; set; }
23 |
24 | public static ApiSettings ApiSettings { get; set; } = new ApiSettings();
25 |
26 | public static HealthcheckSettings HealthcheckSettings { get; set; } = new HealthcheckSettings();
27 |
28 | public static LoggerSettings LogSettings { get; set; } = new LoggerSettings();
29 |
30 | public static DatabaseSettings DatabaseSettings { get; set; } = new DatabaseSettings();
31 |
32 | public static DocsSettings DocsSettings { get; set; } = new DocsSettings();
33 |
34 | public static ShutdownSettings ShutdownSettings { get; set; } = new ShutdownSettings();
35 |
36 | public static QueueSettings QueueSettings { get; set; } = new QueueSettings();
37 |
38 | public static RateLimitingAdditional RateLimitingAdditional { get; set; } = new RateLimitingAdditional();
39 |
40 | public static CacheSettings CacheSettings { get; set; } = new CacheSettings();
41 |
42 | public static WorkerSettings WorkerSettings { get; set; } = new WorkerSettings();
43 |
44 | public static void Run(ApiBasicConfiguration apiBasicConfiguration)
45 | {
46 | ApiBasicConfiguration = apiBasicConfiguration;
47 |
48 | Console.WriteLine("{0} is running...", ApiBasicConfiguration.ApiName);
49 |
50 | var host = new WebHostBuilder()
51 | .UseKestrel(options => options.AllowSynchronousIO = true)
52 | .UseUrls("http://*:" + ApiBasicConfiguration.ApiPort.ToString())
53 | .UseContentRoot(Directory.GetCurrentDirectory())
54 | .UseStartup();
55 |
56 | host.Build().Run();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/AspNetScaffolding3.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | InProcess
6 | true
7 | https://i.imgur.com/Wq5D0gQ.png
8 | https://github.com/thiagobarradas/aspnet-scaffolding3
9 | aspnet, scaffolding, splunk, seq, healthcheck, timezone, requestkey, timeelapsed, api, core, json
10 | Thiago Barradas
11 | Thiago Barradas
12 | https://github.com/thiagobarradas/aspnet-scaffolding3/blob/master/LICENSE
13 | https://github.com/thiagobarradas/aspnet-scaffolding3
14 | AspNet Scaffolding with log, serializer, and all structure to work good for me :D
15 | Ⓒ Thiago Barradas
16 | false
17 | false
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Never
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Controllers/BaseController.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.Cors;
2 | using AspNetScaffolding.Extensions.GracefullShutdown;
3 | using AspNetScaffolding.Extensions.JsonSerializer;
4 | using AspNetSerilog;
5 | using Microsoft.AspNetCore.Cors;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.ModelBinding;
9 | using PackUtils;
10 | using System;
11 | using System.IO;
12 | using System.Linq;
13 | using WebApi.Models.Exceptions;
14 | using WebApi.Models.Response;
15 |
16 | namespace AspNetScaffolding.Controllers
17 | {
18 | [SerilogFilter]
19 | [EnableCors(CorsServiceExtension.CorsName)]
20 | public class BaseController : ControllerBase
21 | {
22 | public BaseController()
23 | {
24 | this.ValidationShutdown();
25 | }
26 |
27 | protected IActionResult CreateJsonResponse(ApiResponse response)
28 | {
29 | IActionResult result;
30 |
31 | if (response.Content != null)
32 | {
33 | result = new JsonResult(response.Content)
34 | {
35 | StatusCode = (int)response.StatusCode
36 | };
37 | }
38 | else
39 | {
40 | result = new StatusCodeResult((int)response.StatusCode);
41 | Response.ContentType = "application/json";
42 | }
43 |
44 | if (response.Headers != null)
45 | {
46 | foreach (var header in response.Headers)
47 | {
48 | Response.Headers[header.Key] = header.Value;
49 | }
50 | }
51 |
52 | return result;
53 | }
54 |
55 | protected void ValidateSignatureFromHeaderWithContent(string secretKey, string headerName)
56 | {
57 | var result = false;
58 | var signature = Request.Headers[headerName].FirstOrDefault() ?? string.Empty;
59 |
60 | if (Request.Body.CanRead &&
61 | Request.Body.CanSeek)
62 | {
63 | try
64 | {
65 | Request.EnableBuffering();
66 | }
67 | catch (Exception) { }
68 |
69 | MemoryStream stream = new MemoryStream();
70 | Request.Body.Seek(0, SeekOrigin.Begin);
71 | Request.Body.CopyTo(stream);
72 | Request.Body.Seek(0, SeekOrigin.Begin);
73 |
74 | using (StreamReader reader = new StreamReader(stream))
75 | {
76 | stream.Seek(0, SeekOrigin.Begin);
77 | var content = reader.ReadToEnd();
78 | result = SignatureUtility.ValidateSignature(signature, secretKey, content);
79 | }
80 | }
81 |
82 | if (result == false)
83 | {
84 | throw new UnauthorizedException();
85 | }
86 | }
87 |
88 | protected void Validate(TRequest request) where TRequest : class, new()
89 | {
90 | if (ModelState.IsValid == false)
91 | {
92 | ErrorsResponse errors = CastModelValidationResultToErrorsResponse(ModelState);
93 | throw new BadRequestException(errors);
94 | }
95 | }
96 |
97 | private ErrorsResponse CastModelValidationResultToErrorsResponse(ModelStateDictionary modelState)
98 | {
99 | ErrorsResponse errorsResponse = new ErrorsResponse();
100 |
101 | foreach (var errorPerProperty in modelState)
102 | {
103 | foreach (var errorDetail in errorPerProperty.Value.Errors)
104 | {
105 | var propertyName = string.Join(".",
106 | errorPerProperty.Key.Split(".")
107 | .Select(r => r.GetValueConsideringCurrentCase()));
108 |
109 | errorsResponse.AddError(errorDetail.ErrorMessage, propertyName);
110 | }
111 | }
112 |
113 | return errorsResponse;
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.GracefullShutdown;
2 | using AspNetScaffolding.Extensions.JsonSerializer;
3 | using AspNetScaffolding.Extensions.RequestKey;
4 | using AspNetScaffolding.Utilities;
5 | using AspNetSerilog.Extensions;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using PackUtils.Converters;
9 | using System;
10 |
11 | namespace AspNetScaffolding.Controllers
12 | {
13 | public class HomeController : BaseController
14 | {
15 | protected readonly RequestKey RequestKey;
16 |
17 | protected readonly IHttpContextAccessor HttpContextAccessor;
18 |
19 | protected readonly GracefullShutdownState GracefullShutdownState;
20 |
21 | public HomeController(
22 | IHttpContextAccessor httpContextAccessor,
23 | GracefullShutdownState gracefullShutdownState,
24 | RequestKey requestKey)
25 | {
26 | HttpContextAccessor = httpContextAccessor;
27 | GracefullShutdownState = gracefullShutdownState;
28 | RequestKey = requestKey;
29 | }
30 |
31 | [HttpGet]
32 | public IActionResult GetAppInfo()
33 | {
34 | this.DisableLogging();
35 |
36 | return Ok(new HomeDetails
37 | {
38 | RIP = GracefullShutdownState.RequestsInProgress,
39 | Service = Api.ApiBasicConfiguration?.ApiName,
40 | BuildVersion = Api.ApiSettings?.BuildVersion,
41 | Environment = EnvironmentUtility.GetCurrentEnvironment(),
42 | RequestKey = RequestKey.Value,
43 | Application = Api.ApiSettings.Application,
44 | Domain = Api.ApiSettings.Domain,
45 | JsonSerializer = Api.ApiSettings.JsonSerializer,
46 | EnvironmentPrefix = Api.ApiBasicConfiguration.EnvironmentVariablesPrefix,
47 | TimezoneInfo = new TimezoneInfo(HttpContextAccessor)
48 | });
49 | }
50 |
51 | public class HomeDetails
52 | {
53 | public string Service { get; set; }
54 |
55 | public string BuildVersion { get; set; }
56 |
57 | public string Environment { get; set; }
58 |
59 | public string Application { get; set; }
60 |
61 | public string Domain { get; set; }
62 |
63 | public string EnvironmentPrefix { get; set; }
64 |
65 | public long RIP { get; set; }
66 |
67 | public JsonSerializerEnum JsonSerializer { get; set; }
68 |
69 | public string RequestKey { get; set; }
70 |
71 | public TimezoneInfo TimezoneInfo { get; set; }
72 | }
73 |
74 | public class TimezoneInfo
75 | {
76 | public TimezoneInfo(IHttpContextAccessor httpContextAccessor)
77 | {
78 | CurrentTimezone = DateTimeConverter.GetTimeZoneByAspNetHeader(
79 | httpContextAccessor,
80 | Api.ApiSettings.TimezoneHeader).Id;
81 | }
82 |
83 | public string UtcNow => DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss");
84 |
85 | public DateTime CurrentNow => DateTime.UtcNow;
86 |
87 | public string DefaultTimezone => Api.ApiSettings.TimezoneDefaultInfo.Id;
88 |
89 | public string CurrentTimezone { get; set; }
90 |
91 | public string TimezoneHeader => Api.ApiSettings.TimezoneHeader;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/AccountId/AccountId.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.Extensions.AccountId
2 | {
3 | public class AccountId
4 | {
5 | public AccountId() { }
6 |
7 | public AccountId(string value)
8 | {
9 | Value = value;
10 | }
11 |
12 | public string Value { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/AccountId/AccountIdMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Http;
3 | using System.Threading.Tasks;
4 |
5 | namespace AspNetScaffolding.Extensions.AccountId
6 | {
7 | public class AccountIdMiddleware
8 | {
9 | private readonly RequestDelegate Next;
10 |
11 | public AccountIdMiddleware(RequestDelegate next)
12 | {
13 | Next = next;
14 | }
15 |
16 | public async Task Invoke(HttpContext context, AccountId accountId)
17 | {
18 | await Next(context);
19 |
20 | context.Items.Add(AccountIdServiceExtension.AccountIdHeaderName, accountId.Value);
21 | }
22 | }
23 |
24 | public static class AccountIdMiddlewareExtension
25 | {
26 | public static void UseAccountId(this IApplicationBuilder app)
27 | {
28 | app.UseMiddleware();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/AccountId/AccountIdService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace AspNetScaffolding.Extensions.AccountId
4 | {
5 | public static class AccountIdServiceExtension
6 | {
7 | public static string AccountIdHeaderName = "AccountId";
8 |
9 | public static void SetupAccountId(this IServiceCollection services, string headerName = null)
10 | {
11 | if (string.IsNullOrWhiteSpace(headerName) == false)
12 | {
13 | AccountIdHeaderName = headerName;
14 | }
15 |
16 | services.AddScoped();
17 | services.AddScoped(obj => new AccountId());
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Cache/CacheService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace AspNetScaffolding.Extensions.Cache
4 | {
5 | public static class CacheService
6 | {
7 | public static void SetupCache(
8 | this IServiceCollection services,
9 | CacheSettings cacheSettings)
10 | {
11 | if (cacheSettings.UseLocker)
12 | {
13 | services.AddSingleton(cacheSettings);
14 | services.AddSingleton();
15 | }
16 |
17 | if (cacheSettings?.Enabled == true)
18 | {
19 | if (cacheSettings.UseRedis)
20 | {
21 | services.AddStackExchangeRedisCache(options =>
22 | {
23 | options.Configuration = cacheSettings.GetCacheConnectionString();
24 | options.InstanceName = cacheSettings.CachePrefix;
25 | });
26 | }
27 | else
28 | {
29 | services.AddMemoryCache();
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Cache/CacheSettings.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.Extensions.Cache
2 | {
3 | public class CacheSettings
4 | {
5 | public bool Enabled { get; set; }
6 |
7 | public bool UseRedis { get; set; }
8 |
9 | public bool UseLocker { get; set; }
10 |
11 | public string Host { get; set; }
12 |
13 | public int TimeoutInMs { get; set; }
14 |
15 | public int Port { get; set; }
16 |
17 | public bool Ssl { get; set; }
18 |
19 | public string Password { get; set; }
20 |
21 | public int LockerDb { get; set; }
22 |
23 | public string LockerPrefix { get; set; }
24 |
25 | public int LockerTtlInSeconds { get; set; }
26 |
27 | public int CacheDb { get; set; }
28 |
29 | public string CachePrefix { get; set; }
30 |
31 | public int CacheTtlInSeconds { get; set; }
32 |
33 | public string GetCacheConnectionString()
34 | {
35 | return $"{this.Host}:{this.Port},ssl={this.Ssl.ToString().ToLower()},password={this.Password},defaultDatabase={this.CacheDb},syncTimeout={this.TimeoutInMs},connectTimeout={this.TimeoutInMs},abortConnect=false";
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Cache/LockerFactory.cs:
--------------------------------------------------------------------------------
1 | using RedLockNet;
2 | using RedLockNet.SERedis;
3 | using RedLockNet.SERedis.Configuration;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Net;
7 | using System.Threading.Tasks;
8 |
9 | namespace AspNetScaffolding.Extensions.Cache
10 | {
11 | public static class LockerFactory
12 | {
13 | private static readonly object Lock = new object();
14 |
15 | private static RedLockFactory RedLockFactory { get; set; }
16 |
17 | public static RedLockFactory Get(CacheSettings cacheSettings)
18 | {
19 | try
20 | {
21 | if (RedLockFactory == null)
22 | {
23 | lock (Lock)
24 | {
25 | if (RedLockFactory == null)
26 | {
27 | var endpoint = new RedLockEndPoint
28 | {
29 | EndPoint = new DnsEndPoint(cacheSettings.Host, cacheSettings.Port),
30 | Password = cacheSettings.Password,
31 | Ssl = cacheSettings.Ssl,
32 | RedisDatabase = cacheSettings.LockerDb,
33 | SyncTimeout = cacheSettings.TimeoutInMs,
34 | ConnectionTimeout = cacheSettings.TimeoutInMs,
35 |
36 | };
37 |
38 | var hosts = new List { endpoint };
39 | RedLockFactory = RedLockFactory.Create(hosts);
40 | }
41 | }
42 | }
43 | }
44 | catch (Exception)
45 | {
46 | CloseConnection();
47 | throw;
48 | }
49 |
50 | return RedLockFactory;
51 | }
52 |
53 | public static void CloseConnection()
54 | {
55 | lock (Lock)
56 | {
57 | RedLockFactory?.Dispose();
58 | RedLockFactory = null;
59 | }
60 | }
61 | }
62 |
63 | public interface ILocker
64 | {
65 | Task GetDistributedLockerAsync(string key, int? ttlInSeconds = null);
66 | }
67 |
68 | public class Locker : ILocker
69 | {
70 | private CacheSettings CacheSettings { get; set; }
71 |
72 | public Locker(CacheSettings cacheSettings)
73 | {
74 | this.CacheSettings = cacheSettings;
75 | }
76 |
77 | public async Task GetDistributedLockerAsync(string key, int? ttlInSeconds = null)
78 | {
79 | if (ttlInSeconds == null)
80 | {
81 | ttlInSeconds = this.CacheSettings.LockerTtlInSeconds;
82 | }
83 |
84 | var lockerFactory = LockerFactory.Get(this.CacheSettings);
85 |
86 | var fullKey = (this.CacheSettings?.LockerPrefix ?? "") + key;
87 | var ttl = TimeSpan.FromSeconds(ttlInSeconds.Value);
88 |
89 | return await lockerFactory.CreateLockAsync(fullKey, ttl);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Configuration/ConfigurationExtension.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace AspNetScaffolding.Extensions.Configuration
5 | {
6 | public static class ConfigurationExtension
7 | {
8 | public static void AddSingletonConfiguration(this IServiceCollection services, string section) where T : class, new()
9 | {
10 | T currentObject = new T();
11 | Api.ConfigurationRoot.GetSection(section).Bind(currentObject);
12 | services.AddSingleton(obj => currentObject);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Cors/CorsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 |
3 | namespace AspNetScaffolding.Extensions.Cors
4 | {
5 | public static class CorsMiddlewareExtension
6 | {
7 | public static void AllowCors(this IApplicationBuilder app)
8 | {
9 | app.UseCors(CorsServiceExtension.CorsName);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Cors/CorsService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace AspNetScaffolding.Extensions.Cors
4 | {
5 | public static class CorsServiceExtension
6 | {
7 | public const string CorsName = "EnableAll";
8 |
9 | public static void SetupAllowCors(this IServiceCollection services)
10 | {
11 | services.AddCors(o => o.AddPolicy(CorsName, builder =>
12 | {
13 | builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
14 | }));
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/CultureInfo/CultureInfoMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Localization;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace AspNetScaffolding.Extensions.CultureInfo
7 | {
8 | public static class CultureInfoMiddlewareExtension
9 | {
10 | public static void UseScaffoldingRequestLocalization(this IApplicationBuilder app, string[] acceptedsLanguages)
11 | {
12 | if (acceptedsLanguages?.Any() == true)
13 | {
14 | app.UseRequestLocalization(options =>
15 | {
16 | options.AddSupportedCultures(acceptedsLanguages);
17 | options.AddSupportedUICultures(acceptedsLanguages);
18 | options.SetDefaultCulture(acceptedsLanguages.FirstOrDefault());
19 | options.RequestCultureProviders = new List
20 | {
21 | new AcceptLanguageHeaderRequestCultureProvider { Options = options }
22 | };
23 | });
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Docs/DocsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 |
3 | namespace AspNetScaffolding.Extensions.Docs
4 | {
5 | public static class DocsMiddlewareExtension
6 | {
7 | public static void UseScaffoldingSwagger(this IApplicationBuilder app)
8 | {
9 | if (DocsServiceExtension.DocsSettings?.Enabled == true)
10 | {
11 | var title = DocsServiceExtension.DocsSettings?.Title ?? "API Reference";
12 |
13 | app.UseStaticFiles();
14 |
15 | app.UseSwagger(c =>
16 | {
17 | c.RouteTemplate = DocsServiceExtension.DocsSettings.SwaggerJsonTemplateUrl.TrimStart('/');
18 | });
19 |
20 | app.UseReDoc(c =>
21 | {
22 | c.RoutePrefix = DocsServiceExtension.DocsSettings.RedocUrl.TrimStart('/');
23 | c.SpecUrl = DocsServiceExtension.DocsSettings.SwaggerJsonUrl;
24 | c.DocumentTitle = title;
25 | });
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Docs/DocsSettings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AspNetScaffolding.Extensions.Docs
4 | {
5 | public class DocsSettings
6 | {
7 | public bool Enabled { get; set; }
8 |
9 | public string Title { get; set; }
10 |
11 | public string AuthorName { get; set; }
12 |
13 | public string AuthorEmail { get; set; }
14 |
15 | public string PathToReadme { get; set; }
16 |
17 | public string PathPrefix { get; set; }
18 |
19 | public string Version { get; set; }
20 |
21 | public string RedocUrl { get; set; }
22 |
23 | public string SwaggerJsonUrl { get; set; }
24 |
25 | public string SwaggerJsonTemplateUrl { get; set; }
26 |
27 | public List IgnoredEnums { get; set; }
28 |
29 | public IEnumerable GetDocsFinalRoutes()
30 | {
31 | return new List
32 | {
33 | SwaggerJsonUrl,
34 | RedocUrl
35 | };
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Docs/QueryAndPathCaseOperationFilter.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.JsonSerializer;
2 | using Microsoft.OpenApi.Models;
3 | using Swashbuckle.AspNetCore.SwaggerGen;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace AspNetScaffolding.Extensions.Docs
8 | {
9 | public class QueryAndPathCaseOperationFilter : IOperationFilter
10 | {
11 | public QueryAndPathCaseOperationFilter()
12 | {
13 | }
14 |
15 | public void Apply(OpenApiOperation operation, OperationFilterContext context)
16 | {
17 | if (operation.Parameters != null)
18 | {
19 | foreach (var param in operation.Parameters.Where(p => p.In == ParameterLocation.Query || p.In == ParameterLocation.Path))
20 | {
21 | param.Name = param.Name.GetValueConsideringCurrentCase();
22 | }
23 |
24 | var grouped = operation.Parameters
25 | .Where(p => p.In == ParameterLocation.Query || p.In == ParameterLocation.Path)
26 | .GroupBy(r => r.Name);
27 |
28 | var queryAndPath = grouped.Select(r => r.OrderBy(p => p.In).First()).ToList();
29 |
30 | operation.Parameters.ToList()
31 | .RemoveAll(p => p.In == ParameterLocation.Query || p.In == ParameterLocation.Path);
32 |
33 | operation.Parameters.ToList().AddRange(queryAndPath);
34 | }
35 |
36 | if (context.ApiDescription.ParameterDescriptions != null)
37 | {
38 | foreach (var param in context.ApiDescription.ParameterDescriptions)
39 | {
40 | param.Name = param.Name.GetValueConsideringCurrentCase();
41 | }
42 | }
43 |
44 | var path = context.ApiDescription.RelativePath;
45 | var matches = Regex.Matches(path, @"[{]{1}[\w\\_]*[}]{1}");
46 |
47 | foreach (var match in matches)
48 | {
49 | var oldValue = match.ToString();
50 | var newValue = "{" + oldValue.TrimStart('{').TrimEnd('}').GetValueConsideringCurrentCase() + "}";
51 | path = path.Replace(oldValue, newValue);
52 | }
53 |
54 | context.ApiDescription.RelativePath = path;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/ExceptionHandler/ExceptionHandlerMiddleware.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.JsonSerializer;
2 | using AspNetScaffolding.Models;
3 | using AspNetScaffolding.Utilities;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.AspNetCore.Http;
6 | using Newtonsoft.Json;
7 | using System;
8 | using System.Net;
9 | using System.Threading.Tasks;
10 | using WebApi.Models.Exceptions;
11 | using WebApi.Models.Helpers;
12 | using WebApi.Models.Response;
13 |
14 | namespace AspNetScaffolding.Extensions.ExceptionHandler
15 | {
16 | public class ExceptionHandlerMiddleware
17 | {
18 | private readonly RequestDelegate Next;
19 |
20 | private readonly bool IsDevelopment;
21 |
22 | public static Func ChangeErrorFormat;
23 |
24 | public ExceptionHandlerMiddleware(RequestDelegate next)
25 | {
26 | Next = next;
27 |
28 | IsDevelopment = EnvironmentUtility.IsDevelopment();
29 | }
30 |
31 | public async Task Invoke(HttpContext context)
32 | {
33 | try
34 | {
35 | await Next(context);
36 | }
37 | catch (Exception ex)
38 | {
39 | await HandleExceptionAsync(context, ex, IsDevelopment);
40 | }
41 | }
42 |
43 | private static Task HandleExceptionAsync(HttpContext context, Exception exception, bool isDevelopment)
44 | {
45 | try
46 | {
47 | context.Request.Body.Position = 0;
48 | }
49 | catch { }
50 |
51 | if (exception is ApiException)
52 | {
53 | return ApiException(context, (ApiException)exception);
54 | }
55 | else
56 | {
57 | return GenericError(context, exception, isDevelopment);
58 | }
59 | }
60 |
61 | private static Task GenericError(HttpContext context, Exception exception, bool isDevelopment)
62 | {
63 | context.Items.Add("Exception", exception);
64 |
65 | if (isDevelopment)
66 | {
67 | var exceptionContainer = new ExceptionContainer(exception);
68 | context.Response.WriteAsync(JsonConvert.SerializeObject(exceptionContainer, JsonSerializerService.JsonSerializerSettings)).Wait();
69 | context.Response.Body.Position = 0;
70 | }
71 |
72 | context.Response.ContentType = "application/json";
73 | context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
74 |
75 | return Task.CompletedTask;
76 | }
77 |
78 | private static Task ApiException(HttpContext context, ApiException exception)
79 | {
80 | var apiResponse = exception.ToApiResponse();
81 |
82 | var statusCode = (int)apiResponse.StatusCode;
83 |
84 | if (exception is PermanentRedirectException)
85 | {
86 | statusCode = 308;
87 | var location = $"{Api.ApiSettings.AppUrl.Trim('/')}{context.Request.Path}{context.Request.QueryString}";
88 |
89 | context.Response.Headers["Location"] = location;
90 | }
91 |
92 | context.Response.ContentType = "application/json";
93 | context.Response.StatusCode = statusCode;
94 |
95 | if (apiResponse.Content != null && ChangeErrorFormat == null)
96 | {
97 | context.Response.WriteAsync(JsonConvert.SerializeObject(apiResponse.Content, JsonSerializerService.JsonSerializerSettings)).Wait();
98 | context.Response.Body.Position = 0;
99 | }
100 | else if (ChangeErrorFormat != null)
101 | {
102 | var content = ChangeErrorFormat.Invoke(exception);
103 | context.Response.WriteAsync(JsonConvert.SerializeObject(content, JsonSerializerService.JsonSerializerSettings)).Wait();
104 | context.Response.Body.Position = 0;
105 | }
106 |
107 | return Task.CompletedTask;
108 | }
109 | }
110 |
111 | public static class ExceptionHandlerMiddlewareExtension
112 | {
113 | public static void UseScaffoldingExceptionHandler(this IApplicationBuilder app)
114 | {
115 | app.UseMiddleware();
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/GracefullShutdown/GracefullShutdownExtensions.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using WebApi.Models.Exceptions;
6 |
7 | namespace AspNetScaffolding.Extensions.GracefullShutdown
8 | {
9 | public static class GracefullShutdownExtensions
10 | {
11 | public static void ValidationShutdown(this ControllerBase controller)
12 | {
13 | if (Api.ShutdownSettings?.Enabled == false)
14 | {
15 | if (Api.ShutdownSettings.GracefullShutdownState.StopRequested && Api.ShutdownSettings.Redirect)
16 | {
17 | throw new PermanentRedirectException(null);
18 | }
19 | else if (Api.ShutdownSettings.GracefullShutdownState.StopRequested)
20 | {
21 | throw new ServiceUnavailableException("Service is unavailable for temporary maintenance");
22 | }
23 | }
24 | }
25 |
26 | public static IApplicationBuilder UseGracefullShutdown(this IApplicationBuilder builder)
27 | {
28 | if (Api.ShutdownSettings?.Enabled != true)
29 | {
30 | return builder;
31 | }
32 |
33 | return builder.UseMiddleware();
34 | }
35 |
36 | public static IServiceCollection AddGracefullShutdown(this IServiceCollection services)
37 | {
38 | services.AddSingleton(Api.ShutdownSettings.GracefullShutdownState);
39 | services.AddSingleton(Api.ShutdownSettings.GracefullShutdownState);
40 |
41 | return services;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/GracefullShutdown/GracefullShutdownMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Hosting;
3 | using Serilog;
4 | using Serilog.Context;
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace AspNetScaffolding.Extensions.GracefullShutdown
10 | {
11 | public class GracefullShutdownMiddleware
12 | {
13 | private readonly RequestDelegate Next;
14 |
15 | private readonly ShutdownSettings ShutdownSettings;
16 |
17 | private readonly GracefullShutdownState State;
18 |
19 | private DateTime ShutdownStarted;
20 |
21 | public GracefullShutdownMiddleware(
22 | RequestDelegate next,
23 | IHostApplicationLifetime applicationLifetime,
24 | GracefullShutdownState state
25 | )
26 | {
27 | if (applicationLifetime == null)
28 | {
29 | throw new ArgumentNullException(nameof(applicationLifetime));
30 | }
31 |
32 | Next = next ?? throw new ArgumentNullException(nameof(next));
33 | ShutdownSettings = Api.ShutdownSettings;
34 | State = state ?? throw new ArgumentNullException(nameof(state));
35 |
36 | applicationLifetime.ApplicationStopping.Register(OnApplicationStopping);
37 | applicationLifetime.ApplicationStopped.Register(OnApplicationStopped);
38 |
39 | EventHandler waitFinish = (sender, e) => OnApplicationStopped();
40 | AppDomain.CurrentDomain.ProcessExit += waitFinish;
41 | }
42 |
43 | public async Task Invoke(HttpContext context)
44 | {
45 | var ignoredRequest = State.StopRequested;
46 |
47 | if (!ignoredRequest)
48 | {
49 | State.NotifyRequestStarted();
50 | }
51 |
52 | try
53 | {
54 | await Next.Invoke(context);
55 | }
56 | finally
57 | {
58 | if (!ignoredRequest)
59 | {
60 | State.NotifyRequestFinished();
61 | }
62 | }
63 | }
64 |
65 | private void OnApplicationStopping()
66 | {
67 | ShutdownStarted = DateTime.UtcNow;
68 | State.NotifyStopRequested();
69 | }
70 |
71 | private void OnApplicationStopped()
72 | {
73 | var shutdownLimit = ShutdownStarted.Add(ShutdownSettings.ShutdownTimeoutTimeSpan);
74 |
75 | while (State.RequestsInProgress > 0 && DateTime.UtcNow < shutdownLimit)
76 | {
77 | LogInfo("Application stopping, requests in progress: {RequestsInProgress}", State.RequestsInProgress);
78 | Thread.Sleep(1000);
79 | }
80 |
81 | if (State.RequestsInProgress > 0)
82 | {
83 | LogError("Application stopped, requests in progress: {RequestsInProgress}", State.RequestsInProgress);
84 | }
85 | else
86 | {
87 | LogInfo("Application stopped, requests in progress: {RequestsInProgress}", State.RequestsInProgress);
88 | }
89 |
90 | Log.CloseAndFlush();
91 | }
92 |
93 | public void LogInfo(string message, long requestsInProgress)
94 | {
95 | LogContext.PushProperty("RequestsInProgress", requestsInProgress);
96 | LogContext.PushProperty("Environment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
97 |
98 | Log.Logger.Information(Api.LogSettings.TitlePrefix + " " + message);
99 | }
100 |
101 | public void LogError(string message, long requestsInProgress)
102 | {
103 | LogContext.PushProperty("RequestsInProgress", requestsInProgress);
104 | LogContext.PushProperty("Environment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
105 |
106 | Log.Logger.Error(Api.LogSettings.TitlePrefix + " " + message);
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/GracefullShutdown/GracefullShutdownState.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace AspNetScaffolding.Extensions.GracefullShutdown
4 | {
5 | public class GracefullShutdownState : IRequestsCountProvider
6 | {
7 | private long _requestsInProgress;
8 | public long RequestsInProgress => Volatile.Read(ref _requestsInProgress);
9 |
10 | private long _requestsProcessed;
11 | public long RequestsProcessed => Volatile.Read(ref _requestsProcessed);
12 |
13 | private bool _stopRequested;
14 | public bool StopRequested => Volatile.Read(ref _stopRequested);
15 |
16 | public void NotifyRequestStarted()
17 | {
18 | Interlocked.Increment(ref _requestsInProgress);
19 | }
20 |
21 | public void NotifyRequestFinished()
22 | {
23 | Interlocked.Decrement(ref _requestsInProgress);
24 | Interlocked.Increment(ref _requestsProcessed);
25 | }
26 |
27 | public void NotifyStopRequested()
28 | {
29 | Volatile.Write(ref _stopRequested, true);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/GracefullShutdown/IRequestsCountProvider.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.Extensions.GracefullShutdown
2 | {
3 | public interface IRequestsCountProvider
4 | {
5 | long RequestsInProgress { get; }
6 |
7 | long RequestsProcessed { get; }
8 |
9 | void NotifyRequestStarted();
10 |
11 | void NotifyRequestFinished();
12 |
13 | void NotifyStopRequested();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/GracefullShutdown/ShutdownSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AspNetScaffolding.Extensions.GracefullShutdown
4 | {
5 | public class ShutdownSettings
6 | {
7 | ///
8 | /// enables gracefull shutdown
9 | ///
10 | public bool Enabled { get; set; }
11 |
12 | ///
13 | /// false - incoming requests will get 500 code after shutdown initiation
14 | /// true - incoming requests will be redirected with 308 code, and same url
15 | ///
16 | public bool Redirect { get; set; }
17 |
18 | ///
19 | /// forces shutdown after X seconds
20 | ///
21 | public int ShutdownTimeoutInSeconds { get; set; } = 60;
22 |
23 | ///
24 | /// forces shutdown after X seconds - timespan format
25 | ///
26 | public TimeSpan ShutdownTimeoutTimeSpan => TimeSpan.FromSeconds(ShutdownTimeoutInSeconds);
27 |
28 | ///
29 | /// State for gracefull shutdown
30 | ///
31 | public GracefullShutdownState GracefullShutdownState { get; set; } = new GracefullShutdownState();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Healthcheck/HealthcheckMiddleware.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Models;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Diagnostics.HealthChecks;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Diagnostics.HealthChecks;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Linq;
8 | using System.Linq;
9 | using System.Threading.Tasks;
10 |
11 | namespace AspNetScaffolding.Extensions.Healthcheck
12 | {
13 | public static class HealthcheckkMiddlewareExtension
14 | {
15 | public static void UseHealthcheck(this IApplicationBuilder app)
16 | {
17 | if (HealthcheckServiceExtension.HealthcheckSettings?.Enabled == true)
18 | {
19 | var options = new HealthCheckOptions
20 | {
21 | AllowCachingResponses = true,
22 | ResponseWriter = WriteResponse
23 | };
24 |
25 | app.UseHealthChecks(
26 | GetFullPath(HealthcheckServiceExtension.ApiSettings, HealthcheckServiceExtension.HealthcheckSettings), options);
27 | }
28 | }
29 |
30 | private static Task WriteResponse(HttpContext httpContext, HealthReport result)
31 | {
32 | httpContext.Response.ContentType = "application/json";
33 |
34 | var json = new JObject(
35 | new JProperty("status", result.Status.ToString().ToLowerInvariant()),
36 | new JProperty("results", new JObject(result.Entries.Select(pair =>
37 | new JProperty(pair.Key, new JObject(
38 | new JProperty("status", pair.Value.Status.ToString().ToLowerInvariant()),
39 | new JProperty("description", pair.Value.Description),
40 | new JProperty("data", new JObject(pair.Value.Data.Select(
41 | p => new JProperty(p.Key, p.Value))))))))));
42 |
43 | return httpContext.Response.WriteAsync(
44 | json.ToString(Formatting.Indented));
45 | }
46 |
47 | public static string GetFullPath(ApiSettings apiSettings, HealthcheckSettings healthcheckSettings)
48 | {
49 | var basePath = apiSettings.GetPathPrefixConsideringVersion();
50 | basePath = ((string.IsNullOrWhiteSpace(basePath) == false) ? "/" + basePath.Trim('/') : "");
51 | var finalPathPart = healthcheckSettings.Path?.Trim('/');
52 |
53 | return (basePath ?? "") + "/" + (finalPathPart ?? "healthcheck");
54 | }
55 | }
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Healthcheck/HealthcheckService.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Models;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using System;
4 |
5 | namespace AspNetScaffolding.Extensions.Healthcheck
6 | {
7 | public static class HealthcheckServiceExtension
8 | {
9 | public static HealthcheckSettings HealthcheckSettings { get; set; }
10 |
11 | public static ApiSettings ApiSettings { get; set; }
12 |
13 | public static void SetupHealthcheck(
14 | this IServiceCollection services,
15 | ApiSettings apiSettings,
16 | HealthcheckSettings healthcheckSettings,
17 | Action builderFunction)
18 | {
19 | HealthcheckSettings = healthcheckSettings;
20 | ApiSettings = apiSettings;
21 |
22 | if (healthcheckSettings?.Enabled == true)
23 | {
24 | var builder = services.AddHealthChecks();
25 | builderFunction?.Invoke(builder, services.BuildServiceProvider());
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Healthcheck/HealthcheckSettings.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.Extensions.Healthcheck
2 | {
3 | public class HealthcheckSettings
4 | {
5 | public bool Enabled { get; set; }
6 |
7 | public string Path { get; set; }
8 |
9 | public bool LogEnabled { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/JsonFieldSelector/JsonFieldSelectorMiddleware.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.StreamExt;
2 | using JsonFieldSelector;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Http;
5 | using System;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace AspNetScaffolding.Extensions.JsonFieldSelector
11 | {
12 | public class JsonFieldSelectorMiddleware
13 | {
14 | private readonly RequestDelegate Next;
15 |
16 | private readonly string PropertyName;
17 |
18 | public JsonFieldSelectorMiddleware(RequestDelegate next)
19 | {
20 | this.Next = next;
21 | this.PropertyName = "fields";
22 | }
23 |
24 | public JsonFieldSelectorMiddleware(RequestDelegate next, string property)
25 | {
26 | if (string.IsNullOrWhiteSpace(property))
27 | {
28 | throw new ArgumentNullException(nameof(property));
29 | }
30 |
31 | this.Next = next;
32 | this.PropertyName = property.ToLowerInvariant();
33 | }
34 |
35 | public async Task Invoke(HttpContext context)
36 | {
37 | await this.Next(context);
38 |
39 | if (context.Request.Query.Any(r => r.Key.ToLowerInvariant() == this.PropertyName))
40 | {
41 | var json = context.Response.Body.AsString();
42 |
43 | if (string.IsNullOrWhiteSpace(json))
44 | {
45 | return;
46 | }
47 |
48 | try
49 | {
50 | var fields = context.Request.Query
51 | .FirstOrDefault(r => r.Key.ToLowerInvariant() == this.PropertyName)
52 | .Value.ToString();
53 |
54 | if (string.IsNullOrWhiteSpace(fields))
55 | {
56 | json = "{ }";
57 | }
58 | else
59 | {
60 | json = JsonFieldSelectorExtension.SelectFieldsFromString(json, fields);
61 | }
62 | }
63 | catch
64 | {
65 | json = "{ }";
66 | }
67 |
68 | var length = Encoding.UTF8.GetBytes(json ?? "").Length;
69 | context.Response.Body.SetLength(length);
70 | context.Response.ContentLength = length;
71 | await context.Response.WriteAsync(json);
72 | }
73 | }
74 | }
75 | }
76 |
77 | namespace AspNetScaffolding.Extensions.JsonFieldSelector
78 | {
79 | public static class JsonFieldSelectorMiddlewareExtension
80 | {
81 | public static void UseJsonFieldSelector(this IApplicationBuilder app, string property = "fields", bool enabled = true)
82 | {
83 | if (enabled)
84 | {
85 | app.UseMiddleware(property);
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/JsonSerializer/CaseUtility.cs:
--------------------------------------------------------------------------------
1 | using PackUtils;
2 |
3 | namespace AspNetScaffolding.Extensions.JsonSerializer
4 | {
5 | public static class CaseUtility
6 | {
7 | public static JsonSerializerEnum JsonSerializerMode { get; set; }
8 |
9 | public static string GetValueConsideringCurrentCase(this string value)
10 | {
11 | if (string.IsNullOrEmpty(value))
12 | {
13 | return string.Empty;
14 | }
15 |
16 | return value.ToCase(JsonSerializerMode.ToString());
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/JsonSerializer/JsonSerializerEnum.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetScaffolding.Extensions.JsonSerializer
2 | {
3 | public enum JsonSerializerEnum
4 | {
5 | Camelcase,
6 | Snakecase,
7 | Lowercase,
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/JsonSerializer/JsonSerializerService.cs:
--------------------------------------------------------------------------------
1 | using CQRS.Extensions;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Converters;
6 | using Newtonsoft.Json.Serialization;
7 | using PackUtils;
8 | using PackUtils.Converters;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 |
13 | namespace AspNetScaffolding.Extensions.JsonSerializer
14 | {
15 | public static class JsonSerializerService
16 | {
17 | public static JsonSerializerSettings JsonSerializerSettings { get; set; }
18 |
19 | public static Newtonsoft.Json.JsonSerializer JsonSerializer { get; set; }
20 |
21 | public static void ConfigureJsonSettings(
22 | this IMvcBuilder mvc,
23 | IServiceCollection services,
24 | JsonSerializerEnum jsonSerializerMode,
25 | string timezoneHeaderName,
26 | TimeZoneInfo defaultTimeZone)
27 | {
28 | CaseUtility.JsonSerializerMode = jsonSerializerMode;
29 |
30 | JsonSerializerSettings = null;
31 | JsonSerializer = null;
32 |
33 | EnumWithContractJsonConverter.IgnoreEnumCase = Api.ApiSettings.UseOriginalEnumValue;
34 |
35 | switch (jsonSerializerMode)
36 | {
37 | case JsonSerializerEnum.Camelcase:
38 | JsonSerializer = JsonUtility.CamelCaseJsonSerializer;
39 | JsonSerializerSettings = JsonUtility.CamelCaseJsonSerializerSettings;
40 | break;
41 | case JsonSerializerEnum.Lowercase:
42 | JsonSerializer = JsonUtility.LowerCaseJsonSerializer;
43 | JsonSerializerSettings = JsonUtility.LowerCaseJsonSerializerSettings;
44 | break;
45 | case JsonSerializerEnum.Snakecase:
46 | JsonSerializer = JsonUtility.SnakeCaseJsonSerializer;
47 | JsonSerializerSettings = JsonUtility.SnakeCaseJsonSerializerSettings;
48 | break;
49 | default:
50 | break;
51 | }
52 |
53 | // for fluent validation in cqrs extensions
54 | PropertyName.Resolver = (propertyName) =>
55 | {
56 | var parts = propertyName.Split('.');
57 | return string.Join(".", parts.Select(r => r.ToCase(jsonSerializerMode.ToString())));
58 | };
59 |
60 | JsonConvert.DefaultSettings = () => JsonSerializerSettings;
61 |
62 | services.AddScoped((provider) => JsonSerializer);
63 | services.AddScoped((provider) => JsonSerializerSettings);
64 |
65 | DateTimeConverter.DefaultTimeZone = defaultTimeZone;
66 | mvc.AddNewtonsoftJson(options =>
67 | {
68 | options.SerializerSettings.ContractResolver = JsonSerializerSettings.ContractResolver;
69 | options.SerializerSettings.NullValueHandling = JsonSerializerSettings.NullValueHandling;
70 | options.SerializerSettings.Converters.Add(new DateTimeConverter(() =>
71 | {
72 | var httpContextAccessor = services.BuildServiceProvider().GetService();
73 | return DateTimeConverter.GetTimeZoneByAspNetHeader(httpContextAccessor, timezoneHeaderName);
74 | }));
75 | foreach (var converter in JsonSerializerSettings.Converters)
76 | {
77 | options.SerializerSettings.Converters.Add(converter);
78 | }
79 | });
80 | }
81 |
82 | public static IList Clone(IList listToClone) where T : ICloneable
83 | {
84 | return listToClone.Select(item => (T)item.Clone()).ToList();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/Formatters/SnakeCaseJsonValueFormatter.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Serialization;
2 | using Serilog.Events;
3 | using Serilog.Formatting.Json;
4 | using System.IO;
5 |
6 | namespace AspNetScaffolding3.Extensions.Logger.Formatters
7 | {
8 | internal class SnakeCaseJsonValueFormatter : JsonValueFormatter
9 | {
10 | private readonly string _typeTagName;
11 | private readonly SnakeCaseNamingStrategy _strategy;
12 |
13 | public SnakeCaseJsonValueFormatter(SnakeCaseNamingStrategy snakeCaseStrategy, string typeTagName = "_typeTag") : base(typeTagName)
14 | {
15 | _typeTagName = typeTagName;
16 | _strategy = snakeCaseStrategy;
17 | }
18 |
19 | protected override bool VisitStructureValue(TextWriter state, StructureValue structure)
20 | {
21 | state.Write('{');
22 | string value = "";
23 | for (int i = 0; i < structure.Properties.Count; i++)
24 | {
25 | state.Write(value);
26 | value = ",";
27 | LogEventProperty logEventProperty = structure.Properties[i];
28 | WriteQuotedJsonString(_strategy.GetPropertyName(logEventProperty.Name, false), state);
29 | state.Write(':');
30 | Visit(state, logEventProperty.Value);
31 | }
32 |
33 | if (_typeTagName != null && structure.TypeTag != null)
34 | {
35 | state.Write(value);
36 | WriteQuotedJsonString(_typeTagName, state);
37 | state.Write(':');
38 | WriteQuotedJsonString(structure.TypeTag, state);
39 | }
40 |
41 | state.Write('}');
42 | return false;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/Formatters/SnakeCaseRenderedCompactJsonFormatter.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Serialization;
2 | using Serilog.Events;
3 | using Serilog.Formatting.Compact;
4 | using Serilog.Formatting.Json;
5 | using Serilog.Formatting;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System;
9 |
10 | namespace AspNetScaffolding3.Extensions.Logger.Formatters
11 | {
12 | internal class SnakeCaseRenderedCompactJsonFormatter : ITextFormatter
13 | {
14 | private readonly SnakeCaseJsonValueFormatter _valueFormatter;
15 | private static readonly SnakeCaseNamingStrategy _strategy = new SnakeCaseNamingStrategy();
16 |
17 | //
18 | // Summary:
19 | // Construct a Serilog.Formatting.Compact.CompactJsonFormatter, optionally supplying
20 | // a formatter for Serilog.Events.LogEventPropertyValues on the event.
21 | //
22 | // Parameters:
23 | // valueFormatter:
24 | // A value formatter, or null.
25 | public SnakeCaseRenderedCompactJsonFormatter(SnakeCaseJsonValueFormatter valueFormatter = null)
26 | {
27 | _valueFormatter = valueFormatter ?? new SnakeCaseJsonValueFormatter(_strategy, "$type");
28 | }
29 |
30 | //
31 | // Summary:
32 | // Format the log event into the output. Subsequent events will be newline-delimited.
33 | //
34 | //
35 | // Parameters:
36 | // logEvent:
37 | // The event to format.
38 | //
39 | // output:
40 | // The output.
41 | public void Format(LogEvent logEvent, TextWriter output)
42 | {
43 | FormatEvent(logEvent, output, _valueFormatter);
44 | output.WriteLine();
45 | }
46 |
47 | //
48 | // Summary:
49 | // Format the log event into the output.
50 | //
51 | // Parameters:
52 | // logEvent:
53 | // The event to format.
54 | //
55 | // output:
56 | // The output.
57 | //
58 | // valueFormatter:
59 | // A value formatter for Serilog.Events.LogEventPropertyValues on the event.
60 | public static void FormatEvent(LogEvent logEvent, TextWriter output, SnakeCaseJsonValueFormatter valueFormatter)
61 | {
62 | if (logEvent == null)
63 | {
64 | throw new ArgumentNullException("logEvent");
65 | }
66 |
67 | if (output == null)
68 | {
69 | throw new ArgumentNullException("output");
70 | }
71 |
72 | if (valueFormatter == null)
73 | {
74 | throw new ArgumentNullException("valueFormatter");
75 | }
76 |
77 | output.Write("{\"@t\":\"");
78 | output.Write(logEvent.Timestamp.UtcDateTime.ToString("O"));
79 | output.Write("\",\"@m\":");
80 | JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Render(logEvent.Properties), output);
81 | output.Write(",\"@i\":\"");
82 | output.Write(EventIdHash.Compute(logEvent.MessageTemplate.Text).ToString("x8"));
83 | output.Write('"');
84 | if (logEvent.Level != LogEventLevel.Information)
85 | {
86 | output.Write(",\"@l\":\"");
87 | output.Write(logEvent.Level);
88 | output.Write('"');
89 | }
90 |
91 | if (logEvent.Exception != null)
92 | {
93 | output.Write(",\"@x\":");
94 | JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output);
95 | }
96 |
97 | foreach (KeyValuePair property in logEvent.Properties)
98 | {
99 | string text = _strategy.GetPropertyName(property.Key, false);
100 | if (text.Length > 0 && text[0] == '@')
101 | {
102 | text = "@" + text;
103 | }
104 |
105 | output.Write(',');
106 | JsonValueFormatter.WriteQuotedJsonString(text, output);
107 | output.Write(':');
108 | valueFormatter.Format(property.Value, output);
109 | }
110 |
111 | output.Write('}');
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/ISimpleLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace AspNetScaffolding.Extensions.Logger
7 | {
8 | public interface ISimpleLogger
9 | {
10 | void Error(string controller, string operation, string message, Exception exception, object additionalData = null);
11 |
12 | void Warning(string controller, string operation, string message, Exception exception, object additionalData = null);
13 |
14 | void Fatal(string controller, string operation, string message, Exception exception, object additionalData = null);
15 |
16 | void Info(string controller, string operation, string message, object additionalData = null);
17 |
18 | void Verbose(string controller, string operation, string message, object additionalData = null);
19 |
20 | void Debug(string controller, string operation, string message, object additionalData = null);
21 |
22 | void Error(string message, Exception exception, object additionalData = null);
23 |
24 | void Warning(string message, Exception exception, object additionalData = null);
25 |
26 | void Fatal(string message, Exception exception, object additionalData = null);
27 |
28 | void Info(string message, object additionalData = null);
29 |
30 | void Verbose(string message, object additionalData = null);
31 |
32 | void Debug(string message, object additionalData = null);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/LoggerBuilderExtension.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.Logger;
2 |
3 | using AspNetScaffolding3.Extensions.Logger.Formatters;
4 |
5 | using Serilog;
6 | using Serilog.Builder;
7 | using Serilog.Formatting.Json;
8 |
9 | namespace AspNetScaffolding3.Extensions.Logger
10 | {
11 | public static class LoggerBuilderExtension
12 | {
13 | public static LoggerBuilder DisableConsoleIfConsoleSinkIsEnabled(this LoggerBuilder loggerBuilder, ScaffoldingConsoleOptions? consoleOptions = null)
14 | {
15 | if (consoleOptions?.Enabled ?? false)
16 | {
17 | loggerBuilder.DisableConsole();
18 | }
19 |
20 | return loggerBuilder;
21 | }
22 |
23 | public static LoggerConfiguration EnableStdOutput(this LoggerConfiguration loggerConfiguration, ScaffoldingConsoleOptions consoleOptions = null)
24 | {
25 | if (consoleOptions?.Enabled ?? false)
26 | {
27 | var minimmumLevel = consoleOptions.MinimumLevel ?? Serilog.Events.LogEventLevel.Verbose;
28 | return consoleOptions.FormatterType switch
29 | {
30 | EConsoleEnricherFormatter.SnakeCase => loggerConfiguration.WriteTo.Console(new SnakeCaseRenderedCompactJsonFormatter(), minimmumLevel).Enrich.FromLogContext(),
31 | _ => loggerConfiguration.WriteTo.Console(formatter: new JsonFormatter(), minimmumLevel).Enrich.FromLogContext(),
32 | };
33 | }
34 |
35 | return loggerConfiguration;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/LoggerService.cs:
--------------------------------------------------------------------------------
1 | using AspNetScaffolding.Extensions.AccountId;
2 | using AspNetScaffolding.Extensions.RequestKey;
3 | using AspNetScaffolding.Extensions.TimeElapsed;
4 | using AspNetScaffolding.Utilities;
5 | using AspNetSerilog;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Serilog;
8 | using Serilog.Builder;
9 | using System;
10 | using System.Collections.Generic;
11 | using AspNetScaffolding3.Extensions.Logger;
12 |
13 | namespace AspNetScaffolding.Extensions.Logger
14 | {
15 | public static class LoggerServiceExtension
16 | {
17 | public static void SetupSerilog(
18 | this IServiceCollection services,
19 | string domain,
20 | string application,
21 | LoggerSettings settings,
22 | List ignoredRoutes)
23 | {
24 | var loggerBuilder = new LoggerBuilder();
25 |
26 | if (settings?.NewRelicOptions?.Enabled == true && string.IsNullOrWhiteSpace(settings?.NewRelicOptions?.LicenseKey))
27 | {
28 | settings.NewRelicOptions.LicenseKey = Environment.GetEnvironmentVariable("NEW_RELIC_LICENSE_KEY");
29 | }
30 |
31 | Log.Logger = loggerBuilder
32 | .UseSuggestedSetting(domain, application)
33 | .SetupSeq(settings?.SeqOptions)
34 | .SetupSplunk(settings?.SplunkOptions)
35 | .SetupNewRelic(settings?.NewRelicOptions)
36 | .SetupDataDog(settings?.DataDogOptions)
37 | .SetupLapi(settings?.LapiOptions)
38 | .DisableConsoleIfConsoleSinkIsEnabled(settings?.ConsoleOptions)
39 | .BuildConfiguration()
40 | .EnableStdOutput(settings?.ConsoleOptions)
41 | .CreateLogger();
42 |
43 | if (settings?.DebugEnabled ?? false)
44 | {
45 | loggerBuilder.EnableDebug();
46 | }
47 |
48 | var config = new SerilogConfiguration
49 | {
50 | Version = Api.ApiSettings.BuildVersion,
51 | InformationTitle = settings?.TitlePrefix + settings?.GetInformationTitle(),
52 | ErrorTitle = settings?.TitlePrefix + settings?.GetErrorTitle(),
53 | BlacklistRequest = settings?.GetJsonBlacklistRequest(),
54 | BlacklistRequestPartial = settings?.JsonBlacklistRequestPartial,
55 | BlacklistResponse = settings?.JsonBlacklistResponse,
56 | BlacklistResponsePartial = settings?.JsonBlacklistResponsePartial,
57 | HeaderBlacklist = settings?.HeaderBlacklist,
58 | HttpContextBlacklist = settings?.HttpContextBlacklist,
59 | QueryStringBlacklist = settings?.QueryStringBlacklist,
60 | RequestKeyProperty = RequestKeyServiceExtension.RequestKeyHeaderName,
61 | AccountIdProperty = AccountIdServiceExtension.AccountIdHeaderName,
62 | TimeElapsedProperty = TimeElapsedServiceExtension.TimeElapsedHeaderName,
63 | IgnoredRoutes = ignoredRoutes
64 | };
65 |
66 | StaticSimpleLogger.UpdateVersion(Api.ApiSettings.BuildVersion);
67 | StaticSimpleLogger.UpdateEnvironment(EnvironmentUtility.GetCurrentEnvironment());
68 |
69 | if (settings.SetupSerilog is null)
70 | {
71 | services.SetupSerilog(config);
72 | }
73 | else
74 | {
75 | settings.SetupSerilog.Invoke(services, config);
76 | }
77 |
78 | services.AddScoped();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/LoggerSettings.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Builder.Models;
2 | using System;
3 | using System.Linq;
4 | using AspNetSerilog;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using System.Collections.Generic;
7 |
8 | namespace AspNetScaffolding.Extensions.Logger
9 | {
10 | public class LoggerSettings
11 | {
12 | public string TitlePrefix { get; set; }
13 |
14 | ///
15 | /// Previously JsonBlacklistRequest
16 | ///
17 | [Obsolete]
18 | public string[] JsonBlacklist { get; set; }
19 |
20 | public string[] GetJsonBlacklistRequest()
21 | {
22 | if (this.JsonBlacklistRequest?.Any() == true)
23 | {
24 | return this.JsonBlacklistRequest;
25 | }
26 |
27 | return this.JsonBlacklist;
28 | }
29 |
30 | public string[] JsonBlacklistRequest { get; set; }
31 |
32 | public Dictionary> JsonBlacklistRequestPartial { get; set; }
33 |
34 | public string[] JsonBlacklistResponse { get; set; }
35 |
36 | public Dictionary> JsonBlacklistResponsePartial { get; set; }
37 |
38 | public string[] HeaderBlacklist { get; set; }
39 |
40 | public string[] HttpContextBlacklist { get; set; }
41 |
42 | public string InformationTitle { get; set; }
43 |
44 | public string ErrorTitle { get; set; }
45 |
46 | public string[] QueryStringBlacklist { get; set; }
47 |
48 | public bool DebugEnabled { get; set; }
49 |
50 | public SeqOptions SeqOptions { get; set; } = new SeqOptions();
51 |
52 | public SplunkOptions SplunkOptions { get; set; } = new SplunkOptions();
53 |
54 | public NewRelicOptions NewRelicOptions { get; set; } = new NewRelicOptions();
55 |
56 | public DataDogOptions DataDogOptions { get; set; } = new DataDogOptions();
57 |
58 | public LapiOptions LapiOptions { get; set; } = new LapiOptions();
59 |
60 | public AspNetScaffolding.Extensions.Logger.ScaffoldingConsoleOptions ConsoleOptions { get; set; } = new AspNetScaffolding.Extensions.Logger.ScaffoldingConsoleOptions();
61 |
62 | public Action SetupSerilog { get; set; }
63 |
64 | public string[] IgnoredRoutes { get; set; }
65 |
66 | public string GetInformationTitle()
67 | {
68 | if (string.IsNullOrWhiteSpace(InformationTitle))
69 | return CommunicationLogger.DefaultInformationTitle;
70 |
71 | return InformationTitle;
72 | }
73 |
74 | public string GetErrorTitle()
75 | {
76 | if (string.IsNullOrWhiteSpace(ErrorTitle))
77 | return CommunicationLogger.DefaultErrorTitle;
78 |
79 | return ErrorTitle;
80 | }
81 | }
82 |
83 | public class ScaffoldingConsoleOptions : Serilog.Builder.Models.ConsoleOptions
84 | {
85 | public EConsoleEnricherFormatter FormatterType { get; set; } = EConsoleEnricherFormatter.Default;
86 | }
87 |
88 | public enum EConsoleEnricherFormatter
89 | {
90 | Default,
91 | SnakeCase
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Logger/SimpleLogger.cs:
--------------------------------------------------------------------------------
1 | using PackUtils;
2 | using Serilog;
3 | using Serilog.Context;
4 | using System;
5 | using System.Diagnostics;
6 | using RK = AspNetScaffolding.Extensions.RequestKey;
7 |
8 | namespace AspNetScaffolding.Extensions.Logger
9 | {
10 | public class SimpleLogger : ISimpleLogger
11 | {
12 | public RK.RequestKey RequestKey { get; set; }
13 |
14 | public SimpleLogger(RK.RequestKey requestKey)
15 | {
16 | this.RequestKey = requestKey;
17 | }
18 |
19 | public void Error(string controller, string operation, string message, Exception exception, object additionalData = null)
20 | {
21 | StaticSimpleLogger.Error(controller, operation, message, exception, this.RequestKey.Value, additionalData);
22 | }
23 |
24 | public void Warning(string controller, string operation, string message, Exception exception, object additionalData = null)
25 | {
26 | StaticSimpleLogger.Warning(controller, operation, message, exception, this.RequestKey.Value, additionalData);
27 | }
28 |
29 | public void Fatal(string controller, string operation, string message, Exception exception, object additionalData = null)
30 | {
31 | StaticSimpleLogger.Fatal(controller, operation, message, exception, this.RequestKey.Value, additionalData);
32 | }
33 |
34 | public void Info(string controller, string operation, string message, object additionalData = null)
35 | {
36 | StaticSimpleLogger.Info(controller, operation, message, this.RequestKey.Value, additionalData);
37 | }
38 |
39 | public void Verbose(string controller, string operation, string message, object additionalData = null)
40 | {
41 | StaticSimpleLogger.Verbose(controller, operation, message, this.RequestKey.Value, additionalData);
42 | }
43 |
44 | public void Debug(string controller, string operation, string message, object additionalData = null)
45 | {
46 | StaticSimpleLogger.Debug(controller, operation, message, this.RequestKey.Value, additionalData);
47 | }
48 |
49 | public void Debug(string message, object additionalData = null)
50 | {
51 | StaticSimpleLogger.Debug(message, this.RequestKey.Value, additionalData);
52 | }
53 |
54 | public void Info(string message, object additionalData = null)
55 | {
56 | StaticSimpleLogger.Info(message, this.RequestKey.Value, additionalData);
57 | }
58 |
59 | public void Verbose(string message, object additionalData = null)
60 | {
61 | StaticSimpleLogger.Verbose(message, this.RequestKey.Value, additionalData);
62 | }
63 |
64 | public void Warning(string message, Exception exception = null, object additionalData = null)
65 | {
66 | StaticSimpleLogger.Warning(message, exception, this.RequestKey.Value, additionalData);
67 | }
68 |
69 | public void Error(string message, Exception exception = null, object additionalData = null)
70 | {
71 | StaticSimpleLogger.Error(message, exception, this.RequestKey.Value, additionalData);
72 | }
73 |
74 | public void Fatal(string message, Exception exception = null, object additionalData = null)
75 | {
76 | StaticSimpleLogger.Fatal(message, exception, this.RequestKey.Value, additionalData);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/AspNetScaffolding3/Extensions/Mapper/MapperExtension.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using System.Collections.Generic;
3 |
4 | namespace AspNetScaffolding.Extensions.Mapper
5 | {
6 | public static class GlobalMapper
7 | {
8 | public static IMapper Mapper { get; set; }
9 |
10 | public static TDestination Map(this object source)
11 | where TDestination : class
12 | {
13 | return GlobalMapper.Mapper.Map(source);
14 | }
15 |
16 | public static TDestination Map(this TDestination destination, TSource source)
17 | where TSource : class
18 | where TDestination : class
19 | {
20 | return GlobalMapper.Mapper.Map(source, destination);
21 | }
22 |
23 | public static IEnumerable MapCollection(this IEnumerable