
ASP.NET Core WebAPI – Clean Architecture
- by Mukesh Murugan
ASP.NET Core WebAPI – Clean Architecture is a Solution Template that is built with Loosely-Coupled and Inverted-Dependency/Onion Architecture along with other essential integrations.
Features
- Onion Architecture
- CQRS with MediatR Library
- Entity Framework Core – Code First
- Repository Pattern – Generic
- MediatR Pipeline Logging & Validation
- Serilog
- Swagger UI
- Response Wrappers
- Healthchecks
- Pagination
- Microsoft Identity with JWT Authentication
- Role based Authorization
- Database Seeding
- Custom Exception Handling Middleware.
- API Versioning
- Complete User Management Module (Register / Generate Token / Forgot Password / Confirmation Mail)
The Problem this Project Solves
Does it really make sense to Setup your ASP.NET Core Solution everytime you start a new WebApi Project ? Aren’t we wasting quite a lot of time in doing this over and over gain?
This is the exact Problem that I intend to solve with this Full-Fledged ASP.NET Core 3.1 WebApi Solution Template, that also follows various principles of Clean Architecture.
The primary goal is to create a Full-Fledged implementation, that is well documented along with the steps taken to build this Solution from Scratch. This Solution Template will also be available within Visual Studio 2019 (by installing the required Nuget Package / Extension).
- Demonstrate Clean Monolith Architecture in ASP.NET Core 3.1
- This is not a Proof of Concept
- Implementation that is ready for Production
- Integrate the most essential libraries and packages

ASP.NET Core 3.1 Web API
This is a Standalone ASP.NET Core WebAPI Solution with multiple Layers of Abstraction. The Library Projects are build on .NET Core 3.1 Libraries / .NET Standard 2.1 Libraries.
One-Click Install
This Project comes with a Visual Studio 2019 Solution Template that allows you to start new Projects using the Clean Architecture for WebAPI with ease. That’s a lot of time saved!
Open Source
This Project is licensed under MIT License, which means it is FREE to use , distribute. Feel free to contribute to this project as well. Let’s make this The Best Ever Template for ASP.NET Core 3.1 WebApi!
Everything is done for you.
Get the Complete Source Code of this Project here. Feel free to contribute as well. The Steps to get started are mentioned in the next section.
Why ASP.NET Core WebAPI – Clean Architecture?
While getting started with this Project, I could not find any Clean Architecture implementation that is specifically built for ASP.NET Core 3.1 WebAPI.
Starting with a Clean Architecture in quite Important for the lifetime of your applications. You really don’t want to re-engineer you solutions half-way down the development stages, do you?
Releases
Every Major Update will be tagged as versioned Release. Included Features and Changes will be made available within the Release Pages.
- v1.1 – Stable Release – Download ZIP | Extension from Visual Studio MarketPlace
- v1.0-preview – Download here
Prerequisites
- Make sure you are running on the latest .NET Core SDK (SDK 3.1 and above only). Get the latest one here.
- Visual Studio 2019 ( You can check out my Installation Guide of Visual Studio 2019 Community which is completely Free to use.) Make sure that ASP.NET and web development workload is installed.
- Install the latest DOTNET & EF CLI Tools by using this command – dotnet tool install –global dotnet-ef
- I Recommend that you read Onion Architecture In ASP.NET Core With CQRS – Detailed article to understand this implementation much better. This project is just an Advanced Version of the mentioned article.
- Once you clear with Onion Architecture Implementation, you will also need to be aware of CQRS MediatR Pattern. I have written a step-by-step guide on how to implement CQRS using MediatR Library. Read it here.
Getting Started
To get started / check out this Boiler Plate Template for Clean Architecture with ASP.NET Core WebApi, there are quite a few ways. Let’s go through them,
1. Using the Visual Studio Project Template
Download the Visual Studio Project Template from Visual Studio Marketplace from here – https://marketplace.visualstudio.com/items?itemName=MukeshMurugan.CleanArchitectureWebApi

Once Installed, Open Visual Studio and Create a New Project. You get to see our newly installed template. Use this Template to Create a new Project.

I created a new Project with the Name, “PointOfSales.Basic”. Here is the Solution Structure that Visual Studio sets up for you. It’s that easy.

After Visual Studio downloads the required packages, all you have to do is to open up appsettings.json, modify your connection string. With that done, Open up Package Manager Console and run the following commands.
That’s it. Now Build and Run your New Application!

2. Or, you can clone the Source Code
Navigate to the Project Repository – https://github.com/iammukeshm/CleanArchitecture.WebApi
Click on the Code -> Download ZIP, as mentioned below. This will download the entire source code for you locally to your machine (OR) Download the Latest Release from here.

Extract the Zip file and navigate to the Root of the WebAPI Project (Not the Solution File). In this directory, open up Power Shell and run the following commands.
Before proceeding do not forget to open up the appsettings.json and change the connection strings / email settings and so on. Note that you can use separation connection strings for both Identity and Application!
dotnet restore
dotnet ef database update -Context ApplicationDbContext
dotnet ef database update -Context IdentityContext
dotnet run (OR) Run the Solution using Visual Studio 2019
When you run the application, Swagger Opens up. Here you can find the available endpoint.
Folder Structure
Here is how the Solution Folder Looks like.

1. Core
The Core Layers will never depend on any other layer. Therefore what we do is that we create interfaces in the Application Layer and these interfaces get implemented in the external layers. This is also known and DIP or Dependency Inversion Principle.
It is also important to note that these layers are .NET Standard 2.1 Libraries. This is so because we need to make these layers as comptatible as possible with other projects. Let’s say tommorow your client wants a .NET 4.7 Solutions (quite unlikely though) with the near-same business logics. At such scenarios, .NET Standard Libraries will be of great help.
1.1 Domain
All the Entities and the most common models are available here. Note that this Layer will NEVER depend on anything else.
1.2 Application
Interfaces, CQRS Features, Exceptions, Behaviors are available here.
2. Infrastructure
Whenever there is a requirement to communicate with an external source, we implement it on the Infrastructure Layer. For example, Database or other Services will be included here. To make the separation more visible, We will maintain further sub projects with the naming convention as ‘Infrastructure.xxxxxx’ where xxxxxx is the actual Point of Concern.
2.1 Infrastructure.Identity
In this implementation, we will make use of the already AWESOME Microsoft Identity. Let’s seperate the User Managment Database from the Main Application Database. This is made possible by multiple – DbContext Classes in each of the required Infrastructure Project
2.2 Infrastructure.Persistence
An Application Specific Database will be maintained. This is to ensure that there is no relation between the DBContext classes of Application and the Identity.
2.3 Infrastructure.Shared
Now, there are some services that are common to the other Infrastructure Layers and has the possibility of use in nearly all the Infrastructure Layers. This includes Mail Service, Date Time Service and so on. Thus it is a better Idea to have a shared Infrastructure project as well.
3. WebApi
This is also known as the Presentation Layer, where you would put in the project that the user can interact with. In our case it is the WebAPI Project.
Support the Project
Your small contribution can help support the project and motivate me to make frequent updates to this Open Source Project. If you find this Project helpful or you learnt something from here, consider supporting.
Did you like this content? Found this article helpful? Consider Supporting by buying me a coffee.
Also, Leave a Star / Review on
Github – https://github.com/iammukeshm/CleanArchitecture.WebApi
Visual Studio Marketplace – https://marketplace.visualstudio.com/items?itemName=MukeshMurugan.CleanArchitectureWebApi
This would help me reach this project to the eyes of many more developers around the world! Thanks
Additional Resources
- Onion Architecture In ASP.NET Core With CQRS – Detailed – We will talk about Onion Architecture In ASP.NET Core and it’s advantages. We will also together build a WebApi that follows a variant of Onion Architecture so that we get to see why it is important to implement such an architecture in your upcoming projects.
- CQRS with MediatR in ASP.NET Core 3.1 – Ultimate Guide – Let’s talk about CQRS in ASP.NET Core 3.1 and it’s implementation along with MediatR and Entity Framework Core – Code First Approach. I will implement this pattern on a WebApi Project. The source code of this sample is linked at the end of the post. Of the several design patterns available, CQRS is one of the most commonly used patterns that helps architect the Solution to accommodate the Onion Architecture.
Credits
There are a lot of References that I had taken in-order to build up this Solution. Few of them are Jason Taylor, Steve Smith , Nick, and many more. The eShop Repository Series of Microsoft has helped quite a lot.
93 Comments
Many Thanks for sharing your valuable knowledge…
Glad that I could help! More to come.
Hi Mukesh,
Nice work to put all the pieces together. Thanks!
Question: How to get add-migration to work? I get the error message “Unable to create an object of type ‘ApplicationDbContext’. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728“
Hi Arthur,
First step, check what the issue is .
add-migration Added_something -verbose
Run this command on package manager console. It will pinpoint you to the issue. From there you can debug the issue.
However, i think this issue is because you have not set the default project properly.
1. Set API Project as the Staryup project.
2. Open up package manager console.
3. There you can see a small dropdown for default project. Choose the project where the specific context class lives.
Run this then,
add-migration migration_name -Context IdentityContext
Try this and let me know.
Thanks and regards
Very interesting, how will you make .net standard libs to work with .net framework 4.7 if 2.1 is not supported even by .net framework 4.8…
hi mukesh,
i really appreciate you. can you please work on multi tenant based architectures also. that would be so great. takecare
Hi,
Hope you liked this implementation. Will surely work on multi-tenancy as well.
Regards
Can you also add a new template for ASP.Net core Web Api Including these
Onion Architecture
CQRS with MediatR Library
Dapper (Instead of Entity Framework Core – Code First)
Dommel
Repository Pattern – Generic
MediatR Pipeline Logging & Validation
Serilog or Nlog
Swagger UI
Response Wrappers
Healthchecks
Pagination
Redis Caching
In-Memory Database
Microsoft Identity with JWT Authentication
Role based Authorization
Custom Exception Handling Middlewares
API Versioning
Fluent Validation
Fluent mapper
SMPT / Mailkit / Sendgrid Email Service
Complete User Management Module (Register / Generate Token / Forgot Password / Confirmation Mail)
Hi, Thanks for reaching out.
Yes, I am already working on adding Dapper to this Solution as new Infrastructure Layer, so that you easily switch the ORM whenever you want. Just working on the architecture part.
Dommel? Have not heard about. Will give it a look though. Will have to check out Fluent Mapper too.
NLOG/Serilog will be integrated soon.
Thanks,
Hope you liked the implementation.
Thanks Mukesh ,
I really loved the architecture that you have created.
The only point where i am stuck is using Dapper since i also have SQL stored procedure and for me to implement SP’s are from dapper.
Regards
Aarun
Hi, Thanks for your feedback.
You can easily add Dapper in the Infrastructure Layer, by adding a new Infrastructure Project named Infrastructure.Persistence.Dapper or something, and have your dapper implementations there. However, in the next version of this Architecture (v1.1) , I am planning to add this option as well, where users can switch between the ORM.
Regards
i would love to see ur approach on ORM swithching. tnx
Good Work Mukesk!
I’m expecting on Dapper implementation or Could you guide me by presenting an article for Implementing Dapper Identity at-least.
Thanks
Prabhakar
Hi, Thanks for the feedback.
Dapper with Identity can be a bit complicated. I will try to write an article on with along with complete source. Maybe that could help you out as well. Will keep you posted.
Regards
Hi, that is a very interesting architecture.
Just a quick question:
I always use the same context for the application and identity, so I can use a join when I want to get the user information that is linked in a entity.
How would that work in this scenario? I would have to do 2 queries?
Hi, yes.In cases where you actually need data from the Identity table, you would have to use both these contexts in your call. I guess it is like adding a bit more security and a cleaner separation. However I am still in the process of making this architecture simple and logical to use. What are your thoughts?
I think this may lead to some performance issues.
For example, if I want to get a list of products with the name of the user that created it I would have to get the list of products, and for each product do a query to get the name of the user.
I can think of two solutions:
– Put the ApplicationUser in the ApplicationDbContext as well, as it allows foreign keys for another schemas (at least in SQL Server)
– Or create another user table in the ApplicationDbContext containing only the necessary information (name, email, etc) so it does not expose all the user data to the application
What about using a store procedure to fetch the data accross databases? In that case we need to add the GUID of the user in AuditableBaseEntity to store it in the Products table and then join with that in the SP. It is a good practice?
Whaiting for The Project is currently at V2 😀
It’s almost done. The documentation is pending. Will release it by this weekend hopefully, along with the Solution Template which will be available on the Visual Studio Market Place 😀
Regards
Bump!
Only one question Mukesh, for the database seeding, do you know Bogus? Do you consider it as a good option to use with EF Core?
Thanks again.
Yes, Bogus is good option if you want to generate bulk sample data for testing purposes and seed it with efcore. But I think it might be an overkill in this scenario. The ultimate aim is to reduce the number of dependencies. When we can do the essential seeding in code, i really don’t see the need to use bogus. But this is quite a scenario specific requirement.
You are right Mukesh. For this reason I’m waiting your solution for seeding in code. It will be interesting to seed more than one related entities to see how to seed the relations between them.
Thanks again
Hi
Can you maybe make a simple projects for beginners.
Hi Carlo, You can find a simplified version of this project here at https://codewithmukesh.com/blog/onion-architecture-in-aspnet-core/
This will get you a real good starting point for clean architecture. Also it covers the approach in a step-by-step manner. Do have a look.
Thanks and Regards
Thank you very much for sharing this great project.
I installed the extension and create a new project and every thing is fine, but when i run the project as https://localhost:41112/swagger
it show this error “This site can’t be reached”
Did i missed some thing.
Again thank you very much
Hi, This usually happens when IIS faces issues. You can switch to kestrel and run the application. Here is a screenshot on how you can switch to Kestrel – https://codewithmukesh.com/wp-content/uploads/2020/07/kestrel.png . I hope this solves the issue.
Thanks and Regards
Hello Mukeshbhai,
I have used setup your application.
While running as Kestrel it getting exception without any details error.
The webpage at https://localhost:9001/
might be temporarily down or it may have moved permanently to a new web address.
Can you suggest to find real error logs behind this error?
Thanks and regards
Hi, as mentioned earlier, Kindly change the port settings of your WebAPI Application. This error usually occurs with a misconfigured IIS / Kestrel and used Ports. Try to change your ports from WebApi -> Properties -> launchsettings.json. Here change both application URL and SSL Port . That should fix it.
Regards
Hello Mukesh,
I changed the application URL and SSL port. Also I switched to Kestrel to run the application. Still, I am getting This site can’t be reached error.
Hi,
When i switch to kestrel that working.
When i public to IIS local that not working.
Issue:
WRN] Unable to bind to https://localhost:5001 on the IPv4 loopback interface: ‘An attempt was made to access a socket in a way forbidden by its access permissions.’.
2021-01-21 22:08:23.411 +07:00 [WRN] Unable to bind to https://localhost:5001 on the IPv6 loopback interface: ‘An attempt was made to access a socket in a way forbidden by its access permissions.’.
2021-01-21 22:08:23.414 +07:00 [FTL] Unable to start Kestrel.
System.IO.IOException: Failed to bind to address https://localhost:5001.
—> System.AggregateException: One or more errors occurred. (An attempt was made to access a socket in a way forbidden by its access permissions.) (An attempt was made to access a socket in a way forbidden by its access permissions.)
—> System.Net.Sockets.SocketException (10013): An attempt was made to access a socket in a way forbidden by its access permissions.
at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, String callerName)
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
Thank You, Mukesh, Just install extension – it’s working fine, Can you add 1 more entity like Product Category and return the product list with category name.
Hi, Sure, I will add that in the next release. But i guess it should be pretty straightforward to achieve it.
Regards
It works when i switch it to Kestrel.
Thank you very much.
Hi Mukesh, I just trying to consume Authenticate API. But the response comes correctly. but when I try to get in the AuthenticationResponse DTO class. that time data come to null. I think something is required on following line
var returnUser = JsonConvert.DeserializeObject(responseBody);
Can you please help – where I am wrong.
Full code:-
public async Task ValidateUser()
{
HttpClient = new HttpClient();
string serializedUser = JsonConvert.SerializeObject(user);
HttpRequestMessage httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Method = new HttpMethod(“POST”);
httpRequestMessage.RequestUri = new Uri(“https://localhost:9001/api/Account/authenticate”);
httpRequestMessage.Content = new StringContent(serializedUser);
httpRequestMessage.Content.Headers.ContentType
= new System.Net.Http.Headers.MediaTypeHeaderValue(“application/json”);
// var response=await HttpClient.SendAsync(httpRequestMessage);
HttpResponseMessage response = HttpClient.SendAsync(httpRequestMessage).Result;
var responseStatusCode = response.StatusCode;
var responseBody = response.Content.ReadAsStringAsync().Result;
// AuthenticationResponse authenticationResponse = new AuthenticationResponse();
if (responseStatusCode.ToString()==”OK”)
{
var returnUser = JsonConvert.DeserializeObject(responseBody);
((CustomeAuthenticationProvider)AuthenticationStateProvider)
.MarkUserAsAuthenticated(user.Email);
NavigationManager.NavigateTo(“/”);
await SessionStorage.SetItemAsync(“email”, user.Email);
//await SessionStorage.SetItemAsync(“token”, authenticationResponse.JWToken);
}
Response
{
“succeeded”: true,
“message”: “Authenticated xyz@gmail.com“,
“errors”: null,
“data”: {
“id”: “4f3cbcea-fcfc-42c0-940a-0e26f0323301”,
“userName”: “xyz@gmail.com”,
“email”: “xyz@gmail.com”,
“roles”: [
“Basic”
],
“isVerified”: true,
“jwToken”: “xxxx”
}
}
Hi, I do not understand it clearly. You tried to consume to authenticate endpoint and got the required response? I see the response looks fine too..
Can i know if you getting the required response on both ‘returnUser’ and ‘responseBody’ objects?
Hi Mikesh, awesome work. Learned a lot by using your code, since I am a beginner in C#.
Shouldn’t there be a parameter in the PagedResponse that tells, how many pages the response has? Otherwise it will be hard to implement a client.
Regards
Hi, Thanks for the coffee!
Yes, it was already to the TODO List. I was hesitant as it can be a performance killer, doing some research though. However, with regards to paging, I have a detailed guide at – https://codewithmukesh.com/blog/pagination-in-aspnet-core-webapi/ . Please go through it as well.
Regards
Hi Mukesh, thanks a lot for the hard work. I’m also just a beginner in C# that’s been learning by going through your code. I’m currently trying to deploy this to Heroku with ClearDB mySql but I also wanted to be able to keep using local sql server with microsoft management studio so I tried changing the usesqlserver code to usemysql using pomelo as a provider if there was a database url environment variable and parsing my ClearDB database url to connection string to use it. However, when I auto generate migrations after deploying to Heroku using exec commands in webapi.csproj with dotnet ef update database –context ApplicationDbContext and IdentityContext, it tells me that there is wrong SQL syntax. Generating it locally after deleting migrations folder also still has sql server in it. Is there a nice and clean way to do this or completely move to mysql to use remote db?
Hi, thank you for this. Is it correct to assume that the WebApi project can also be replaced with a normal MVC project?
Hi, Yes it can be. But to keep things clean , I am also working on another project that is essentially dedicated for Razor + WebAPI. You can check out the Github Repo probably – https://github.com/iammukeshm/CleanArchitecture.WebApplication It’s still under construction. Will be out by end of this month.
Regards.
Wondering will v2 be out soon?
Yes, I am planning. But do not have much of other features in the TODO list. Do you have any other suggestions. In a month or so , I am thinking to build another similar project but with AdminLTE + IS4 + MVC/Razor + Hangfire. Any Suggestion? Thanks
Do yo have any sample with a Web Project using Razor Pages and comsumig any Web API
Hi, you can maybe check this – https://github.com/iammukeshm/COVID19.Tracker
Thanks friend
Great
Thanks!
Hi, thanks for sharing, is there I need to config. call ILogger.loginformation(“error”) doesn’t log in file and console.
Hi, We are using Serilog for Logging. If you see the appsettings, I have by default configured only to log on to the console (Kestrel web Server Console.)
If you want to log it to additional targets like File, DB, please configure as needed.
I have a complete tutorial of Serilog here – https://codewithmukesh.com/blog/serilog-in-aspnet-core-3-1/
Thanks
In Email service;
public ILogger _logger { get; }
public EmailService(IOptions mailSettings,ILogger logger)
{
_mailSettings = mailSettings.Value;
_logger = logger;
_logger.LogInformation(“Test log”);
}
“Test log” doesn’t display in console or in file.
Hi, the usage of ILogger is incorrect. It’s always ILogger. In your case it should be ILogger . Just try it and let me know.
Regards
I’ve finally made it work, by removing Log.CloseAndFlush(); in Program.cs. Is this proper?
Hi Mukesh, Thanks for this great architecture. I noticed that due to the routing & versioning done from BaseApiController, developer cannot change the controller name in the route from the actual controller level.
Is there any way of doing so? Or I need to remove the BaseApiController to implement this?
For example, I want the UserProfileController to be called as “/api/v1/user-profile/” rather than being called as “/api/v1/UserProfile”.
If possible, please respond asap. 🙂
Hi, I had placed it in the BaseAPI Controller because you don’t have to worry about it everytime you create a new API. Yes, alternatively you can remove the inheritance from BaseAPIController and switch back to the default ControllerBase for your new APIs. This way you can have your own custom routes.
Regards
How to handle API response if user token expired ( unauthorised )?
If token expired then how it would handle? currently it throwing below error message.
Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. ValidTo: ‘System.DateTime’, Current time: ‘System.DateTime’.
at Microsoft.IdentityModel.Tokens.Validators.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateLifetime(Nullable`1 notBefore, Nullable`1 expires, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Share your thought.
Thank you.
Hello Mukesh, Thanks for the great article and extension. Is there an example of authorizing an endpoint based on the user roles. Also would the future updates include refresh token endpoint ?
Refresh tokens are already included in the application partly. But yeah, it will be included in the upcoming update for sure. About your first question, authorizing based on user role is pretty straight forward. Just have a [Authorize(Roles=”SuperAdmin”)] attribute over the required controller / action method.
Regards
Thanks in advance,
Hi Mukesh Can you tell how i use ApplicationUser entity id property under Infrastructure.Identity in Core.Domain enitty
Is this with Refresh Token
Refresh tokens will be implemented in the upcoming version
Hello Mukesh,
I learned a lot from this, thanks a lot.
I do have a query, I have installed the template but I’m not sure what I should do in order to create a new table? I’ve gone through the simpler tutorial but whenever I hit update database command, the model isn’t getting generated in the database.
Thanks!
Hi Mukesh,
I’m also just a beginner in C# so
Do you have any sample Web Project using this API for Authentication ?
Best regards
Hi Mukesh,
I just trying to consume Authenticate API. The response comes correctly. but when I always get @User.Identity.Name a= NULL.
Here is my code to get authentication from api, please help me – where I am wrong?
// In Http service to call api
public async Task<Response> AuthenticateAsync(AuthenticationRequest request, string ipAddress)
{
var json = JsonConvert.SerializeObject(request);
var httpContent = new StringContent(json, Encoding.UTF8, “application/json”);
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(_configuration[“BaseAddress”]);
var httpResponse = await client.PostAsync(“/api/Account/authenticate”, httpContent);
string jsonData = await httpResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response>(jsonData);
}
// In MVC Web app AccountController
public async Task Login(LoginViewModel model, string returnUrl = null)
{
……
var request = new AuthenticationRequest();
request.Email = model.Email;
request.Password = model.Password;
var result = await _accountService.AuthenticateAsync(request, null);
if (result.Succeeded)
{
var userPrincipal = this.ValidateToken(result.Data.JWToken);
var authProperties = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
IsPersistent = false
};
HttpContext.Session.SetString(SystemConstants.AppSettings.DefaultLanguageId, _configuration[SystemConstants.AppSettings.DefaultLanguageId]);
HttpContext.Session.SetString(SystemConstants.AppSettings.Token, result.Data.JWToken);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
userPrincipal,
authProperties);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError(string.Empty, result.Message);
return View(model);
}
}
private ClaimsPrincipal ValidateToken(string jwtToken)
{
IdentityModelEventSource.ShowPII = true;
SecurityToken validatedToken;
TokenValidationParameters validationParameters = new TokenValidationParameters();
validationParameters.ValidateLifetime = true;
validationParameters.ValidAudience = _configuration[“Tokens:Audience”];
validationParameters.ValidIssuer = _configuration[“Tokens:Issuer”];
validationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[“Tokens:Key”]));
ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out validatedToken);
return principal;
}
Hi Mukesh
What is the best approach to handle SQL transactions with CQRS ?
Hi Mukesh,
Great and easy to follow architecture layout. Just a quick question, If I want to add database project to the solution, which layer does it go to?
Wow! Hi Mukesh,
Great work man and I was having so much trouble especially with Role Management. Can I suggest the idea of developing a Blog project?
I have always wanted to do one just don’t know where to start
Hi Mukesh,
I just trying to consume Authenticate API. The response comes correctly. but when I try to get username in view by using @User.Identity.Name, data come to null.
Here is my code, please help – where I am wrong ?
// In Http service to call api
public async Task<Response> AuthenticateAsync(AuthenticationRequest request, string ipAddress)
{
var json = JsonConvert.SerializeObject(request);
var httpContent = new StringContent(json, Encoding.UTF8, “application/json”);
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(_configuration[“BaseAddress”]);
var httpResponse = await client.PostAsync(“/api/Account/authenticate”, httpContent);
string jsonData = await httpResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response>(jsonData);
}
// In MVC Web app AccountController
public async Task Login(LoginViewModel model, string returnUrl = null)
{
……
var request = new AuthenticationRequest();
request.Email = model.Email;
request.Password = model.Password;
var result = await _accountService.AuthenticateAsync(request, null);
if (result.Succeeded)
{
var userPrincipal = this.ValidateToken(result.Data.JWToken);
var authProperties = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
IsPersistent = false
};
HttpContext.Session.SetString(SystemConstants.AppSettings.DefaultLanguageId, _configuration[SystemConstants.AppSettings.DefaultLanguageId]);
HttpContext.Session.SetString(SystemConstants.AppSettings.Token, result.Data.JWToken);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
userPrincipal,
authProperties);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError(string.Empty, result.Message);
return View(model);
}
}
private ClaimsPrincipal ValidateToken(string jwtToken)
{
IdentityModelEventSource.ShowPII = true;
SecurityToken validatedToken;
TokenValidationParameters validationParameters = new TokenValidationParameters();
validationParameters.ValidateLifetime = true;
validationParameters.ValidAudience = _configuration[“Tokens:Audience”];
validationParameters.ValidIssuer = _configuration[“Tokens:Issuer”];
validationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[“Tokens:Key”]));
ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out validatedToken);
return principal;
}
Hi Mukesh,
I just trying to consume Authenticate API. But when I get ClaimsPrincipal for Cookie Authentication in MVC web, @User.Identity.Name is null,
Please help me . Below is my code:
// in AccountController
private ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
TokenValidationParameters validationParameters = new TokenValidationParameters();
validationParameters.ValidateLifetime = true;
validationParameters.ValidAudience = _configuration[“Tokens:Audience”];
validationParameters.ValidIssuer = _configuration[“Tokens:Issuer”];
validationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration[“Tokens:Key”]));
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal;
}
public async Task Login(LoginViewModel model, string returnUrl = null)
{
var request = new AuthenticationRequest();
request.Email = model.Email;
request.Password = model.Password;
var result = await _accountService.AuthenticateAsync(request, null);
if (result == null)
{
ModelState.AddModelError(“”, result.Message);
return View(model);
}
if (result.Succeeded)
{
_logger.LogInformation(“User logged in.”);
var userPrincipal = GetPrincipal(result.Data.JWToken);
var authProperties = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
IsPersistent = false
};
HttpContext.Session.SetString(SystemConstants.AppSettings.DefaultLanguageId, _configuration[SystemConstants.AppSettings.DefaultLanguageId]);
HttpContext.Session.SetString(SystemConstants.AppSettings.Token, result.Data.JWToken);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
userPrincipal,
authProperties);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError(string.Empty, result.Message);
return View(model);
}
}
// In AccountService
public async Task<Response> AuthenticateAsync(AuthenticationRequest request, string ipAddress)
{
var json = JsonConvert.SerializeObject(request);
var httpContent = new StringContent(json, Encoding.UTF8, “application/json”);
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(_configuration[“BaseAddress”]);
var httpResponse = await client.PostAsync(“/api/Account/authenticate”, httpContent);
string jsonData = await httpResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response>(jsonData);
}
Thank you very much for your valuable information sharing. It has become a source of information for me that I will constantly review. I have two requests from you. The first is to explain the microservice architecture in the same way in the project with Identity, cache, log, api gatway, docker, RabbitMQ technologies. My second request is that you never close your web page :). Best regards
Thanks for sharing such an awesome implementation. I truly support what you just did… but that issue we are having while trying to use IIS does not allow your project to be publish to a shared web hosting as http://www.smarterasp.net and i do not know how to solve that issue.
Do you have any experience doing a web deploy of your project? I do not know how to solve this.
Hi, What is the issue that you face? Can I get more details?
i am trying to run the API from swagger but what Autheticate Berear Key i have to put it gives me you are not Authorized
Thank you so much !!! I love you
You are welcome. A new version of this Repository is coming by December end 😀
Hi Mukesh,
Very impressive. We would like to use this template in project.
However we have to follow database first approach due to reason our DB team has made db tables and schema already.
Would you please share or guide how to use this with “Database First Approach”
regards
Jawad
Hi. Switching to DB first should be quite simple. Just generate your db classes and remove the ones already generated. Make sure of the naming of the context class as well.
Regards
Much thanks
Hola Mukesh
Por mas que modifico “applicationUrl”: “https://localhost:XXXX/” desde WebApi -> Propiedades -> launchsettings.json no se muestra la Api
Hi, Sorry for the delayed response. I had pinged you back in Linkedin as well. On my side, I don’t actually face any issue while opening with IIS. If you face any such issues, the ideal suspect is the clash of certain SSL ports. I am attaching a GIST of my launchsettings.json that works for me. I can confirm that with these settings, I am able to run the API via IIS.
https://gist.github.com/iammukeshm/45d1e3ea9caed25212d96d2ba6309cf6
Thanks
Hi Mukesh,
Thanks for this amazing template. I have 3 questions and was wondering if you can help.
1. After Create, Delete or Update we do a query of the whole database again to get the updated record.
Will there not be performance issues with a large database?
2. Can we just get the updated or created record?
3. Also do you have any plan of turning this into multi-tenant App in the future?
I have learnt a lot reading through your code
Kind REgards,
Zak
Thanks a lot Mukesh. It’s great project. Good on you.
I was just wondering If there is any plan to add unit tests as well?
Hi
I am using your structure. that is very good and usefull.
but i have problem, I add new service to “[].Infrastructure.Shared” project and use it in a Repository.
But when i use this repository in QueryHandler contructor, i faceup bellow error when call this query :
“Message”: “Error constructing handler for request of type MediatR.IRequestHandler`2[.Application.Features.MT4.Queries.GetEquity.GetEquityQuery,AspNetCoreHero.Results.Result`1[.Application.Features.MT4.Queries.GetEquity.GetEquityResponse]]. Register your handlers with the container. See the samples in GitHub for examples.”,
could you help me please.
Hi, I believe that you have not registered the Dependency into the containers. If that’s not the reason, please check if you have added services.AddMediatR(Assembly.GetExecutingAssembly()); to the Infrastructure.Shared Project. This line of code actually registers all the mediatr handlers that are available in Shared Project with the Service Container. I guess this should solve your issue. Do Let me know. Also, please feel free to leave a review of the Project here – https://marketplace.visualstudio.com/items?itemName=MukeshMurugan.CleanArchitectureWebApi. Much Appreciated. Thanks and Regards
This is very nice and clean Architecture.
Is OAuth included in this? How to add OAuth with JWToken in existing project?
https://jasonwatmore.com/post/2019/10/11/aspnet-core-3-jwt-authentication-tutorial-with-example-api
When I try running a template project which I have created I am getting “This site can’t be reached”. I have also tried cloning the repo but I got the same. I am new to web API development. Can you help me out to solve this?
I resolved the issue by notice one of the comment. When I try changing the SSL port to “44377” it got worked.
I referred to your reply it’s due to a clash of SSL port. For any reason is that to be of “44377” only?
https://gist.github.com/iammukeshm/45d1e3ea9caed25212d96d2ba6309cf6
Hi, to answer your question, I quote this from a Microsoft IIS Documentation ;
“If you want to test SSL access to your site, you can do this with IIS Express by using an SSL port between 44300 and 44399 and using the IIS Express self-signed certificate. Trying to use SSL with a port outside this range results in a URL binding failure when your website is launched under IIS Express.”
Thus, there is a limitation in the port you can use for https while running under IIS.This is mostly due to the certificate signature within the server. Hope it cleared a few things.
Regards
Great!! Got it. Mukesh. Thank you so much for the clear explanation.
This is the resource every developer that wants to be an architect is looking for