{"id":16964651,"url":"https://github.com/sander1095/munisio","last_synced_at":"2025-04-11T23:02:09.118Z","repository":{"id":46508510,"uuid":"414300612","full_name":"sander1095/munisio","owner":"sander1095","description":"A modern HATEOAS library for .NET to reduce your client-side validation","archived":false,"fork":false,"pushed_at":"2023-10-29T13:52:31.000Z","size":55,"stargazers_count":11,"open_issues_count":14,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-06T05:19:49.973Z","etag":null,"topics":["asp-net-core","csharp","dotnet","hacktoberfest","hateoas","validation"],"latest_commit_sha":null,"homepage":"","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/sander1095.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}},"created_at":"2021-10-06T17:03:54.000Z","updated_at":"2024-10-28T09:55:07.000Z","dependencies_parsed_at":"2023-10-29T14:31:10.502Z","dependency_job_id":null,"html_url":"https://github.com/sander1095/munisio","commit_stats":null,"previous_names":["sander1095/munisio"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sander1095%2Fmunisio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sander1095%2Fmunisio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sander1095%2Fmunisio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sander1095%2Fmunisio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sander1095","download_url":"https://codeload.github.com/sander1095/munisio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248492875,"owners_count":21113162,"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":["asp-net-core","csharp","dotnet","hacktoberfest","hateoas","validation"],"created_at":"2024-10-13T23:44:01.958Z","updated_at":"2025-04-11T23:02:09.076Z","avatar_url":"https://github.com/sander1095.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Munisio\n\n## Overview\n\nThis is an ASP.NET Core HATEOAS library that enables you to easily implement HATEOAS principles in your ASP.NET Core Web API projects. HATEOAS is a constraint of the REST architectural style that allows clients to navigate a web API by following hyperlinks contained in the responses.\n\n✨ The inspiration for this project was to use HATEOAS to remove duplicate business logic from your front-end by simply checking for the existence of links of the actions you want to perform instead. If this sounds interesting to you, you can [read this blog post](https://stenbrinke.nl/blog/reducing-duplicate-code-in-our-applications-using-hateoas/) for more information.\n\n❌ Minimal API's [are not supported](https://github.com/ArcadyIT/munisio/issues/19) at the time of writing. Contributions are welcome!\n\n## Getting Started\n\nTo get started with this library, follow these simple steps:\n\n### 1. Installation \n\nInstall the package via NuGet Package Manager:\n\n**// TODO: This can only be done after setting up NuGet publishing (https://github.com/ArcadyIT/munisio/issues/3)**\n```\ndotnet add package Munisio\n```\n\n### 2. Configuration\n\nIn your `Startup.cs` or wherever you can configure your `IServiceCollection`, configure the HATEOAS service:\n\n```csharp\nusing Munisio;\n\npublic class Startup\n{\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // Add Munisio's Action Filter to your MVC pipeline\n        // so your DTO's can be filled with HATEOAS links\n        services.AddControllers(x =\u003e x.AddHateoas());\n        // ...\n    }\n}\n```\n\nNext, you need to tell Munisio where to find your HATEOAS providers. You can do this in 2 ways:\n\n```csharp\nusing Munisio;\n\npublic class Startup\n{\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // This is done in the previous step.\n        services.AddControllers(x =\u003e x.AddHateoas());\n    \n        // Option 1: Let Munisio search and register your providers automatically by searching the current assembly\n        // or pass the assemblies that contain your providers as an argument.\n        services.AddHateoasProviders(); \n    \n        // Option 2: Configure your providers yourself. Tweet is a DTO in this case.\n        services.AddTransient\u003cIHateoasProvider\u003cTweet\u003e, TweetHateoasProvider\u003e();\n    }\n}\n```\n\n### 3. Usage\n\nMunisio is now set up 🚀. Now you need to configure your models to support HATEOAS links.\n\nImagine we have the following DTO:\n\n```csharp\npublic class Tweet\n{\n    public int Id { get; set; }\n    public string Text { get; set; }\n    public int UserId { get; set; }\n    public int Retweets { get; set; }\n    public int Likes { get; set; }\n    public bool IsDeleted { get; set; }\n}\n```\n\nTo be able to return HATEOAS links, you'll need to inherit from the `Munisio.Models.HateoasObject` class or implement the `Munisio.Models.IHateoasObject` interface:\n\n\n```csharp\npublic class Tweet : HateoasObject\n{\n    public int Id { get; set; }\n    public string Text { get; set; }\n    public int UserId { get; set; }\n    public int Retweets { get; set; }\n    public int Likes { get; set; }\n    public bool IsDeleted { get; set; }\n\n    // HateoasObject contains the following:\n    // public ICollection\u003cHateoasLink\u003e Links { get; } = new List\u003cHateoasLink\u003e();\n\n    public bool CanBeDeleted()\n    {\n        // This is only an example.\n        // Normally you would only store this on your domain model and let it contain complex logic.\n        return true;\n    }\n}\n```\n\nYou'll now need to implement the `IHateoasProvider\u003cTDTOType\u003e` or `IAsyncHateoasProvider\u003cTDTOType` interface, depending on if you need async support to resolve your links or not. I recommend storing these in the same project as your Web Api.\n\n```csharp\npublic class TweetHateoasProvider : IAsyncHateoasProvider\u003cTweet\u003e\n{\n    public TweetHateoasProvider()\n    {\n        // Feel free to inject services you need to resolve links into the constructor of your providers!\n    }\n\n    public async Task EnrichAsync(IHateoasContext context, Tweet model)\n    {\n        // You can add links by specifying the URL to the related endpoint yourself.\n        // AddLink() adds a GET link.\n        // You can also use AddPatchLink(), AddDeleteLink(), AddPutLink(), AddPostLink() accordingly.\n        model.AddLink(\"getUser\", $\"api/users/{model.UserId}\");\n\n        // Or you can let ASP.NET Core resolve the link itself to avoid hardcoding URLs.\n        // \"GetUser\" would be the name of the action in a UsersController, for example.\n        model.AddLink(\"getTweets\", context.LinkGenerator.GetPathByName(context.HttpContext, \"GetTweets\", values: null)!);\n\n        // The \"context\" property contains ASP.NET Core's authorization service so you can add links only if a user is authorized to perform a specific action.\n        // Furthermore, there are multiple extension methods available after calling Add*Link() so you can have fine-grained control about when a link is added or not.\n        await model\n            .AddDeleteLink(\"delete\", $\"api/tweets/{model.Id}\")\n            .When(() =\u003e tweet.CanBeDeleted())\n            .WhenAsync(() =\u003e context.AuthorizeAsync(model, Operations.Delete)); // Note: Operations.Delete is custom!\n\n        model.AddPatchLink(\"retweet\", context.LinkGenerator.GetPathByName(context.HttpContext, \"Retweet\", values: null)!);\n    }\n}\n```\n\nIf you were to retrieve this tweet using `api/tweets/1`, the result can look like this:\n\n```json\n{\n  \"id\": 1,\n  \"text\": \"Just setting up my twttr.\",\n  \"userId\": 12,\n  \"retweets\": 178778,\n  \"likes\": 122462,\n  \"isDeleted\": false,\n  \"links\": [\n    {\n      \"rel\": \"getUser\",\n      \"href\": \"api/users/12\",\n      \"method\": \"GET\"\n    },\n    {\n      \"rel\": \"getTweets\",\n      \"href\": \"api/tweets\",\n      \"method\": \"GET\"\n    },\n    {\n      \"rel\": \"delete\",\n      \"href\": \"api/tweets/1\",\n      \"method\": \"DELETE\"\n    },\n    {\n      \"rel\": \"retweet\",\n      \"href\": \"api/tweets/1/retweet\",\n      \"method\": \"PATCH\"\n    }    \n  ]\n}\n```\n\nFinally:\n\n- I recommend taking a look at the `samples` folder, Intellisense in your IDE or at the `HateoasExtensions.cs` and `HateoasLinkBuilder.cs` for more options!\n- I recommend having 1 \"provider class\" per \"type\": `TweetHateoasProvider` could also implement `IHateoasProvider\u003c\u003e` or `IAsyncHateoasProvider\u003c\u003e` for other `Tweet` DTO types, like list models, etc..\n\n## Advanced features\n\nThis library contains some more advanced features.\n\n### Returning HATEOAS in lists\nThe samples provided in this Readme aren't very complex. More complex features can be found in the `samples/` folder. One of these \"advanced\" features is adding HATEOAS to a list entity and onto its children. An example:\n\n```json\n{\n  \"items\": [\n    {\n      \"id\": 1,\n      \"text\": \"Just setting up my twttr.\",\n      \"userId\": 12,\n      \"retweets\": 178778,\n      \"likes\": 122462,\n      \"isDeleted\": false,\n      \"links\": [\n        {\n          \"rel\": \"getUser\",\n          \"href\": \"api/users/12\",\n          \"method\": \"GET\"\n        },\n        {\n        \"rel\": \"getTweets\",\n        \"href\": \"api/tweets\",\n        \"method\": \"GET\"\n        },\n        {\n          \"rel\": \"delete\",\n          \"href\": \"api/tweets/1\",\n          \"method\": \"DELETE\"\n        },\n        {\n          \"rel\": \"retweet\",\n          \"href\": \"api/tweets/1/retweet\",\n          \"method\": \"PATCH\"\n        }\n      ]\n    }\n  ],\n  \"links\": [\n    {\n      \"rel\": \"addTweet\",\n      \"href\": \"api/tweets\",\n      \"method\": \"POST\"\n    }\n  ]\n}\n```\n\nIn order to do this, you'll need to return a `HateoasCollection\u003c\u003e` from your API:\n\n```csharp\n[HttpGet(Name = \"GetTweets\")]\npublic ActionResult\u003cHateoasCollection\u003cTweet\u003e\u003e GetTweets()\n{\n    var tweets = _database.GetTweets();\n    var mappedTweets = HateoasCollection.ForItems(tweets);\n    return Ok(mappedTweets);\n}\n```\n\nThe `Tweet` must also inherit from HateoasObject to make this work!\n\nNow, you can extend the previously described `TweetHateoasProvider` as follows to get the result mentioned above.\n\n```csharp\npublic class TweetHateoasProvider :\n    IAsyncHateoasProvider\u003cTweet\u003e,\n    IHateoasProvider\u003cHateoasCollection\u003cTweet\u003e\u003e\n{\n    // .... Existing code can be found above\n\n    // New code!\n    public void Enrich(IHateoasContext context, HateoasCollection\u003cTweet\u003e model)\n    {\n        // Note: You probably want to add some more advanced checks here.\n        // For example, \"is the user logged in?\"\n        // But for now we'll keep things simple!\n        model.AddPostLink(\"addTweet\", \"api/tweets\");\n    }\n}\n```\n\n### Storing DTO's in different assemblies\nSome projects do not store their DTO's in the same assembly as their Web API where the Controllers live. In this case, you should install `Munisio.Models` in those assemblies so you have access to types like `IHateoasObject`, `HateoasObject`, `HateoasCollection\u003c\u003e`, etc..\n\n\n**// TODO: This can only be done after setting up NuGet publishing (https://github.com/ArcadyIT/munisio/issues/3)**\n```\ndotnet add package Munisio.Models\n```\n\nThis way your DTO projects do not require a FrameworkReference for ASP.NET Core.\n\n## Sample\n\nFor a more detailed usage example, check out the provided sample project in the `samples` folder.\n\n## Contributions\n\nContributions are welcome! If you find a bug or have an idea for improvement, please open an issue or submit a pull request!\n\n## License\n\nThis library 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%2Fsander1095%2Fmunisio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsander1095%2Fmunisio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsander1095%2Fmunisio/lists"}