HAL.AspNetCore
18.2.1
dotnet add package HAL.AspNetCore --version 18.2.1
NuGet\Install-Package HAL.AspNetCore -Version 18.2.1
<PackageReference Include="HAL.AspNetCore" Version="18.2.1" />
<PackageVersion Include="HAL.AspNetCore" Version="18.2.1" />
<PackageReference Include="HAL.AspNetCore" />
paket add HAL.AspNetCore --version 18.2.1
#r "nuget: HAL.AspNetCore, 18.2.1"
#:package HAL.AspNetCore@18.2.1
#addin nuget:?package=HAL.AspNetCore&version=18.2.1
#tool nuget:?package=HAL.AspNetCore&version=18.2.1
HAL
A .NET 9 library for implementing the Hypertext Application Language (HAL) and HAL-Forms in web APIs and clients.
Purpose
HAL is a standard for building discoverable, self-describing REST APIs using hypermedia links. This library helps you:
- Expose resources with links and forms for easy client navigation
- Support both standard and OData-based endpoints
- Integrate HAL in .NET and Angular applications
Use HAL if you want your API clients to discover available actions and related resources dynamically, following the HATEOAS principle.
Overview
This project provides:
- Core HAL resource and link models
- JSON serialization support
- ASP.NET Core integration for easy HAL responses
- OData support for list endpoints and paging
- .NET and Angular client libraries
Specifications
Packages
HAL.Common
which contains theResource
,Resource<T>
,FormsResource
,FormsResource<T>
andLink
implementations and the converters needed for serialization withSystem.Text.Json
.HAL.AspNetCore
addsIResourceFactory
,IFormFactory
andILinkFactory
which can be used in your controllers to easily generate resources from your models. It also comes with aHalControllerBase
class which can be used for all Controllers which return HAL.HAL.AspNetCore.OData
addsIODataResourceFactory
andIODataFormFactory
which can be used in your controllers to easily generate list endoints with paging from OData $filter, $skip and $top syntax.Hal.Client.Net
is a client library to consume HAL APIs in .Net applications. When using it, you should callapp.Services.AddHalClientFactoy()
to inject theIHalClientFactory
which can then be resolved in your application.Hal.Client.Angular
/@wertzui/ngx-hal-client
is a client library to consume HAL APIs in Angular applications. It exposes theHalClientModule
which then provides theHalClient
and aFormService
.
Installation
Add the desired package(s) via NuGet:
# Example for HAL.AspNetCore
dotnet add package HAL.AspNetCore
Angular Client Installation
To use the Angular HAL client, install the npm package:
npm install @wertzui/ngx-hal-client
Usage
Without OData
Use this approach for standard REST endpoints.
Configure services in Program.cs
to enable HAL serialization:
builder.Services
.AddControllers()
.AddHal()
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
});
Return HAL resources from your controller:
[Route("[controller]")]
public class MyController : HalControllerBase
{
private readonly IResourceFactory _resourceFactory;
public MyController(IResourceFactory resourceFactory)
{
_resourceFactory = resourceFactory ?? throw new ArgumentNullException(nameof(resourceFactory));
}
[HttpGet]
[ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Get))]
public ActionResult<IResource> GetList()
{
var models = new[]
{
new MyModelListDto {Id = 1, Name = "Test1"},
new MyModelListDto {Id = 2, Name = "Test2"},
};
var result = _resourceFactory.CreateForListEndpoint(models, _ => "items", m => m.Id);
return Ok(result);
}
[HttpGet("{id}")]
public ActionResult<IResource<ModelFullDto>> Get(int id)
{
var model = new ModelFullDto { Id = id, Name = $"Test{id}", Description = "Very important!" };
var result = _resourceFactory.CreateForGetEndpoint(model);
return Ok(result);
}
// PUT, POST, ...
}
With OData
Use this approach if your API supports OData queries for filtering, paging, and sorting.
Configure services in Program.cs
to enable OData and HAL:
builder.Services
.AddControllers(options => // or .AddMvc()
{
options.OutputFormatters.RemoveType<ODataOutputFormatter>();
options.InputFormatters.RemoveType<ODataInputFormatter>();
})
.AddOData()
.AddHALOData()
.AddJsonOptions(o => // not neccessary, but creates a much nicer output
{
o.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
});
var app = builder.Build();
app.UseRouting();
// ...
app.UseEndpoints(_ => { });
app.MapControllers();
Return HAL resources with OData support from your controller:
[Route("[controller]")]
public class MyController : HalControllerBase
{
private readonly IODataResourceFactory _resourceFactory;
public MyController(IODataResourceFactory resourceFactory)
{
_resourceFactory = resourceFactory ?? throw new ArgumentNullException(nameof(resourceFactory));
}
[HttpGet]
[ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Get))]
public ActionResult<Resource> GetList(
// The SwaggerIgnore attribute and all parameters beside the options are just here to give you a nice swagger experience.
// If you do not need that, you can remove everything except the options parameter.
// If you are using RESTworld, you can also remove everything except the options parameter, because there is a custom Swagger filter for that.
[SwaggerIgnore] ODataQueryOptions<TEntity> options,
[FromQuery(Name = "$filter")] string? filter = default,
[FromQuery(Name = "$orderby")] string? orderby = default,
[FromQuery(Name = "$top")] long? top = default,
[FromQuery(Name = "$skip")] long? skip = default)
{
var models = new[]
{
new MyModelListDto { Id = 1, Name = "Test1" },
new MyModelListDto { Id = 2, Name = "Test2" },
};
// Apply the OData filtering
models = options.Apply(models.AsQueryable()).Cast<MyModelListDto>().ToArray()
var result = _resourceFactory.CreateForOdataListEndpointUsingSkipTopPaging(models, _ => "items", m => m.Id, options);
return Ok(result);
}
// GET, PUT, POST, ...
}
When to use HAL
- You want your API to be self-describing and discoverable by clients
- You want to support hypermedia-driven navigation and forms
- You want to enable dynamic client behavior based on available links/actions
Contributing
Contributions are welcome! Please open issues or submit pull requests.
License
This project is licensed under the Unlicense.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Asp.Versioning.Abstractions (>= 8.1.0)
- HAL.Common (>= 8.2.0)
- Macross.Json.Extensions (>= 3.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on HAL.AspNetCore:
Package | Downloads |
---|---|
HAL.AspNetCore.OData
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
18.2.1 | 262 | 9/16/2025 |
18.2.0 | 216 | 6/19/2025 |
18.1.1 | 265 | 4/15/2025 |
18.1.0 | 253 | 3/17/2025 |
18.0.1 | 309 | 1/26/2025 |
18.0.0 | 345 | 11/20/2024 |
17.2.2 | 2,210 | 11/6/2024 |
17.2.1 | 533 | 10/11/2024 |
17.2.0 | 284 | 9/27/2024 |
17.1.1 | 483 | 7/18/2024 |
17.1.0 | 365 | 7/3/2024 |
17.0.0 | 161 | 7/3/2024 |
16.0.0 | 170 | 7/2/2024 |
15.0.0 | 557 | 6/4/2024 |
14.1.0 | 767 | 1/9/2024 |
14.0.0 | 223 | 12/22/2023 |
13.1.0 | 396 | 11/27/2023 |
13.0.0 | 287 | 11/14/2023 |
12.1.0 | 428 | 10/23/2023 |
12.0.0 | 193 | 10/23/2023 |
11.0.0 | 376 | 9/26/2023 |
10.4.1 | 273 | 9/5/2023 |
10.4.0 | 597 | 7/3/2023 |
10.3.0 | 304 | 6/28/2023 |
10.2.3 | 521 | 6/14/2023 |
10.2.2 | 460 | 5/25/2023 |
10.2.1 | 310 | 5/16/2023 |
10.2.0 | 366 | 5/10/2023 |
10.1.0 | 375 | 4/30/2023 |
10.0.1 | 261 | 4/27/2023 |
10.0.0 | 337 | 4/18/2023 |
9.1.2 | 382 | 3/11/2023 |
9.1.1 | 726 | 2/22/2023 |
9.1.0 | 426 | 2/9/2023 |
9.0.0 | 873 | 12/12/2022 |
9.0.0-pre | 191 | 11/21/2022 |
8.0.0 | 693 | 11/9/2022 |
7.4.1 | 1,159 | 10/20/2022 |
7.4.0 | 955 | 9/27/2022 |
7.3.0 | 2,252 | 6/7/2022 |
7.2.1 | 658 | 6/8/2022 |
7.2.0 | 1,577 | 4/1/2022 |
7.1.0 | 1,005 | 3/30/2022 |
7.0.4 | 3,320 | 3/7/2022 |
7.0.3 | 1,051 | 2/24/2022 |
7.0.2 | 1,678 | 2/21/2022 |
7.0.1 | 804 | 1/14/2022 |
7.0.0 | 1,140 | 12/3/2021 |
6.1.0 | 4,496 | 11/24/2021 |
6.0.2 | 1,564 | 10/12/2021 |
6.0.1 | 529 | 10/12/2021 |
6.0.0 | 727 | 10/6/2021 |
5.0.0 | 844 | 10/5/2021 |
4.0.0 | 1,256 | 9/7/2021 |
3.1.1 | 642 | 8/13/2021 |
3.1.0 | 1,065 | 6/28/2021 |
3.0.0 | 534 | 6/28/2021 |
2.0.2 | 678 | 5/10/2021 |
2.0.1 | 1,184 | 3/31/2021 |
2.0.0 | 606 | 3/24/2021 |
1.2.2 | 474 | 2/12/2021 |
1.2.1 | 549 | 1/28/2021 |
1.2.0 | 494 | 1/27/2021 |
1.1.1 | 478 | 1/26/2021 |
1.1.0 | 515 | 1/20/2021 |
1.0.1 | 482 | 1/19/2021 |
1.0.0 | 463 | 1/17/2021 |