{"id":23109333,"url":"https://github.com/sangeethnandakumar/prowebapi","last_synced_at":"2026-05-18T02:31:09.688Z","repository":{"id":142980294,"uuid":"378888345","full_name":"sangeethnandakumar/ProWebAPI","owner":"sangeethnandakumar","description":"WebAPI with best standards template. Check ReadMe.md for implementations","archived":false,"fork":false,"pushed_at":"2021-08-23T13:13:17.000Z","size":127,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-02T11:48:37.580Z","etag":null,"topics":["asp","openapi","swagger","webapi"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sangeethnandakumar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-06-21T10:11:02.000Z","updated_at":"2021-08-23T13:13:20.000Z","dependencies_parsed_at":"2023-04-07T04:02:29.029Z","dependency_job_id":null,"html_url":"https://github.com/sangeethnandakumar/ProWebAPI","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sangeethnandakumar/ProWebAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sangeethnandakumar%2FProWebAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sangeethnandakumar%2FProWebAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sangeethnandakumar%2FProWebAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sangeethnandakumar%2FProWebAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sangeethnandakumar","download_url":"https://codeload.github.com/sangeethnandakumar/ProWebAPI/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sangeethnandakumar%2FProWebAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33162595,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T22:39:12.733Z","status":"online","status_checked_at":"2026-05-18T02:00:06.436Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp","openapi","swagger","webapi"],"created_at":"2024-12-17T01:35:46.568Z","updated_at":"2026-05-18T02:31:09.673Z","avatar_url":"https://github.com/sangeethnandakumar.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ProWebAPI\nASP.NET Core API with all proper standards\n\n### Standards Implemented\n| Name | Description\n| ------ | ------\n| Swagger | Implementation of Swagger OpenAPI standards\n| Standard Response | Implementation of a standard response pattern for DTO and internal\n| Versioning | Supports versioned endpoints using Microsoft MVC API Versioning\n| Environment Variable | Dynamic environment based appsettings and configurations\n| OData v4 | Support for endpoints with OData for easy client manipulation\n| Global Exception Handler | Enables standard error response for any 500 server errors\n| Response Casing | Pascal casing with Newtonsoft\n| OAuth v2 | Add support for OAuth from other repo (No imp here)\n| EF Core Repo structing | UnitOfWork implementation and auto disposal\n| Redis Cache | Implementing caching with Redis (In Prog..)\n\n# Swagger\nInstall Swagger\n```\nSwashbuckle.AspNetCore\n```\nSetup DI\n```csharp\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddControllers();\n            services.AddSwaggerGen(c =\u003e\n            {\n                c.SwaggerDoc(\"v1\", new OpenApiInfo { Title = \"ProWebAPI\", Version = \"v1\" });\n                c.ResolveConflictingActions(apiDescriptions =\u003e apiDescriptions.First());\n            });\n        }\n\n        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n        {\n            if (env.IsDevelopment())\n            {\n                app.UseDeveloperExceptionPage();\n                app.UseSwagger();\n                app.UseSwaggerUI(c =\u003e c.SwaggerEndpoint(\"/swagger/v1/swagger.json\", \"ProWebAPI v1\"));\n            }\n            app.UseHttpsRedirection();\n            app.UseRouting();\n            app.UseAuthorization();\n            app.UseEndpoints(endpoints =\u003e\n            {\n                endpoints.MapControllers();\n            });\n        }\n```\n\n# Standard Resoponse for API\nStandard response\n```csharp\nnamespace ProWebAPI.Modal\n{\n    public enum ResponseStatus\n    {\n        SUCCESS,\n        WARNING,\n        FAILED\n    }\n\n    public enum ErrorCodes\n    {\n        //RESERVED\n        ERR01, // =\u003e Invalid request validation (400)\n\n        ERR02, // =\u003e Unsupported OData Query (400)\n        ERR03, // =\u003e Unhandled exception on server (500)\n\n        //CUSTOM CODES\n        ERR04\n    }\n\n    public class Response\n    {\n        protected ResponseStatus ResponseStatus { get; set; }\n        public string Status { get; set; }\n    }\n\n    public class SuccessResponse\u003cT\u003e : Response\n    {\n        public T Data { get; set; }\n\n        public SuccessResponse()\n        {\n            ResponseStatus = ResponseStatus.SUCCESS;\n            Status = ResponseStatus.SUCCESS.ToString();\n        }\n    }\n\n    public class ErrorResponse : Response\n    {\n        public string ErrorCode { get; set; }\n        public string Message { get; set; }\n        public List\u003cstring\u003e Info { get; set; }\n\n        public ErrorResponse()\n        {\n            ResponseStatus = ResponseStatus.FAILED;\n            Status = ResponseStatus.FAILED.ToString();\n            ErrorCode = ErrorCodes.ERR01.ToString();\n            Info = new List\u003cstring\u003e();\n        }\n    }\n\n    public class Dto\u003cT\u003e\n    {\n        public bool IsSuccess { get; set; }\n        public string Error { get; set; }\n        public T Data { get; set; }\n    }\n}\n```\n\n# Versioning\nInstall Nuget library\n```nuget\n    Microsoft.AspNetCore.Mvc.Versioning\n```\nAdd to dependency container\n```csharp\n   services.AddApiVersioning(options =\u003e\n            {\n                options.AssumeDefaultVersionWhenUnspecified = true;\n                options.DefaultApiVersion = new ApiVersion(1, 0);\n                options.ReportApiVersions = true;\n            });\n```\nDecorate the controllers base on the need\n```csharp\n    [ApiController]\n    [ApiVersion(\"1.0\")]\n    [ApiVersion(\"2.0\")]\n    [Route(\"api/v{version:apiVersion}/[controller]\")]\n    public class StudentsController : ControllerBase\n    {\n        [HttpGet]\n        [MapToApiVersion(\"1.0\")]\n        [Route(\"Success\")]\n        public Response Success()\n        {\n        }\n        \n        [HttpGet]\n        [MapToApiVersion(\"2.0\")]\n        [Route(\"Success\")]\n        public Response Success20()\n        {\n        }\n\n        [HttpGet]\n        [Route(\"Failure\")]\n        public Response Failure()\n        {\n        }\n               \n        [HttpPost]\n        [Route(\"Submit\")]\n        public Response Submit(User user)\n        {\n        }\n    }\n```\n\n# Request Validator\nInstall Nuget library\n```nuget\n    FluentValidation.AspNetCore\n```\nRegister validators on DI\n```csharp\n services.AddFluentValidation(x =\u003e x.RegisterValidatorsFromAssemblyContaining\u003cStartup\u003e());\n```\nOn the 'RequestDto' Namespace, Add the validators along with dto\n```csharp\nnamespace ProWebAPI.RequestDtos\n{\n    public class User\n    {\n        public string FirstName { get; set; }\n        public string LastName { get; set; }\n        public int Age { get; set; }\n    }\n\n    public class UserValidator : AbstractValidator\u003cUser\u003e\n    {\n        public UserValidator()\n        {\n            RuleFor(x =\u003e x.FirstName).NotNull().NotEmpty();\n            RuleFor(x =\u003e x.LastName).NotNull().NotEmpty();\n            RuleFor(x =\u003e x.Age).NotNull().GreaterThan(0).LessThan(150).NotEmpty();\n        }\n    }\n}\n```\nBuild a ValidationFilter\n```csharp\nnamespace ProWebAPI.Filters\n{\n    public class ValidationFilter : IAsyncActionFilter\n    {\n        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)\n        {\n            if (!context.ModelState.IsValid)\n            {\n                var errorResponse = new ErrorResponse\n                {\n                    ErrorCode = ErrorCodes.ERR01.ToString(),\n                    Message = \"Data submitted is not in correct format\",\n                    Status = ResponseStatus.WARNING.ToString()\n                };\n\n                var errorModalState = context.ModelState\n                    .Where(x =\u003e x.Value.Errors.Count \u003e 0)\n                    .ToDictionary(kv =\u003e kv.Key, kv =\u003e kv.Value.Errors.Select(x =\u003e x.ErrorMessage))\n                    .ToArray();\n\n                foreach (var error in errorModalState)\n                {\n                    foreach (var subError in error.Value)\n                    {\n                        errorResponse.Info.Add($\"{error.Key}: {subError}\");\n                    }\n                }\n\n                context.Result = new BadRequestObjectResult(errorResponse);\n                return;\n            }\n            await next();\n        }\n    }\n}\n```\nRegister the filter and turn off [ApiController] auto 400 Bad Request intercept\n```csharp\n services.AddControllers(options =\u003e\n            {\n                options.Filters.Add\u003cValidationFilter\u003e();\n            })\n             .ConfigureApiBehaviorOptions(options =\u003e\n            {\n                options.SuppressModelStateInvalidFilter = true;\n            });\n```\n\n# AppSettings.json\nConfigure appsettings for databases\n```json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft\": \"Warning\",\n      \"Microsoft.Hosting.Lifetime\": \"Information\"\n    }\n  },\n  \"DataConnections\": {\n    \"DatabaseOne\": {\n      \"IsStandardSecurity\": false,\n      \"Server\": \"DESKTOP//SQLSERVER\",\n      \"Catalogue\": \"MyDatabase\",\n      \"Username\": \"sangee\",\n      \"Password\": \"password@123\"\n    },\n    \"DatabaseTwo\": {\n      \"IsStandardSecurity\": false,\n      \"Server\": \"DESKTOP//SQLSERVER\",\n      \"Catalogue\": \"MyDatabase\",\n      \"Username\": \"sangee\",\n      \"Password\": \"password@123\"\n    },\n    \"DatabaseThree\": {\n      \"IsStandardSecurity\": false,\n      \"Server\": \"DESKTOP//SQLSERVER\",\n      \"Catalogue\": \"MyDatabase\",\n      \"Username\": \"sangee\",\n      \"Password\": \"password@123\"\n    }\n  }\n}\n```\nCreate 2 ENVIRONMENT configurations\n```\nappsettings.Development.json\nappsettings.Production.json\n```\n\n# OData v4\nTo support OData Install the NuGetpackages\n```\nMicrosoft.AspNetCore.Mvc.NewtonsoftJson\nMicrosoft.AspNetCore.OData\nOData.Swagger\n```\nAdd to service DI for OData\n```csharp\n   services.AddControllers().AddNewtonsoftJson();\n   ..\n   ...\n   ....\n   .....\n   services.AddOData();\n   services.AddOdataSwaggerSupport();\n```\nConfigure OData\n```csharp\n app.UseEndpoints(endpoints =\u003e\n            {\n                endpoints.MapControllers();\n                endpoints.EnableDependencyInjection();\n                endpoints.Select().Count().Filter().OrderBy().MaxTop(100).Expand();\n            });\n```\nOverride [EnableQuery] attribute with [EnableOData] to catch errors and return standard response\n```csharp\nnamespace ProWebAPI.Attributes\n{\n    public class EnableODataAttribute : EnableQueryAttribute\n    {\n        public override void ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)\n        {\n            try\n            {\n                base.ValidateQuery(request, queryOptions);\n            }\n            catch (ODataException ex)\n            {\n                var errorResponse = new ErrorResponse\n                {\n                    ErrorCode = ErrorCodes.ERR02.ToString(),\n                    Message = \"Unsupported OData query\",\n                    Info = new List\u003cstring\u003e() { ex.Message },\n                    Status = ResponseStatus.WARNING.ToString()\n                };\n\n                request.HttpContext.Response.ContentType = \"application/json\";\n                request.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;\n                request.HttpContext.Response.WriteAsJsonAsync(errorResponse);\n                return;\n            }\n            catch (Exception ex)\n            {\n                var errorResponse = new ErrorResponse\n                {\n                    ErrorCode = ErrorCodes.ERR03.ToString(),\n                    Message = \"Something went wrong\",\n                    Status = ResponseStatus.FAILED.ToString(),\n                    Info = new List\u003cstring\u003e() { \"An operation on the server resulted in failure\" }\n                };\n                request.HttpContext.Response.ContentType = \"application/json\";\n                request.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;\n                request.HttpContext.Response.WriteAsJsonAsync(errorResponse);\n                return;\n            }\n        }\n    }\n}\n```\nDecorate the Action Methord\n```csharp\n        [HttpGet]\n        [MapToApiVersion(\"2.0\")]\n        [Route(\"Success\")]\n        [EnableOData]\n        public IActionResult Success20()\n        {\n            var listOfData = new List\u003cStudent\u003e();\n            listOfData.Add(new Student { Name = \"Student A\", Age = 10 });\n            listOfData.Add(new Student { Name = \"Student B\", Age = 11 });\n            listOfData.Add(new Student { Name = \"Student C\", Age = 12 });\n            listOfData.Add(new Student { Name = \"Student D\", Age = 13 });\n            return Ok(new SuccessResponse\u003cList\u003cStudent\u003e\u003e\n            {\n                Data = listOfData\n            });\n        }\n```\n\nQuery by URL\n```url\nhttps://localhost:44351/api/v2/Students/Success?   $expand=data(  $filter= age gt 11; $orderby=Name desc; $select=Name  )\n```\n\n# Global Exception Handler\nCreate the Middleware\n```csharp\nnamespace ProWebAPI.Middlewares\n{\n    public class ExceptionMiddleware\n    {\n        private readonly RequestDelegate _next;\n\n        public ExceptionMiddleware(RequestDelegate next)\n        {\n            _next = next;\n        }\n\n        public async Task InvokeAsync(HttpContext context)\n        {\n            try\n            {\n                await _next(context);\n            }\n            catch (Exception)\n            {\n                //Write exception log here\n                await HandleExceptionAsync(context);\n            }\n        }\n\n        private Task HandleExceptionAsync(HttpContext context)\n        {\n            context.Response.ContentType = \"application/json\";\n            context.Response.StatusCode = StatusCodes.Status500InternalServerError;\n            var serverError = new ErrorResponse\n            {\n                ErrorCode = ErrorCodes.ERR03.ToString(),\n                Message = \"Something went wrong\",\n                Status = ResponseStatus.FAILED.ToString(),\n                Info = new List\u003cstring\u003e() { \"An operation on the server resulted in failure\" }\n            };\n            return context.Response.WriteAsJsonAsync(serverError);\n        }\n    }\n}\n```\nCreate an extension methord for app builder\n```csharp\nnamespace ProWebAPI.Extensions\n{\n    public static class ExceptionExtensions\n    {\n        public static void UseGlobalExceptionHandler(this IApplicationBuilder app)\n        {\n            app.UseMiddleware\u003cExceptionMiddleware\u003e();\n        }\n    }\n}\n```\nRegister the middleware\n```csharp\napp.UseGlobalExceptionHandler();\n```\n\n# Response Casing\nAllow member casing (which will be in pascal case for props) so that OData query result won't conflict with casing\n```csharp\n services.AddControllers()\n            .AddNewtonsoftJson(options =\u003e\n            {\n                options.UseMemberCasing();\n            })\n```\n\n# EF Core + UnitOfWork\nAdd supporting class libraries\n```\nProWeb.Entities\nProWeb.Service\nProWeb.Data\nProWeb.Commons\n```\nInstall the required packages on `ProWeb.Data`\n```\nMicrosoft.EntityFrameworkCore\nMicrosoft.EntityFrameworkCore.Design\nMicrosoft.EntityFrameworkCore.SqlServer\nMicrosoft.EntityFrameworkCore.Tools\n```\nTo run migrations\n`SET ProWeb.Data as [Startup Project] and run migrations`\n```\nAdd-Migration Test\nUpdate-Database\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsangeethnandakumar%2Fprowebapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsangeethnandakumar%2Fprowebapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsangeethnandakumar%2Fprowebapi/lists"}