spring-projects / spring-projects/spring-framework

Spring WebFlux HATEOAS No Encoder for EntityModel with preset Content-Type 'null'

Open
#27,327 5 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

in: web type: enhancement
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

Affects: org.springframework.boot:spring-boot-starter-parent:2.5.2


Currently also an open stackoverflow question.

While trying to expose and consume an endpoint which produces application/x-ndjson and returns a HAL-JSON some kind of integration is missing, I'm able to return and de-serialize a Mono<List> of HAL-JSON, but not a Flux.

pom.xml and Pojo class
(...)
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath />
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
		(...)
	<dependencies>
(...)
public class Pojo {
	private Integer id;
	private String name;

	public Pojo(Integer id, String name) {
		this.id=id; this.name=name;
	}

	public Integer getId() { return id; }
	public String getName() { return name; }
}
@RestController
@RequestMapping()
public class PojoResource {
	Flux<Pojo> getPojos() {
		return Flux.just(new Pojo(1, "alpha"), new Pojo(2, "beta"), new Pojo(3, "gamma"));
	}
	
	@GetMapping(value="/pojoA", produces = MediaType.APPLICATION_NDJSON_VALUE)
	public Flux<Pojo> getAllPojosAsStream() {
		return getPojos();
	}
	
	@GetMapping("/pojoB")
	public Mono<List<EntityModel<Pojo>>> getAllPojosAsHAL() {
		return getAllPojosAsHALStream().collectList();
	}

	@GetMapping(value="/pojoC", produces = MediaType.APPLICATION_NDJSON_VALUE)
	public Flux<EntityModel<Pojo>> getAllPojosAsHALStream() {
		return getPojos().map(p -> EntityModel.of(p));
	}
}

Calling for Flux of Pojo

curl -X 'GET' 'http://localhost:8080/pojoA' -H 'accept: application/x-ndjson'

Works like a charm:

{"id":1,"name":"alpha"}
{"id":2,"name":"beta"}
{"id":3,"name":"gamma"}

Calling for Mono of List of EntityModel of Pojo

curl -X 'GET' 'http://localhost:8080/pojoB' -H 'accept: */*'

Another gem:

[
  {
    "id": 1,
    "name": "alpha",
    "links": []
  },
  {
    "id": 2,
    "name": "beta",
    "links": []
  },
  {
    "id": 3,
    "name": "gamma",
    "links": []
  }
]

But calling for Flux of EntityModel of Pojo

curl -X 'GET' 'http://localhost:8080/pojoC' -H 'accept: application/x-ndjson'

Fails with an exception:

org.springframework.http.converter.HttpMessageNotWritableException: No Encoder for [org.springframework.hateoas.EntityModel<my.package.Pojo>] with preset Content-Type 'null'
(Full stacktrace can be provided if needed)

Is this not an intended use case?
If you can provide me a little of info about the jackson encoders perhaps I can contribute with my grain of sand.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the /pojoC example with a Flux<EntityModel> and the application/x-ndjson Accept header, then compare it with the working /pojoA and /pojoB endpoints. Trace the WebFlux message-writing path for EntityModel and identify the missing encoder integration; done means the streaming HAL response is encoded successfully without the reported exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.