'No converter found' for ID in REST endpoint
Closed this issue · 9 comments
Hi @paulcwarren
I have a question regarding spring-content-rest...
I'm using spring-content to store media files, for example MP4 files, on a file system. That works perfectly and was really simple to set up!
Now I need an endpoint to stream my MP4 videos in the browser. As I have read that this is already supported by spring-content-rest, I want to use it... I'm however struggling with setting it up; maybe you can help?
The definition of my media store is simple:
@StoreRestResource(path = "media-file-contents")
@RestResource(exported = false)
public interface SpringContentMediumFileStore extends ContentStore<MediumFileContentJpaEntity, MediumFileContentJpaId> {
}
The JPA entity as well:
@InfrastructureRing
@Entity
@Table(name = "MEDIUMFILECONTENTS")
public class MediumFileContentJpaEntity implements MediumFileContent {
@EmbeddedId
private MediumFileContentJpaId id;
@ContentId
@Column(name = "content_id")
private String contentId;
@ContentLength
@Column(name = "content_length")
private long contentLength;
@MimeType
@Column(name = "mime_type")
private String mimeType = "video/mp4";
}
Writing to the file store and reading from it works perfectly fine...
The point where I am struggling with my endpoint setup is my ID, which basically consists of two other IDs:
@Embeddable
public class MediumFileContentJpaId implements MediumFileContentId, Serializable {
@AttributeOverride(name = "value", column = @Column(name = "supporting_medium_id"))
private SupportingMediumId mediumId;
@AttributeOverride(name = "value", column = @Column(name = "medium_file_id"))
private MediumFileId fileId;
}
SupportingMediumId
and MediumFileId
are just value objects that wrap UUID strings.
Via the ContentRestConfigurer
I set the base URI for the endpoint to /contentApi
:
@Bean
public ContentRestConfigurer contentRestConfigurer() {
return new ContentRestConfigurer() {
@Override
public void configure(RestConfiguration config) {
config.setBaseUri(URI.create("/contentApi"));
}
};
}
Here comes my problem... When I call the endpoint with my two IDs, like localhost:8080/content/Api/media-file-contents/{supportingMediumId}/{mediumFileId}
, or just with any string localhost:8080/content/Api/media-file-contents/{just-any-string}
, I get the following exception:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [ch.my_customer.infrastructure.media.content.MediumFileContentJpaId]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-5.3.2.jar:5.3.2]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-5.3.2.jar:5.3.2]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-5.3.2.jar:5.3.2]
at internal.org.springframework.content.rest.controllers.resolvers.EntityResolver.findOne(EntityResolver.java:140) ~[spring-content-rest-1.2.5.jar:na]
at internal.org.springframework.content.rest.controllers.resolvers.EntityResolver.resolve(EntityResolver.java:82) ~[spring-content-rest-1.2.5.jar:na]
at internal.org.springframework.content.rest.controllers.StoreHandlerMethodArgumentResolver.resolveArgument(StoreHandlerMethodArgumentResolver.java:99) ~[spring-content-rest-1.2.5.jar:na]
at internal.org.springframework.content.rest.controllers.ResourceHandlerMethodArgumentResolver.resolveArgument(ResourceHandlerMethodArgumentResolver.java:38) ~[spring-content-rest-1.2.5.jar:na]
I already tried to tackle this in two ways...
- I created a
org.springframework.core.convert.converter.Converter
:
@Component
public class StringToMediumFileContentJpaIdConverter implements Converter<String, MediumFileContentJpaId> {
@Override
public MediumFileContentJpaId convert(String input) {
// impl. that returns a MediumFileContentJpaId
}
}
- I registered a converter in the
FilesystemStoreConfigurer
:
@Bean
public FilesystemStoreConfigurer configurer() {
return new FilesystemStoreConfigurer() {
@Override
public void configureFilesystemStoreConverters(ConverterRegistry registry) {
registry.addConverter(new Converter<String, MediumFileContentJpaId>() {
@Override
public MediumFileContentJpaId convert(String s) {
// impl. that returns a MediumFileContentJpaId
}
});
}
};
}
Nothing helped so far... Can you help me out here? How do I have to configure spring-content-rest so that I can stream my MP4s under an endoint that takes my two nested IDs as parameters (localhost:8080/content/Api/media-file-contents/{supportingMediumId}/{mediumFileId}
)?
Alternatively I would also be happy if I could call the endpoint with the contentId
, but that doesn't seem to work as well (because it looks like the framework is always expecting a MediumFileContentJpaId
based on my interface).
Thanks in advance!
Stefan
I'm not sure, but I think there is a problem here:
You create a new DefaultConversionService
instead of using the corresponding spring beans?
At least I assume that this is the reason why my converter for the Id is not found...
Best regards,
Stefan
Thanks for researching. I will take a look at this soonest. The only comment/question I have about the initial report is the format of the URI. Splitting the ID over two segments of the URI looks problematic. I would image you would have to do something like:
localhost:8080/content/Api/media-file-contents/{supportingMediumId}-{mediumFileId}
Otherwise, I am not sure how we would ever "know" the ID was the last two segments of the URI.
Question, is that something that Spring Data REST does support?
I don't know whether this is supported by Spring Data REST... Maybe it would be possible if you provide an approach how one can easily define the desired controllers/endpoints and the mapping to the content store manually (at the moment I am also missing the possibility to only activate some specific endpoints and HTTP verbs; for example if I just want to activate a GET endpoint for streaming, but PUT, POST, etc. should not be allowed).
However, if I could do it with {supportingMediumId}-{mediumFileId}
as suggested, that would be absolutely fine for me. The possibility to use a custom converter to convert the Id-segment (one string) into my ID value object would already help a lot.
Ok. So, for ContentStore's, Spring Content REST leans pretty heavily on Spring Data so I'd first like to figure out if/how Spring Data/REST supports this use case and then figure out if/how Spring Content/REST might support it. Once I get a better understanding we might be able to tease the two apart so that you don't have to use Spring Data REST yourself.
To that end I put together this sample project to check that I understand your data model. This uses the second approach above. You should be able to run this and
curl -X POST -H 'Content-Type:application/hal+json' -d '{}' http://localhost:8080/media-files/
to create a new entity.
Haven't done anything with content yet. Let me know if this is representative, or not.
Yes, that's representative.
(Basically, I just want to be able to use a custom Java object for the entity ID. Currently, this is not possible with Spring Content; as it has to be a String, UUID, or anything else that can be converted with the default converters provided by Spring.)
Hi @stefan-ka, I figured out how to do this and committed an updated sample app. You should be able to run the test and see it green.
It uses Spring Data REST that allows configuration of both Spring Data and REST to know about the embedded id. I am not sure if you are or want to use Spring Data REST. If so, I thought this might be an option for you.
Hi @paulcwarren
Thanks for the suggestion. However, it should be possible to use Spring Content REST without Spring Data REST, or not? I don't really need Spring Data REST; actually the Video Streaming Endpoint (for the videos in my Sprint Content store) is the one and only endpoint I want to have exposed.
Thought I would try it in case it would work as you could use it now.
You are right. I have observed quite a few users who choose not to use Spring Data REST so the aim has been to not require it.
Will look into this soonest then. I am OOO Friday/Monday so probably not until next week now though.
Thanks @paulcwarren.
For the moment (as a workaround), I only use Spring Content as the file store and implemented the streaming endoint by myself.