Coder Social home page Coder Social logo

riskfirst.hateoas's People

Contributors

coldridj avatar dnikolovv avatar gibletto avatar hannan-gc avatar hannanmumtaz avatar hendonnelly avatar jbicks avatar jessesingleton avatar jmontagu avatar justinbicknell avatar nicky-iliev avatar tasmith81 avatar wigmorewelsh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

riskfirst.hateoas's Issues

Define Link class as [Keyless] for .NET 6

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.

Multiple route attribute in same name is not supported while configuring for the HATEOAS library

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

DefaultRouteMap is Transient

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.

#28

New release soon?

Its been 2 years since your last nuget publish but this repo is actively maintained. Are you going to publish sometime soon?

A common standard needs to be agreed upon

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.

How to add links to collection of <Model>

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;
    }

Having multiple HttpMethod attributes on a route breaks DefaultRoutMap

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)

Not working with nginx load balancer

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.

image

  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");
    });
  });

DependencyContext.Default is null

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).

Cannot add pagelinks, getting NullReferenceException

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?

Link Policies should be combinable

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)

Breaking Change Introduced in v1.2.0

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.

Could not load type 'Microsoft.AspNetCore.Http.Internal.QueryCollection' from assembly 'Microsoft.AspNetCore.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

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

DefaultRouteMap doesn't work when integration testing

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.

Unable to transform links with .Net Core 3.1 and Microsoft.AspNetCore.Mvc.Versioning

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

Unable to add multiple routes, while configuring for the HATEOAS library

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

RequireSelfLink fails when no name is specified in the method httpheader

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.

Suggestion for better decoupling/separation of concerns

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?

RouteMap is empty

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.

  • MySolution.Models - contains project models, and references RiskFirst.Hateoas package.
  • MySolution.WebApi - References MySolution.Models, and Hateoas Services are configured.

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.

Multi-Targeting Support For .Net Framework 4.5.1

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).

Property name, "_links" verus "links". Configurable?

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?

Incorrectly configured route causes a NRE from LinksTransformationBuilder.AddRoutePath

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;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.