Create a text/plain Jersey response

2020-02-10 08:06发布

问题:

I have some code that works, but I am looking for a better way to do it. I have a RESTful web API that I want to support JSON, XML, and TEXT media types. The JSON and XML is easy with a JAXB annotated "bean" class. I just got text/plain to work, but I wish Jersey was a little more intelligent and would be able to convert a list of my beans, List<Machine> to a string using toString.

Here is the Resource class. The JSON and XML media types use a JAXB annotated bean class. The text plain uses a custom string format (basically the stdout representation of a command).

@Path("/machines")
public class MachineResource {
    private final MachineManager manager;

    @Inject
    public MachineResource(MachineManager manager) {
        this.manager = manager;
    }

    @GET @Path("details/")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public List<Machine> details() {
        return manager.details();
    }
    @GET @Path("details/")
    @Produces({ MediaType.TEXT_PLAIN })
    public String detailsText() {
        StringBuilder text = new StringBuilder();       
        for(Machine machine : manager.details()) {
            text.append(machine.toString());
        }
        return text.toString();
    }

Is there a better way to do this with Jersey automatically converting to a string, so I only have to implement one method here? (That can handle all 3 media types)

I see that I can implement a MessageBodyWriter, but that seems like a lot more trouble.

EDIT:

If it matters, I am using the embedded Jetty and Jersey options.

Thanks!

回答1:

Implementing a MessageBodyReader/Writer would be what you need to do in order to do the following:

@GET @Path("details/")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
public List<Machine> details() {
    return manager.details();
}

It's not very much code to write, and if you are able to write it generic enough you will be able to get some re-use out of it.



回答2:

1. @Provider

For some reason the documentation does not mention that I need to annotate the MessageBodyWriter class with @Provider. I have done that...

@Provider
@Produces(MediaType.TEXT_PLAIN)
public class PlainTextWriter implements MessageBodyWriter<Machine> {

... and now I see this message on start up.* (Good)

INFO: Provider classes found:
  class com.blah.resources.PlainTextWriter

I still get the exception,

SEVERE: A message body writer for Java class java.util.ArrayList, 
and Java type java.util.List<com.blah.beans.Machine>, 
and MIME media type text/plain was not found
  • Without @Provider, you get this message on start-up: INFO: No provider classes found.

2. Use the correct type

The second part is that I was not implementing for the correct type. I had a List of beans. I assumed Jersey would be smart enough to understand how to serialize a list, but I guess it could do that in different ways. Here was my second change: (Note the addition of List)

@Provider
@Produces(MediaType.TEXT_PLAIN)
public class PlainTextWriter implements MessageBodyWriter<List<Machine>>

It is working now. It's pretty cool because now I have a generic plain/text implementation. I only have to implement the toPlainText method of my interface for each Bean. No sweat!

3. Use the correct package.

It broke again. Looking at the log I see this:

INFO: Scanning for root resource and provider classes in the packages:
  com.my.project.resources
...
INFO: No provider classes found.

So I refactored my MessageBodyWriter into that package and it works again!