{"id":19846538,"url":"https://github.com/ssukhpinder/rest.api.features","last_synced_at":"2025-11-24T11:05:11.885Z","repository":{"id":227328852,"uuid":"771036432","full_name":"ssukhpinder/REST.API.Features","owner":"ssukhpinder","description":"Collection of REST API Features followed with best pratices","archived":false,"fork":false,"pushed_at":"2024-03-18T07:22:14.000Z","size":30,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-11T12:37:48.115Z","etag":null,"topics":["aspnetcore","beginner","coding","csharp","dotnet","dotnet-core","programming","rest","restful-api"],"latest_commit_sha":null,"homepage":"https://medium.com/c-sharp-progarmming","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ssukhpinder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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},"funding":{"github":"ssukhpinder","buy_me_a_coffee":"sukhpindersingh","custom":"https://medium.com/c-sharp-progarmming"}},"created_at":"2024-03-12T15:21:21.000Z","updated_at":"2024-03-26T18:06:18.000Z","dependencies_parsed_at":"2024-03-18T08:32:43.868Z","dependency_job_id":null,"html_url":"https://github.com/ssukhpinder/REST.API.Features","commit_stats":null,"previous_names":["ssukhpinder/rest.api.features"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssukhpinder%2FREST.API.Features","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssukhpinder%2FREST.API.Features/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssukhpinder%2FREST.API.Features/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssukhpinder%2FREST.API.Features/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssukhpinder","download_url":"https://codeload.github.com/ssukhpinder/REST.API.Features/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241225381,"owners_count":19930169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["aspnetcore","beginner","coding","csharp","dotnet","dotnet-core","programming","rest","restful-api"],"created_at":"2024-11-12T13:11:45.790Z","updated_at":"2025-11-24T11:05:11.858Z","avatar_url":"https://github.com/ssukhpinder.png","language":"C#","readme":"# REST.API.Features\n\n## 1. Common Response Model\nAll the API methods will adhere to the common response model structure. All features/controller methods defined in the above repository have followed the best practice.\n### Example\n```\n/// \u003csummary\u003e\n/// Common wrapper class for all API response models where T can be a simple or complex data type\n/// \u003c/summary\u003e\n/// \u003ctypeparam name=\"T\"\u003e\u003c/typeparam\u003e\npublic class ResponseMetaData\u003cT\u003e\n{\n    public HttpStatusCode Status { get; set; }\n    public string? Message { get; set; }\n    public bool IsError { get; set; }\n    public string? ErrorDetails { get; set; }\n    public string? CorrealtionId { get; set; }\n    public T? Result { get; set; }\n}\n```\n\n### 1.1 Return the model from the controller\n\n```\npublic IActionResult Get()\n{\n    var responseMetadata = new ResponseMetaData\u003cstring\u003e()\n    {\n        Status = System.Net.HttpStatusCode.OK,\n        IsError = false,\n        Message = CommonApiConstants.Responses.ResponseV1\n    };\n    return StatusCode((int)responseMetadata.Status, responseMetadata);\n}\n```\n\n## 2. Exception Handling\n### 2.1 Global Exception\nA global middleware that gets activated whenever an exception occurs within the application.\n- The middleware will also honour the common response model in case of exception\n- Finally, the JSON response will be returned.\n\n### Example\n```\npublic class GlobalExceptionHandler\n{\n    private readonly RequestDelegate _next;\n    private readonly ILogger\u003cGlobalExceptionHandler\u003e _logger;\n\n    public GlobalExceptionHandler(\n        RequestDelegate next,\n        ILogger\u003cGlobalExceptionHandler\u003e logger)\n    {\n        _next = next;\n        _logger = logger;\n    }\n\n    public async Task InvokeAsync(HttpContext context)\n    {\n        try\n        {\n            await _next(context);\n        }\n        catch (Exception exception)\n        {\n            ResponseMetaData\u003cstring\u003e responseMetadata = new()\n            {\n                Status = HttpStatusCode.InternalServerError,\n                IsError = true,\n                ErrorDetails = CommonExceptionConstants.SomeUnknownError\n            };\n\n            var serializedResponseMetadata = JsonConvert.SerializeObject(responseMetadata);\n            _logger.LogError(exception, \"Exception occurred: {Message}\", JsonConvert.SerializeObject(serializedResponseMetadata));\n\n            context.Response.StatusCode = StatusCodes.Status500InternalServerError;\n            context.Response.ContentType = \"application/json\";\n            await context.Response.WriteAsync(serializedResponseMetadata);\n        }\n    }\n\n}\n```\n\n### 2.2 Custom Exception\nThe first step is to create a CustomApiException as follows\n```\npublic class CustomApiException : Exception\n{\n    private readonly HttpStatusCode _statusCode;\n    private readonly string? _exceptionStrckTrace;\n\n    public HttpStatusCode StatusCode =\u003e _statusCode;\n    public string? ExceptionStackTrace =\u003e _exceptionStrckTrace;\n\n    public CustomApiException() { }\n    public CustomApiException(string message) : base(message) { }\n    public CustomApiException(string message, Exception ex) : base($\"{CommonExceptionConstants.CustomExceptionPrefix} {message}\", ex) { }\n    public CustomApiException(HttpStatusCode statusCode, string message) : base($\"{CommonExceptionConstants.CustomExceptionPrefix} {message}\")\n    {\n        _statusCode = statusCode;\n    }\n    public CustomApiException(HttpStatusCode statusCode, Exception ex) : base($\"{CommonExceptionConstants.CustomExceptionPrefix} {ex.Message}\")\n    {\n        _statusCode = statusCode;\n        _exceptionStrckTrace = ex.StackTrace;\n    }\n    public CustomApiException(HttpStatusCode statusCode, string message, Exception ex) : base($\"{CommonExceptionConstants.CustomExceptionPrefix} {message}\")\n    {\n        _statusCode = statusCode;\n        _exceptionStrckTrace = ex.StackTrace;\n    }\n}\n```\nThe CustomApiException class is a custom exception type that inherits from the base Exception class. It includes properties and constructors to handle and propagate exceptions in a customized manner within an API application. \n\nA custom middleware that gets activated whenever an exception or **Custom Exception** occurs within the application. \n\u003eNote: A custom exception middleware is beneficial in scenarios where we need to customize the custom status codes, message, stack trace, and other properties.\n- The middleware will also honour the common response model in case of any exception\n- Finally, the JSON response will be returned.\n\n### Example\n```\npublic class CustomExceptionHandler\n{\n    private readonly RequestDelegate _next;\n    private readonly ILogger\u003cCustomExceptionHandler\u003e _logger;\n\n    public CustomExceptionHandler(\n        RequestDelegate next,\n        ILogger\u003cCustomExceptionHandler\u003e logger)\n    {\n        _next = next;\n        _logger = logger;\n    }\n\n    public async Task InvokeAsync(HttpContext context)\n    {\n        try\n        {\n            await _next(context);\n        }\n        catch (Exception exception)\n        {\n\n            ResponseMetaData\u003cstring\u003e responseMetadata = new()\n            {\n                Status = HttpStatusCode.InternalServerError,\n                IsError = true,\n                ErrorDetails = CommonExceptionConstants.SomeUnknownError\n            };\n\n            if (exception is CustomApiException)\n            {\n                responseMetadata.ErrorDetails = CommonExceptionConstants.CustomSomeUnknownError;\n            }\n            else\n            {\n                responseMetadata.ErrorDetails = CommonExceptionConstants.SomeUnknownError;\n            }\n\n            var serializedResponseMetadata = JsonConvert.SerializeObject(responseMetadata);\n            _logger.LogError(exception, \"Exception occurred: {Message}\", JsonConvert.SerializeObject(serializedResponseMetadata));\n\n            context.Response.StatusCode = StatusCodes.Status500InternalServerError;\n            context.Response.ContentType = \"application/json\";\n            await context.Response.WriteAsync(serializedResponseMetadata);\n        }\n    }\n}\n```\n\n## 3. Versioning\nLet's start by installing two NuGet packages that we'll need to implement API versioning:\n\n- Asp.Versioning.Http\n- Asp.Versioning.Mvc.ApiExplorer\n\n### 3.1 Configuration\nNow that the API Versioning package has been installed onto the project, the next step is to configure it. The following code shows how you can configure API Versioning in ASP.NET Core:\n```\npublic static IServiceCollection UseAppVersioningHandler(this IServiceCollection services)\n{\n    #region Versioning \n\n    services.AddApiVersioning(options =\u003e\n    {\n        options.DefaultApiVersion = new ApiVersion(1);\n        options.ReportApiVersions = true;\n        options.AssumeDefaultVersionWhenUnspecified = true;\n        options.ApiVersionReader = ApiVersionReader.Combine(\n            new UrlSegmentApiVersionReader());\n\n        /* \n         * If you want to add both options i.e. URL versioning as well as header versioning.\n         * If attribute with version is supplied on controller then URL versioning will be applicable on Swqagger\n         * Else X-Api-Version will be added as a header attribute to the remaining controller methods\n         */\n\n        /* Uncomment this code block\n        options.ApiVersionReader = ApiVersionReader.Combine(\n            new UrlSegmentApiVersionReader(),\n            new HeaderApiVersionReader(\"X-Api-Version\"));\n        */\n    }).AddApiExplorer(options =\u003e\n    {\n        options.GroupNameFormat = \"'v'V\";\n        options.SubstituteApiVersionInUrl = true;\n    });\n\n    #endregion\n\n    return services;\n}\n```\n\n### 3.2 Registration\nThe above extension method will be used as follows in the Program.cs class\n```\n// Add versioning\nbuilder.Services.UseAppVersioningHandler();\n```\n\n### 3.3 Update Folder Structure\nThe Controller folder structure breakdown is as follows:\n- **REST.API.Features**\n  - **Controllers**\n    - **v1**\n      - VersionController\n    - **v2**\n      - VersionController\n\n### 3.4 Add Version Attributes\nFinally, the controller methods are structured with the following attributes:\n\n```\n/// \u003csummary\u003e\n/// Controller class contains API methods to demonstrate versioning \n/// \u003c/summary\u003e\n[ApiController]\n[ApiVersion(1)]\n[Route(\"api/v{v:apiVersion}/[controller]\")]\n```\n","funding_links":["https://github.com/sponsors/ssukhpinder","https://buymeacoffee.com/sukhpindersingh","https://medium.com/c-sharp-progarmming"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssukhpinder%2Frest.api.features","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssukhpinder%2Frest.api.features","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssukhpinder%2Frest.api.features/lists"}