GithubHelp home page GithubHelp logo

spring-projects / spring-hateoas-examples Goto Github PK

View Code? Open in Web Editor NEW
371.0 16.0 183.0 295 KB

Collection of examples on how (and why) to build hypermedia-driven apps with Spring HATEOAS

License: Apache License 2.0

Java 97.38% HTML 2.51% Shell 0.11%
rest hateoas spring

spring-hateoas-examples's Introduction

Spring HATEOAS Examples

icon?job=spring hateoas examples%2Fmain&subject=main icon?job=spring hateoas examples%2F0.x&subject=0

This repository contains example projects to interact with Spring HATEOAS.

  • Learn how to interact with a Spring HATEOAS-powered app, from inside as well as the command line.

  • See how to upgrade a REST resource without having to create new media types, version URIs, etc.

We have separate folders for each of these:

Spring HATEOAS Modules

  • Basics - Poke and prod at a hypermedia-powered service from inside the code as well as externally using standard tools

  • Simplified - Use Spring HATEOAS in the simplest way possible.

  • API Evolution - Upgrade an existing REST resource

  • Hypermedia - Create hypermedia-driven REST resources, linking them together, and supporting older links.

  • Affordances - Create richer hypermedia controls using more complex hypermedia formats

  • Spring HATEOAS + Spring Data REST - How to stir in custom links and logic with a Spring Data REST-based app

Note
The main branch tracks Spring HATEOAS 1.0, based upon Spring Boot 2 + Spring Framework 5. To see examples depicted against the 0.x branch (Spring 4.x) visit the 0.x branch.

Community

The Spring HATEOAS community has its own contributions when it comes to examples of building hypermedia.

  • Siren HATEOAS Examples - The maintainer of the Siren extension of Spring HATEOAS has an extra set of examples.

spring-hateoas-examples's People

Contributors

avivmu avatar gregturn avatar michael-simons avatar spring-operator 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  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

spring-hateoas-examples's Issues

Error in this example: Hypermedia Root Controller

In this example I can't get this to work without an error RepresentationModel Needs a T.

RepresentationModel model = new RepresentationModel();

What is the right way to do this without a T in the context of a Root Controller?

/**
 * @author Greg Turnquist
 */
@RestController
class RootController {

	@GetMapping("/")
	ResponseEntity<RepresentationModel> root() {

		RepresentationModel model = new RepresentationModel();

		model.add(linkTo(methodOn(RootController.class).root()).withSelfRel());
		model.add(linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"));
		model.add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees"));
		model.add(linkTo(methodOn(ManagerController.class).findAll()).withRel("managers"));

		return ResponseEntity.ok(model);
	}
}

Looking for an example to use hateoas with microservices

Hi,

I discovered these examples in a Feign ticket: this one. They are great!

Is there also an example that describe how, from a "Best Practice" view, to make several HATEOAS services communicate with each other ?
I currently use Feign to do that, but as @gregturn answered, this is probably not the best way, since Feign uses hard coded URLs and breaks some of the HATEOAS purposes.

So, for instance, if a user wants to book a meeting with a teacher, and if the service that manage the booking must communicate with a schedule-service (which knows about the teacher availabilites) and a teacher-service (which knows about teacher properties), how can it request these other services?
Of course, I understand that we can use RestTemplate to issue these requests; I am just wondering if there is an existing example that could guide us as the best way to do this, as good as the api-evolution example and the hypermedia example.

Thanks!

Example to showcase how to create a relationship between resources

The existing hypermedia example provides an overview on related resources (Employee, Manager), how to link them and evolve api.

In addition to that, I would like to see example code on how to create relationships and how to provide hypermedia links for that. Especially when it comes to many-to-many relationships.

Imagine the Employee works in different Projects, so these two resources are many-to-many.

How do we add hypermedia links to create these relationships and how are these POST/PUT requests modeled in the controllers? I would be grateful to see this in a spring-hateoas-example.

Possible error in examples for "HATEOAS with Affordances"

Hello, I am quite new to the whole Spring ecosystem, so excuse me if I am wrong. I just started new job that uses Spring and I am trying my best to learn up to date info in regards to Spring.

When reading through guide about HATEOAS and Affordances I came across following snippet:

return new EntityModel<>(savedEmployee,
			linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel()
				.andAffordance(afford(methodOn(EmployeeController.class).updateEmployee(null, savedEmployee.getId())))
				.andAffordance(afford(methodOn(EmployeeController.class).deleteEmployee(savedEmployee.getId()))),
			linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees")
		).getId() >>> ISSUE
			.map(Link::getHref)
			.map(href -> {
				try {
					return new URI(href);
				} catch (URISyntaxException e) {
					throw new RuntimeException(e);
				}
			})
			.map(uri -> ResponseEntity.noContent().location(uri).build())
			.orElse(ResponseEntity.badRequest().body("Unable to create " + employee));

The method call getId() that I marked with >>> ISSUE seems to cause problems for me. When I used it my IDE (IntelliJ IDEA) complained that this method does not exists in the EnityModel<Employee> class. I couldn't find any reference to getId() method in documentation for both the current Spring HATEOAS API documentation, or the 1.0.0 API documentation.

When reading through source code I found that getId() wasn't actually used, it uses .getLink(IanaLinkRelations.SELF), which when used in my code worked immediately. It might be good idea to update the README.adoc file to reflect that.

Also, when reading through the current API documenation I found that the constructor EntityModel(T content, Link... links) is deprecated and that EntityModel.of(T content, Link... links) should be used instead. I think it might be good to update the example code and guide to use the newer and not deprecated solution.

Lastly, it is nice that RepresentationModelAssembler<> is mentioned and kept out of the example for simplicity, but it might be a good idea to point to an example where it is used, or have a short snippet showing how it can be implemented and used. I followed the guide found on Spring.io, so you might consider linking to it as an example.

Multiple versions not supported

My API has two versions v1 / v2 and I am versioning the api using the uri.
endpoint version 1: https://mytestapi.test/api/v1/search
endpoint version 2: https://mytestapi.test/api/v2/search
The response always has v1 in the self_link even if the request came for v2

 {
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/v1/search/252f9d84-ea93-4234-887b-78e57ac6d730"
                    }
                },
                "id": "252f9d84-ea93-4234-887b-78e57ac6d730",
       
            }

Wrong link created in https://github.com/spring-projects/spring-hateoas-examples/tree/master/hypermedia

The self link generated for GET http://localhost:8080/managers/1/employees is wrong:

    "self": {
        "href": "http://localhost:8080/employees"
    },

This points to the list of all employees, not just the employees of manager 1.

What is the best way to fix this? Maybe add another EmployeeRepresentationModelAssembler that does not simply add the self link by calling

         super.addLinks(resources);

but that extracts the manager id from the manager one of its content’s members? (By design, they must all have the same manager.) But what if the manager does not (yet) have any employees? Then we would not be able to reconstruct the manager’s id from the empty list of his emplyoees.

Very ugly… Is there a better way for this?

Maybe this: In the /managers/{id}/employees route in the EmployeeController, overload the addlinks method in EmployeeRepresentationModelAssembler by one that returns the correct link with the manager’s id?

@GetMapping("/managers/{id}/employees")
public ResponseEntity<CollectionModel<EntityModel<Employee>>> findEmployees(@PathVariable long id) {
	
	EmployeeRepresentationModelAssembler assemblerByManager = new EmployeeRepresentationModelAssembler() {
		@Override
		public void addLinks(CollectionModel<EntityModel<Employee>> resources) {
			resources.add(linkTo(methodOn(EmployeeController.class).findEmployees(id)).withSelfRel());
			resources.add(linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"));
			resources.add(linkTo(methodOn(EmployeeController.class).findAllDetailedEmployees()).withRel("detailedEmployees"));
			resources.add(linkTo(methodOn(ManagerController.class).findAll()).withRel("managers"));
			resources.add(linkTo(methodOn(RootController.class).root()).withRel("root"));
		}
	};
	
	CollectionModel<EntityModel<Employee>> resources = assemblerByManager.toCollectionModel(repository.findByManagerId(id));

	return ResponseEntity.ok(resources);
}

Would that be OK? Is it OK to create our own instances of EmployeeRepresentationModelAssembler? Or should we always use one of the assembler instances injected by the Spring framework?

OOP approach to resource

I would like to see some example, where I can use HATEOAS approach for creating rest api services on some real objects. Is this approach good way?

I mean something like this:

@Data
public class Invoice {
  // ... some fields/attributes and other things
  public Receipt claimPaid(PaymentInfo paymentInfo) {
    // ....
  }

  public boolean isCancellable() {
    // .... Return if invoice can be canceled (from business point of view) 
  }
  
  public void cancel() {
    // .... Actually cancel invoice and possibly delete it
  }
}

I would like to see relations in rest api resource for "claimPaid" method (always) and for "cancel" method, if applicable.

Is hateoas usable (without much coding) in a such way?

I know that using spring-data there is extremely easy to have HAL working on entities (no controller class is needed for example. Just entity and "empty" repository interface with RepositoryRestResource annotation).

Please provide some "real life" example in repository, or at least some info how we should use it in such application.

Make Traverson work with MockMvc

When wiring a MockMvc test environment, Traverson seems unable to plan along given it is trying to connect to a REAL resource.

Traverson is good for testing since it is built to understand hypermedia. The current options are to NOT user Traverson and instead do the hypermedia handling with existing tools, or to run a full blow Spring Boot embedded container to get the benefits.

Encapsulate Hateoas Response with ApiResponse Object

{
  "_embedded": {
    "employees": [
      {
        "id": 1,
        "name": "Bilbo Baggins",
        "role": "burglar",
        "_links": {
          "self": {
            "href": "https://example.com:9001/employees/1"
          },
          "employees": {
            "href": "https://example.com:9001/employees"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "https://example.com:9001/employees"
    },
    "root": {
      "href": "https://example.com:9001"
    }
  }
}

Rewrite to



{
  "timestamp": "20210120",
  "status": "OK",
  "message": "Success",
  "status_code": 200,
	"errors":[],
 
  "_embedded": {
    "employees": [
      {
        "id": 1,
        "name": "Bilbo Baggins",
        "role": "burglar",
        "_links": {
          "self": {
            "href": "https://example.com:9001/employees/1"
          },
          "employees": {
            "href": "https://example.com:9001/employees"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "https://example.com:9001/employees"
    },
    "root": {
      "href": "https://example.com:9001"
    }
  }


}

A simpler splitting logic from name to first and last name

The current code has the following method for splitting name to two:

public void setName(String wholeName) {

	String[] parts = wholeName.split(" ");
	this.firstName = parts[0];
	if (parts.length > 1) {
		this.lastName = StringUtils.arrayToDelimitedString(Arrays.copyOfRange(parts, 1, parts.length), " ");
	} else {
		this.lastName = "";
	}
}

IMHO, the same could be done without using arrays and 3rd party libraries:

public void setName(String wholeName) {
	final int i = wholeName.indexOf(" ");
	if (i > 0) {
		this.firstName = wholeName.substring(0, i);
		this.lastName = wholeName.substring(i + 1);
	} else {
		this.firstName = wholeName;
		this.lastName = "";
	}
}

Should I submit a PR for this?

Cannot make POST request -- object attributes are null on arrival

Hi,

I've written a simple create method for the Employee entity, but attributes of the object received are null:

@RequestMapping(value = "/employees", method = RequestMethod.POST, consumes = MediaTypes.HAL_JSON_VALUE)
public Employee create(Employee employee){
return repository.save(employee);
}

Could you please update the repo with such a simple but working example or at least try to explain what could be the issue here? Thank you very much!

Leverage Spring Data REST's link building utility

Current, we have to do stuff like this to mesh a custom controller with a Spring Data REST one.

private static Link applyBasePath(Link link, String basePath) {

	URI uri = link.toUri();

	URI newUri = null;
	try {
		newUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), //
				uri.getPort(), basePath + uri.getPath(), uri.getQuery(), uri.getFragment());
	} catch (URISyntaxException e) {
		e.printStackTrace();
	}

	return new Link(newUri.toString(), link.getRel());
}

Take advantage of DATAREST-1423 to simplify the integration.

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.