{"id":23818736,"url":"https://github.com/tugrulelmas/abiokaapi","last_synced_at":"2025-09-07T01:30:33.851Z","repository":{"id":68613580,"uuid":"64290390","full_name":"tugrulelmas/AbiokaApi","owner":"tugrulelmas","description":"Application framework","archived":false,"fork":false,"pushed_at":"2017-11-09T08:22:51.000Z","size":3465,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-06T09:28:46.896Z","etag":null,"topics":["angularjs","architecture","authenticationhandler","domain-driven-design","dynamic-handlers","infrastructure","material","netframework","nhibernate","repository","service-interceptors"],"latest_commit_sha":null,"homepage":"http://demo.abioka.com","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/tugrulelmas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2016-07-27T08:18:40.000Z","updated_at":"2024-08-14T16:19:42.000Z","dependencies_parsed_at":"2023-03-22T09:23:42.708Z","dependency_job_id":null,"html_url":"https://github.com/tugrulelmas/AbiokaApi","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tugrulelmas/AbiokaApi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tugrulelmas%2FAbiokaApi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tugrulelmas%2FAbiokaApi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tugrulelmas%2FAbiokaApi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tugrulelmas%2FAbiokaApi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tugrulelmas","download_url":"https://codeload.github.com/tugrulelmas/AbiokaApi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tugrulelmas%2FAbiokaApi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273986610,"owners_count":25202704,"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","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"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":["angularjs","architecture","authenticationhandler","domain-driven-design","dynamic-handlers","infrastructure","material","netframework","nhibernate","repository","service-interceptors"],"created_at":"2025-01-02T06:16:21.103Z","updated_at":"2025-09-07T01:30:33.174Z","avatar_url":"https://github.com/tugrulelmas.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build status](https://ci.appveyor.com/api/projects/status/0oiw9f5woi46fjyo?svg=true)](https://ci.appveyor.com/project/tugrulelmas/abiokaapi) [![Build Status](https://travis-ci.org/tugrulelmas/AbiokaApi.svg?branch=master)](https://travis-ci.org/tugrulelmas/AbiokaApi) [![abioka MyGet Build Status](https://www.myget.org/BuildSource/Badge/abioka?identifier=77345714-4b00-4c21-afa7-d58539087443)](https://www.myget.org/)\n\n# AbiokaApi\n\nThis is a boilerplate framework that helps building fast, robust, and modern web applications. S.O.L.I.D principles are applied in this project. It implements NLayer Architecture (Domain, Application, Infrastructure and UI Layers) and also provides a good infrastructure to implement best practices such as Aspect Oriented Programming, Dependency Injection.\n\n* [Installation](#installation)\n* [License](#license)\n* [Demo](#demo)\n\n### Covered Functionality\n- [Authentication](#authentication)\n- [Authorization](#authorization)\n- [OAuth 2.0](#oauth-20)\n- [Validation](#validation)\n- [Localization](#localization)\n- Inversion of Control\n- CRUD Operations\n- Repository Pattern\n- RESTful Services\n- [Single Page Application](#single-page-application)\n- [Aspect Oriented Programming](#aspect-oriented-programming)\n- Object Oriented Programming\n\n### Used Technologies\n- [Asp.Net Web Api 2](https://www.asp.net/web-api)\n- [SQL Server](https://www.microsoft.com/en-us/sql-server)\n- [FluentValidation](https://github.com/JeremySkinner/FluentValidation)\n- [Castle Windsor](https://github.com/castleproject/Windsor)\n- [NHibernate](http://nhibernate.info)\n- [Fluent NHibernate](https://github.com/jagregory/fluent-nhibernate)\n- [AngularJS](https://angularjs.org)\n- [Angular Material](https://material.angularjs.org)\n- [Material Design Data Table](https://github.com/daniel-nagy/md-data-table)\n- [Gulp](http://gulpjs.com)\n- [npm](https://www.npmjs.com)\n- [NUnit](https://www.nunit.org)\n- [Moq](https://github.com/moq/moq4)\n- [Unsplash](https://unsplash.com)\n\n## Aspect Oriented Programming\nThere are dynamic handlers and service interceptors for adding additional behavior to the RESTful or Application Service layer without modifying service codes.\n\n#### 1. Dynamic Handler\nIt aims to add behavior for RESTful Service (AbiokaApi.Host application). These behaviors can be logging Http Request and Response messages, checking authentication etc. If you want to do something for every request or response, you should use a dynamic handler. \n\n##### Usage\nWrite a class that implements IDynamicHandler interface.\n```csharp\npublic class NhUnitOfWorkHandler : IDynamicHandler\n{\n    private readonly IUnitOfWork unitOfWork;\n\n    public short Order =\u003e 10;\n\n    public NhUnitOfWorkHandler(IUnitOfWork unitOfWork) {\n        this.unitOfWork = unitOfWork;\n    }\n\n    public void BeforeSend(IRequestContext requestContext) {\n        if (!unitOfWork.IsInTransaction)\n        {\n            unitOfWork.BeginTransaction();\n        }\n    }\n\n    public void AfterSend(IResponseContext responseContext) {\n        if (unitOfWork.IsInTransaction)\n        {\n            unitOfWork.Commit();\n        }\n    }\n\n    public void OnException(IExceptionContext exceptionContext) {\n        if (unitOfWork.IsInTransaction)\n        {\n            unitOfWork.Rollback();\n        }\n    }\n}\n```\nRegister this class with IoC container. To learn additional information about Lifestyles please read [this](https://github.com/castleproject/Windsor/blob/master/docs/lifestyles.md).\n```csharp\n DependencyContainer.Container.Register\u003cIDynamicHandler, NhUnitOfWorkHandler\u003e(LifeStyle.PerWebRequest)\n```\nThere are following dynamic handlers:\n\n##### 1.1. AuthenticationHandler\nIt checks Http Request header to find Json Web Token and throws an exception, if there is no valid token and the action is not allowed for anonymous login.\n\n##### 1.2. ExceptionHandler\nIt catches every exception and wraps it and then returns Http Response with specific Status Code and additional Header value.\n\n##### 1.2. NhUnitOfWorkHandler\nIt opens a db transaction before calling service layer and commits this transaction after service layer response. If there is an exception, it rollbacks the transaction.\n\n#### 2. Service Interceptors\nIt aims to add behavior for the application services (AbiokaApi.ApplicationService application).\n\n##### 2.1 RoleValidationInterceptor\nif the current user hasn't the role which is necessary for the application service method, it throws an exception. \n\n```csharp\ninternal class RoleValidationInterceptor : IServiceInterceptor\n{\n    private readonly ICurrentContext currentContext;\n\n    public RoleValidationInterceptor(ICurrentContext currentContext) {\n        this.currentContext = currentContext;\n    }\n\n    public int Order =\u003e 0;\n\n    public void BeforeProceed(IInvocationContext context) {\n        var attributes = context.Method.GetCustomAttributes(typeof(AllowedRole), true);\n        if (attributes == null || attributes.Count() == 0)\n            return;\n\n        if(currentContext.Current.Principal.Roles == null)\n            throw new DenialException(\"AccessDenied\");\n\n\n        var allowedRoles = (AllowedRole)attributes.First();\n        if (currentContext.Current.Principal.Roles.Any(r =\u003e allowedRoles.Roles.Contains(r)))\n            return;\n\n        throw new DenialException(\"AccessDenied\");\n    }\n}\n```\n\n## Validation\nCreating a class that inherits CustomValidator\u003c`parameter type`\u003e is enough to validate the parameter type for every service method which has this parameter.\n\n**Example**\n\n```csharp\npublic interface IUserService : IReadService\u003cUser\u003e\n{\n    User Add(AddUserRequest request);\n}\n```\n\n```csharp\npublic class AddUserRequestValidator : CustomValidator\u003cAddUserRequest\u003e\n{\n    private readonly IUserSecurityRepository userSecurityRepository;\n\n    public AddUserRequestValidator(IUserSecurityRepository userSecurityRepository) {\n        this.userSecurityRepository = userSecurityRepository;\n\n        RuleFor(r =\u003e r.Email).NotEmpty().WithMessage(\"IsRequired\").EmailAddress().WithMessage(\"ShouldBeCorrectEmail\");\n        RuleFor(r =\u003e r.Password).NotEmpty().WithMessage(\"IsRequired\");\n    }\n\n    protected override void DataValidate(AddUserRequest instance, ActionType actionType) {\n        var tmpUser = userSecurityRepository.GetByEmail(instance.Email);\n        if (tmpUser != null)\n            throw new DenialException(\"UserIsAlreadyRegistered\", instance.Email);\n    }\n}\n```\n\n## Authentication\n\n[JWT](http://jwt.io) is used for Authentication. Once the user is logged in, then JWT is generated and returned as a response. Each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. You may want to look at [AuthenticationHandler](#11-authenticationhandler) \n\n**Allow Anonymous**\n\nAdding `AllowAnonymous` attributte to a controller method disables token validation for that method.\n\n```csharp\n[RoutePrefix(\"api/User\")]\npublic class UserController : BaseReadController\u003cUser\u003e\n{\n    private readonly IUserService userService;\n\n    public UserController(IUserService userService)\n        : base(userService) {\n        this.userService = userService;\n    }\n\n    [AllowAnonymous]\n    [HttpPost]\n    [Route(\"Login\")]\n    public HttpResponseMessage Login([FromBody]LoginRequest request) {\n        var token = userService.Login(request);\n\n        var response = Request.CreateResponse(HttpStatusCode.OK, token);\n        return response;\n    }\n}\n```\n## OAuth 2.0\n\nFacebook and Google accounts can also used for login.\n\n![Login](files/login.PNG)\n\n## Authorization\n\nOnly defining the roles with `AllowedRole` attributte for service method is sufficient for authorization. You may want to look at [RoleValidationInterceptor](#21-rolevalidationinterceptor)\n\n**Example**\n```csharp\npublic interface IUserService : IReadService\u003cUser\u003e\n{\n     [AllowedRole(\"Admin\", \"SuperUser\")]\n     void Update(User entiy);\n}\n```\n## Single Page Application\n\nAngularJS provides SPA template for UI.\n\n#### abioka-data-table\n\nIt's a compenent wraps [md-data-table](https://github.com/daniel-nagy/md-data-table). It uses [$resource](https://docs.angularjs.org/api/ngResource/service/$resource) object to interact with RESTful server-side data sources. \n\n**Example**\n\nWith load-data option:\n```html\n\u003cabioka-data-table options=\"vm.options\" load-data=\"vm.loadData\"\u003e\n\u003c/abioka-data-table\u003e\n```\n```javascript\n /* @ngInject */\n function UsersController($timeout, AdminResource) {\n     var vm = this;\n     vm.loadData = false;\n     vm.options = {\n         loadOnInit: false, // If you want to load data immediately, pass this as true.\n         rowSelection: false,\n         resource: AdminResource.users,\n         query: {},\n         columns: [{ name: \"Email\", text: \"Email\", order: true }],\n         dialogController: 'UserDialogController',\n         editTemplate: '/app/components/user/userDialog.html',\n         deleteTemplate: '/app/shared/deleteComponent/deleteComponent.html'\n     };\n\n     $timeout(function () {\n         vm.loadData = true;\n     }, 1000);\n }\n```\n\nWith cellTemplate option:\n```html\n\u003cabioka-data-table options=\"vm.options\"\u003e\n\u003c/abioka-data-table\u003e\n```\n```javascript\n/* @ngInject */\nfunction LoginAttemptsController($filter, AdminResource) {\n    var vm = this;\n\n    var resultTemplate = \"\u003cspan class='ab-label' ng-class=\\\"{'label-warning': entity.LoginResult === 'WrongPassword', 'label-success': entity.LoginResult === 'Successful'}\\\"\u003e{{entity.LoginResult | translate}}\u003c/span\u003e\";\n    var fulldateFormat = $filter(\"translate\")(\"FullDateFormat\");\n    var dateTemplate = \"\u003cspan\u003e{{entity.Date | abDate:'\" + fulldateFormat + \"'}}\u003c/span\u003e\";\n\n    vm.options = {\n        loadOnInit: true,\n        rowSelection: false,\n        isReadOnly: true,\n        resource: AdminResource.loginAttempts,\n        query: { order: '-Date' }, // default order by Date descending while loading data.\n        columns: [{ name: \"Date\", text: \"Date\", order: true, cellTemplate: dateTemplate },\n            { name: \"User.Email\", text: \"Email\" },\n        { name: \"LoginResult\", text: \"LoginResult\", cellTemplate: resultTemplate },\n        { name: \"IP\", text: \"IP\" }],\n    };\n}\n```\n\n### Localization\n\nLanguage resources are stored under [resources](AbiokaApi.Host/src/app/resources) folder and used with `translate` filter.\n\n**Example**\n\nresource_en.json:\n```json\n{\n  \"Password\": \"Password\",\n  \"PasswordAgain\": \"Password Again\",\n  \"IsAdmin\": \"Is Admin\",\n  \"LoginForm\": \"Login Form\",\n}\n```\n\nin html:\n```html\n\u003clabel\u003e{{'Password' | translate}}\u003c/label\u003e\n```\n\n## Demo\n\nYou can test this application at [demo.abioka.com](http://demo.abioka.com)\n\n```\nUser Name: demo@abioka.com\nPassword : demo\n```\n\n[![Abioka Demo](http://img.youtube.com/vi/UZl7nWQIbtw/0.jpg)](http://www.youtube.com/watch?v=UZl7nWQIbtw)\n\n## Installation\n\nClone this repository to your local machine.\n\n```\ngit clone https://github.com/tugrulelmas/AbiokaApi.git\n```\n\nRun the following codes:\n```\ncd AbiokaApi/AbiokaApi.Host\nnpm install\ngulp\n```\n\nChange connection string in [web.config](AbiokaApi.Host/Web.config)\n\nChange Facebook and Google's client and secret key's value in [web.config](AbiokaApi.Host/Web.config)\n\nOpen the [AbiokaApi.sln](AbiokaApi.sln) with Visual Studio and then run the project.\n\nOpen http://localhost:84 in your lovely browser.\n\n#### Installation via nuget\n\nInstall full code:\n\n```\nInstall-Package AbiokaApi.All\n```\n\nor install custom libraries:\n```\nInstall-Package AbiokaApi.Domain\nInstall-Package AbiokaApi.Repository\nInstall-Package AbiokaApi.ApplicationService\nInstall-Package AbiokaApi.Infrastructure.Common\nInstall-Package AbiokaApi.Infrastructure.Framework\nInstall-Package AbiokaApi.Host\n```\n\n## License\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftugrulelmas%2Fabiokaapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftugrulelmas%2Fabiokaapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftugrulelmas%2Fabiokaapi/lists"}