RESTEasy doesn't recognize custom message body

2019-05-04 07:07发布

问题:

My MessageBodyWriter

@Provider
@Produces("text/csv")
public class CSVMessageBodyWriter implements MessageBodyWriter<JaxbList>

    public static final String CONTENT_DISPOSITION_HEADER = "Content-Disposition";     
   //$NON-NLS-1$
    private final static HeaderDelegate<ContentDispositionHeader> header = RuntimeDelegate.getInstance().createHeaderDelegate(ContentDispositionHeader.class);

    public long getSize(JaxbList t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return CsvSerializer.class.isAssignableFrom(type);
    }

    public void writeTo(JaxbList t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                    OutputStream entityStream) throws IOException, WebApplicationException {

        // set content disposition. This will enable browsers to open excel
        ContentDispositionHeader contentDispositionHeader = ContentDispositionHeader.createContentDispositionHeader(MediaTypeUtils.CSV_TYPE);
        contentDispositionHeader.setFileName("representation"); //$NON-NLS-1$
        httpHeaders.putSingle(CONTENT_DISPOSITION_HEADER, header.toString(contentDispositionHeader));

        Charset charset = Charset.forName(ProviderUtils.getCharset(mediaType));
        OutputStreamWriter writer = new OutputStreamWriter(entityStream, charset);

        PrintWriter printWriter = new PrintWriter(writer);
        Iterator<String[]> rows = ((CsvSerializer) t).getEntities();
        while (rows.hasNext()) {
            printWriter.println(CsvWriter.getCSVRow(rows.next()));
        }
        printWriter.flush();
    }
}

My REST Application

@Path("app/v3")
@GZIP
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, "text/csv"})
 public class ApplicationREST

Extension of Application

@ApplicationPath("/")
public class JaxRsActivator extends Application {

  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> classes = new HashSet<Class<?>>();

  public JaxRsActivator() {
    singletons.add(new CSVMessageBodyWriter());
    classes.add(ApplicationREST.class);
  }

  @Override
  public Set<Object> getSingletons() {
    Set<Object> defaults = super.getSingletons();
    singletons.addAll(defaults);
    return singletons;
  }

  public Set<Class<?>> getClasses() {
    return classes;
  }
}

When I run in debug mode I can hit my JaxRsActivator class, so I know that the providers are being loaded. However I get the error "Could not find MessageBodyWriter for response object of type: net.comp.jaxb.JaxbList of media type: text/csv"

回答1:

Based on your code above it would appear that your CSVMessageBodyWriter is being called, but it is failing your isWriteable check.

Your isWriteable check is always going to return false. You are checking if JaxbList is assignable from CSVSerializer. This is always going to fail and your CSVMessageBodyWriter will not be deemed able to handle text/csv.

Try changing your isWriteable method to the following:

public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
{
    return true;
}

This will serialize all text/csv annotated methods. If you want to constrain it to JaxbList then you can do something like this:

public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
{
    ParameterizedType paramType = (ParameterizedType) genericType;

    if(paramType.getRawType().equals(JaxbList.class))
    {
        return true;
    }

    return false;
}

Here is a simple working example of configuring a custom MessageBodyWriter:

Application

public class ProductGuideApplication extends Application
{
    private Set<Object> singletons = new HashSet<Object>();
    private Set<Class<?>> classes = new HashSet<Class<?>>();

    public ProductGuideApplication() 
    { 
        singletons.add(new CSVMessageBodyWriter());
        classes.add(FooResource.class);
    }

    @Override
    public Set<Object> getSingletons()
    { 
        return singletons;
    }

    @Override
    public Set<Class<?>> getClasses() 
    {
        return classes;
    }
}

Resource

@Path("/foo")
public class FooResource
{
    @GET
    @Produces("text/csv")
    public List<Consumer> getConsumers()
    {
        Consumer consumer1 = new Consumer();
        consumer1.setId("1234");
        consumer1.setGender("Male");

        Consumer consumer2 = new Consumer();
        consumer2.setId("2345");
        consumer2.setGender("Male");

        List<Consumer> consumers = new ArrayList<Consumer>();
        consumers.add(consumer1);
        consumers.add(consumer2);

        return consumers;
    }
}

MessageBodyWriter

@Provider
@Produces("text/csv")
public class CSVMessageBodyWriter implements MessageBodyWriter<List<Consumer>>
{
    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
    {
        ParameterizedType paramType = (ParameterizedType) genericType;

        if(paramType.getRawType().equals(List.class))
        {
            if(paramType.getActualTypeArguments()[0].equals(Consumer.class))
            {
                return true;
            }
        }

        return false;
    }

    @Override
    public long getSize(List<Consumer> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
    {
        return 0;
    }

    @Override
    public void writeTo(List<Consumer> t, Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, Object> httpHeaders,
            OutputStream entityStream) throws IOException,
            WebApplicationException 
    {
        //Write your CSV to entityStream here.
    }
}