riskfirst / riskfirst.hateoas Goto Github PK
View Code? Open in Web Editor NEWPowerful HATEOAS functionality for .NET web api
License: MIT License
Powerful HATEOAS functionality for .NET web api
License: MIT License
Starting a new API with .NET 6, I've got an error when trying to migrate the database. It says that the entity type 'Link' requires a primary key or need to be defined as Keyless.
Scenerio:
[HttpGet("~/areas/location/{id:int}", Name = "Get Locations")] //Here Name attribute is same as below route also.
[HttpGet("~/location/{id:int}", Name = "Get Locations")] //route name aslo same ("Get Locations")
public async Task<IActionResult> Getlocation(int id)
{
_linksService.AddLinksAsync(locationmodel);
// Already we have
}
we are aware that , route Name attribute cannot be support same name with more than one route.
Expected:
[HttpGet("~/areas/location/{id:int}")] //-- Here We will remove that Name attribute and instead of that we will pass that text in another parameter as below stated.[HttpGet("~/location/{id:int}")]
public async Task<IActionResult> Getlocation(int id)
{
_linksService.AddLinksAsync(locationmodel,"Get Locations");
// Possible to pass Rel info (Or)
_linksService.AnotherExtensionMethod(locationmodel,"Get Locations");
// Possible to pass Rel info
}
Also, I have configured the Startup like below:
services.AddLinks(config =>
{
config.UseRelativeHrefs();
config.ConfigureRelTransformation(transform => transform.Add(ctx => $"{ctx.LinkSpec.RouteName}"));
config.AddPolicy<Location>("LocationsPolicy", policy =>
{
policy.RequireRoutedLink("users", "Get Locations", x => new { id = x.Id });
});
});
Output :
"_links": {
"users": {
"rel": "Get Locations",
"href": "/api/locations/1/users",
"method": "GET"
}
Problem: In this case i have to create indivitual policy for each action methods. N number of actions are there(with same name).
Can someone throw more light on how to resolve the issue?
Your help is much appreciated
Actual
Expected
References
Hi Folks, Is there any sample using ItemsLinkContainer with RequiresPagingLinks?
The DefaultRouteMap ctor does an Assembly scan per dependency. This is very slow.
I fixed it by adding
services.AddSingletone<IRouteMap, DefaultRouteMap>()
If it is intended to be transient, the assembly scan should be cached.
Its been 2 years since your last nuget publish but this repo is actively maintained. Are you going to publish sometime soon?
Actual
Expected
References
The doc says LinksContainer, but the class name is LinkContainer actually
Also Requires... should be changed to Require - i.e RequiresSelfLink should be RequireSelfLink, RequiresRoutedLink ->RequireRoutedLink, etc.
If you look at the examples in the readme, you will see that the link names (e.g. self
) are different from the link relations (the rel
tag).
{
id:1,
someOtherField: "foo",
_links : {
self: {
rel: "MyController\GetModelRoute",
href: "https://api.example.com/my/1"
method: "GET"
},
all: {
rel: "MyController\GetAllModelsRoute",
href: "https://api.example.com/my"
method: "GET"
},
delete: {
rel: "MyController\DeleteModelRoute",
href: "https://api.example.com/my/1"
method: "DELETE"
}
}
}
This works okay but doesn't adhere to the Hypertext Application Language (HAL) standard.
Quoting the Wikipedia article - Links have a target URI, as well as the name of the link (referred to as 'rel')
. However, the concept of a link name and its rel
are separate in the current implementation.
If we wanted to use XML instead of JSON (as mentioned in this issue) we don't know what approach to take.
In most examples (e.g. https://tools.ietf.org/html/draft-michaud-xml-hal-02#section-6) the value of the rel
tag is what we would have as a link name in the JSON format.
<resource rel="self" href="/orders">
<link rel="next" href="/orders?page=2"/>
<link rel="find" href="/orderse{/id}" templated="true"/>
</resource>
This is probably going to be a major change, bumping the current version to 4.0, but it needs to be decided.
In the README.MD is this example
[HttpGet(Name="GetAllModelsRoute")]
public async Task<IEnumerable<MyModel>> GetAllModels()
{
//... snip .. //
}
But how would you implement the AddLinksAsync
part?
As far as I can see ILinksService
doesn't accept an IEnumerable<MyModel>
as parameter for AddLinksAsync
...
Something like this would be nice:
[HttpGet(Name="GetAllModelsRoute")]
public async Task<IEnumerable<MyModel>> GetAllModels()
{
var all = await _dbContext.Models.ToListAsync();
await linksService.AddLinksAsync(all);
return all;
}
Given it appears @jamiecoh has moved on from RiskFirst and no nuget release has been made for the security issues, is it time ownership of this repo and package moves to the community?
A controller with 2 HttpMethod attributes
[HttpPut(), HttpPatch(Name = "PatchModelRoute")]
public async Task<SomeModel> Update(...)
{ ... }
Generates the following error
System.Reflection.AmbiguousMatchException: Multiple custom attributes of the same type found.
at System.Attribute.GetCustomAttribute(MemberInfo element, Type attributeType, Boolean inherit)
at System.Reflection.CustomAttributeExtensions.GetCustomAttribute[T](MemberInfo element)
at RiskFirst.Hateoas.DefaultRouteMap.<>c.<.ctor>b__5_4(<>f__AnonymousType0`2 m)
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at RiskFirst.Hateoas.DefaultRouteMap..ctor(IActionContextAccessor contextAccessor, ILogger`1 logger)
I have an Nginx load balancer that passes host onto the header when I make a request but my hypermedia links aren't including the port (8080). It works fine when I run my api instances separately but not with the nginx load balancer.
services.AddLinks(config =>
{
config.AddPolicy<User>(policy =>
{
policy.RequireSelfLink()
.RequireRoutedLink("get", "GetUser", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("post", "PostUser", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("put", "PutUser", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("delete", "DeleteUser", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("all", "GetUsers", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("requests", "GetRequestsByUserId", x => new { id = x.Id })
.RequireRoutedLink("root", "ApiRoot");
});
config.AddPolicy<Request>(policy =>
{
policy.RequireSelfLink()
.RequireRoutedLink("post", "PostRequest", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("put", "PutRequest", x => new { id = x.Id })
.RequireRoutedLink("delete", "DeleteRequest", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("all", "GetRequests", x => new { id = x.Id }, condition => condition.AuthorizeRoute())
.RequireRoutedLink("open", "GetOpenRequests")
.RequireRoutedLink("filter1", "GetRequestsByCityState")
.RequireRoutedLink("filter2", "GetRequestsByZip")
.RequireRoutedLink("root", "ApiRoot");
});
config.AddPolicy<Root>(policy =>
{
policy.RequireSelfLink()
.RequireRoutedLink("users", "GetUsers")
.RequireRoutedLink("requests", "GetRequests")
.RequireRoutedLink("account", "PostUser")
.RequireRoutedLink("reset", "ResetPassword");
});
});
Hello, has anyone used the package with Autofaq ? It seems the dependecy context is null in that case.
The issue is happening when the package is called from another dll project. I will provide more information, in case needed.
p.s. returning a new empty list here
internal class DefaultAssemblyLoader : IAssemblyLoader
{
public IEnumerable<Assembly> GetAssemblies()
{
should do the work (I think).
This should be public.
While trying to call linksService.AddLinksAsync(result)
I am getting NullReferenceException.
at RiskFirst.Hateoas.DefaultRouteMap.GetCurrentRoute()
at RiskFirst.Hateoas.DefaultLinksService.d__8`1.MoveNext()
Is there a documentation where it shows an example of using page links?
I originally had this functionality, and it makes sense. I just want to define a policy against IPagedLinkContainer
to add paging links if there is more than one page and use that on every policy for PagedLinkContainer<TResource>
results.
I removed it, and forgot to make IPagedLinkContainer
public (#2)
Is there a way add link to the Created or updated Resource
Suppose a user is created or updated, i would like to add a link to retrive the user instead of just OK, how can that can be done.
I created a console app using a pre dotnet core project template. Added MVC core deps and run it.
There is a NRE inside DefaultRouteMap ctor - DependencyContext.Default is null.
There is a hack for initializing the DependencyContext.Default (if you supply a App.deps.json (copied from .net core application))
v1.1.3 of the NuGet package has the minimum requirements of .Net 4.5.1 and .Net Standard 1.6.
v1.2.0 has the minimum requirements of .Net 4.6.1 and .Net Standard 2.0.
This is a breaking change because you've dropped support for older (and still widely used!) platforms and so the version number of the package should have been incremented to v2.0 as per SemVer 2.0 rules.
Hi,
first of all love the library!.
I'm just having a minor issue with paging when using .NET 5.
Basically the LinksHandlerContext has this line:
public IQueryCollection CurrentQueryValues => ActionContext?.HttpContext?.Request?.Query ?? new QueryCollection();
and by removing the 'new QueryCollection', it allows the operation to work as expected.
I have a change which seems to fix it for me, i was hoping you guys could review and push a new build.
Thanks
ActionContext.RouteData.Routers is empty is aspcore 2.2.
Alternative is to use LinkGenerator to for UrlGeneration
I've added a PR to solve that.
Regards,
Hannan
In DefaultRouteMap
, the routes are discovered using Assembly.GetExecutingAssembly()
and then searching for types which extend Controller
.
However, when you are performing an integration test using Microsoft.AspNetCore.TestHost
(as recommended by Microsoft here) then the 'executing' assembly is actually the test assembly, not the service assembly. This means the routes in the service assembly are never discovered and so custom link policies are never applied.
I have been adding links successfully to a .Net Core 3.1 API project.
Today I have tried to add versioning to my API via the Microsoft.AspNetCore.Mvc.Versioning
package and am now getting errors in LinkTransformationBuilderExtensions.AddRoutePath
.
Specifically, ctx.LinkGenerator.GetPathByRouteValues()
returns null for insert and delete links.
System.InvalidOperationException: Invalid path when adding route 'InsertValueRoute'. RouteValues: action=Get,controller=Values,version=1
at RiskFirst.Hateoas.LinkTransformationBuilderExtensions.<>c.<AddRoutePath>b__2_0(LinkTransformationContext ctx) in C:\Source\Doowruc\GitHub\Doowruc\riskfirst.hateoas\src\RiskFirst.Hateoas\LinkTransformationBuilderExtensions.cs:line 33
at RiskFirst.Hateoas.BuilderLinkTransformation.<>c__DisplayClass2_0.<Transform>b__0(StringBuilder sb, Func`2 transform) in C:\Source\Doowruc\GitHub\Doowruc\riskfirst.hateoas\src\RiskFirst.Hateoas\BuilderLinkTransformation.cs:line 21
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
at RiskFirst.Hateoas.BuilderLinkTransformation.Transform(LinkTransformationContext context) in C:\Source\Doowruc\GitHub\Doowruc\riskfirst.hateoas\src\RiskFirst.Hateoas\BuilderLinkTransformation.cs:line 19
at RiskFirst.Hateoas.DefaultLinksEvaluator.BuildLinks(IEnumerable`1 links, ILinkContainer container) in C:\Source\Doowruc\GitHub\Doowruc\riskfirst.hateoas\src\RiskFirst.Hateoas\DefaultLinksEvaluator.cs:line 25
I have added a .Net Core 3.1 sample project to my fork (https://github.com/doowruc/riskfirst.hateoas) which demonstrates the issue. This is a copy of the existing classes in the BasicSimple sample
I am having an api like below with two routes (one for backward compatibility)
[HttpGet("~/v2/location/{id:int}", Name = "LocationV2")]
[HttpGet("~/location/{id:int}", Name = "Location")]
public async Task<IActionResult> Getlocation(int id)
{
....
_linksService.AddLinksAsync(locationmodel);
....
}
Also, I have configured the Startup like below
services.AddLinks(config =>
{
config.UseRelativeHrefs();
config.ConfigureRelTransformation(transform => transform.Add(ctx => $"{ctx.LinkSpec.RouteName}"));
config.AddPolicy<Location>(policy =>
{
policy.RequireRoutedLink("users", "Get Users for a Location", x => new { id = x.Id });
policy.RequireRoutedLink("areas", "Get Areas for a Location", x => new { locationId = x.Id });
});
});
I am getting an exception "Multiple custom attributes of the same type found", while trying to call the api. It fails in the AddLinksAsync method. Can someone throw more light on how to resolve the issue?
Your help is much appreciated
Thanks
Hello,
If you set a RequireSelfLink policy for an entity type returned by a method with no route name in the HttpGet/HttpPost/... attribute, the system fails because the next line returns null in LinkTransformationBuilderExtensions:
var path = ctx.LinkGenerator.GetPathByRouteValues(ctx.HttpContext, ctx.LinkSpec.RouteName, ctx.LinkSpec.RouteValues);
If you don't specify a route name, the library sets the method full name (Namespace.Controller.MethodName) in RouteInfo. The LinkGenerator is not able to get the route with this name, but if you set a null value, it works.
Fix:
Change RouteInfo to allow null values:
RouteName = name; // ?? $"{methodInfo?.ControllerType.Namespace}.{methodInfo?.ControllerType?.Name}.{methodInfo?.MethodName}";
And remove the null check LinkTransformationBuilderExtensions.AddRoutePath.
Well, I have to say that I have changed the library target to net6.0 but the routing library is the same since netcore 3.0.
Need to solve this through use of SerializableDictionary (instead of using Dictionary) with the help of https://stackoverflow.com/questions/67959/net-xml-serialization-gotchas or https://msdn.microsoft.com/en-us/library/gg496181.aspx in your source code in LinkContainer.cs and ILinkContainer.cs
Thank you for writing this very helpful HATEOAS library. I have a suggestion that I think might improve it. Please let me know if you think it's a good idea.
Currently whatever library contains the data transfer objects (DTOs) that are used to pass representations of domain objects between the server and the client must have a dependency on RiskFirst.Hateoas. Furthermore, every DTO class must inherit from RiskFirst.Hateoas.Models.LinkContainer
, and every collection of DTOs must be an ItemsLinkContainer
.
It seems to me that this is not ideal. We might (at least hypthetically) want to reuse those same DTOs in another part of the application that deals clients that aren't RESTful or aren't even using HTTP...perhaps a Console client or WinForms or WPF or something else entriely.
Because DTOs are part of the core, maybe they shouldn't be tightly coupled to a library that specializes in HTTP communication. I was wondering if LinkContainer could become LinkContainer<T>
where T
is your DTO class, thus keeping DTO separate from the HATEOAS object. Something similar could be done with the collection class ItemsLinkContainer<T>
.
What do you think... Is it possible? Is it a good idea? Could it be done without negatively affecting the output when the LinkContainer<T>
is serialized to JSON?
When riskfirst.hateoas package is referenced by a different project in the solution and not directly referenced by the Web Project the RouteMap has no routes.
Solution Setup.
When i add the RiskFirst.Hateoas package to MySolution.WebApi the RouteMap is correctly populated.
Am not logging this as a bug, it was just interesting behavior that was tricky to diagnose.
This library currently only supports .Net Standard 1.6 which means it is currently unusable on the full .Net Framework.
However, all of the dependencies of this project support multi-targeting .Net Framework 4.5.1. I can't raise a Pull Request for this right now, but the required changes are:
In RiskFirst.Hateoas.csproj
, change TargetFramework
to:
<TargetFrameworks>netstandard1.6;net451</TargetFrameworks>
In SelfLinkRequirement.cs
, change Task.CompletedTask
to Task.FromResult(true)
(the former is only available in .Net 4.6+ whereas the latter is pretty much equivalent but works in .Net 4.5 also).
It seems that HATEOAS has no defined preference for the use of "links" or "_links". Some libraries use one or the other.
As part of our API definition, we have published the contracts using "links" but this library uses "_lnks" as the property name. I don't really want to change the contract since customer have already started building clients against that published contract.
Is there any way to configure this property name?
An incorrectly configured route, for example badly defined parameters
// Note: id in attr vs myObjectId as param name
[HttpGet("{id:int}",Name="MyBadRoute")
public async Task<MyObject> GetMyObject(int myObjectId)
{
}
And routed via RequiresRoutedLink
options.AddPolicy<MyObject>(policy => policy.RequiresRoutedLink("blah","MyBadRoute",x => new {myObjectId = x.MyObjectId});
Will cause a NullReferenceException due to GetVirtualPath
returning null in LinksTransformationBuilderExtensions
...
var virtualPathData = ctx.Router.GetVirtualPath(virtualPathContext);
return virtualPathData.VirtualPath;
Just wondering if it would be possible to inject the service in some way so I could add links in a result filter rather than of every API call.
The packages at Nuget.org are lagging from what is released here. That makes this package look more abandoned than it actually is.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.