You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Protobuf数据传输至JAXRS端点遇媒体类型及内部错误求助

Fixing Protobuf Data Transfer with JAX-RS

Let's break down your issues one by one and fix them step by step, with clear explanations and corrected code snippets.


1. Initial 415 Unsupported Media Type Error: Root Causes & Fixes

Your first attempt had two critical mistakes that triggered the 415 error:

Mistake 1: Sending Protobuf Schema Instead of Serialized Binary Data

You were sending the text content of a .proto file (the human-readable schema) instead of a serialized Protobuf message instance. The application/x-protobuf media type is designed for binary Protobuf data, not the schema itself.

Mistake 2: Mismatched Content-Type Header

Using Entity.text(inputproto) sets the request's Content-Type to text/plain, but your server endpoint expects application/x-protobuf. This mismatch between what you're sending and what the server accepts causes the 415 error.

Quick Fix for the Initial Approach (Without Custom Providers)

If you want to send raw Protobuf binary data directly:

Client Code:

// First, create and serialize a Protobuf message using your generated Album class
Album album = Album.newBuilder()
    .setSongTitle("Hello World")
    .setTitle("My Debut Album")
    .addArtist("Jane Doe")
    .addReleaseYear(2024)
    .build();

Client genClient = ClientBuilder.newClient();
WebTarget target = genClient.target("http://localhost:8080/ClientJAXRS/rest/Hello/proto");

// Send binary data with the correct Content-Type header
Response res = target.request()
    .header("Content-Type", "application/x-protobuf")
    .put(Entity.entity(album.toByteArray(), "application/x-protobuf"));

Server Endpoint (Raw Binary Handling):

@PUT
@Path("/proto")
@Consumes("application/x-protobuf")
@Produces(MediaType.TEXT_PLAIN)
public Response getInfo(InputStream protoStream) throws IOException {
    // Parse the binary stream into an Album instance
    Album album = Album.parseFrom(protoStream);
    return Response.ok("Received album: " + album.getSongTitle()).build();
}

2. 500 Internal Server Error: Root Causes & Fixes

Your second attempt with custom MessageBodyWriter/MessageBodyReader was on the right track, but had several key issues:

Mistake 1: Using GET with @Consumes

GET requests do not support request bodies. You need to use POST or PUT to send data to the server.

Mistake 2: Incorrect Getters for Repeated Fields

Your Album message has repeated fields (artist, release_year), so the generated Java methods are getArtistList() and getReleaseYearList() (not getArtist() or getReleaseYear()). Using the wrong getter throws a runtime error, causing the 500.

Mistake 3: Broken MessageBodyWriter

You wrapped the OutputStream with a PrintWriter (for text), but Protobuf's writeTo() writes binary data directly. This corrupts the binary stream and breaks parsing.

Mistake 4: Client Not Sending a Request Body

Your client was making a GET request without sending any Protobuf data, so the server tried to parse an empty body into an Album, leading to an error.

Mistake 5: Missing Provider Registration

You need to register your ProtoMessageBodyWriter with both the JAX-RS server and client so they know how to handle the Album type.

Corrected Implementation

Step 1: Fix the MessageBodyWriter/Reader

@Provider
@Produces("application/x-protobuf")
@Consumes("application/x-protobuf")
public class ProtoMessageBodyHandler implements MessageBodyWriter<Album>, MessageBodyReader<Album> {

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

    @Override
    public void writeTo(Album album, Class<?> type, Type genericType, Annotation[] annotations, 
                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, 
                        OutputStream out) throws IOException {
        // Write binary Protobuf data directly to the stream (no PrintWriter!)
        album.writeTo(out);
    }

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

    @Override
    public Album readFrom(Class<Album> type, Type genericType, Annotation[] annotations, 
                          MediaType mediaType, MultivaluedMap<String, String> httpHeaders, 
                          InputStream in) throws IOException {
        return Album.parseFrom(in);
    }
}

Step 2: Fix the Server Endpoint

Switch to POST and use correct getters for repeated fields:

@POST
@Path("/proto-data")
@Consumes("application/x-protobuf")
@Produces(MediaType.TEXT_PLAIN)
public Response getInfo(Album inpAlbum) {
    StringBuilder sbuilder = new StringBuilder("Input Album:\n");
    sbuilder.append("Title: ").append(inpAlbum.getTitle()).append("\n");
    sbuilder.append("Artists: ").append(inpAlbum.getArtistList()).append("\n");
    sbuilder.append("Release Years: ").append(inpAlbum.getReleaseYearList()).append("\n");
    sbuilder.append("Song Title: ").append(inpAlbum.getSongTitle()).append("\n");
    return Response.ok(sbuilder.toString()).build();
}

Step 3: Register the Provider

  • Server Side: Add the provider to your JAX-RS application (if using an Application subclass):
    @ApplicationPath("/rest")
    public class MyApplication extends Application {
        @Override
        public Set<Class<?>> getClasses() {
            Set<Class<?>> classes = new HashSet<>();
            classes.add(HelloResource.class); // Your endpoint class
            classes.add(ProtoMessageBodyHandler.class); // Register the provider
            return classes;
        }
    }
    
  • Client Side: Register the provider when creating the client:
    Client client = ClientBuilder.newClient()
        .register(ProtoMessageBodyHandler.class);
    

Step 4: Corrected Client Code

Send a POST request with the serialized Album instance:

// Create the Album message
Album album = Album.newBuilder()
    .setSongTitle("Let It Be")
    .setTitle("Abbey Road")
    .addArtist("The Beatles")
    .addReleaseYear(1969)
    .build();

// Register the provider and send the request
Client client = ClientBuilder.newClient()
    .register(ProtoMessageBodyHandler.class);

WebTarget target = client.target("http://localhost:8081/ClientJAXRS/rest/Hello/proto-data");

Response response = target.request()
    .post(Entity.entity(album, "application/x-protobuf"));

// Print the response details
System.out.println("Status Code: " + response.getStatus());
System.out.println("Response Body:\n" + response.readEntity(String.class));

// Clean up resources
response.close();
client.close();

Key Takeaways

  • Protobuf is Binary: Always send serialized Protobuf message instances (binary data), not the schema text.
  • Correct Media Type: Use application/x-protobuf for both request and response content types.
  • GET vs POST/PUT: Use POST or PUT when sending data in the request body (GET has no body).
  • Register Providers: Ensure your custom MessageBodyWriter/MessageBodyReader is registered with both server and client.
  • Use Generated Getters: For repeated fields, use the List-returning getters (e.g., getArtistList()).

内容的提问来源于stack exchange,提问作者aΨVaN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 23:47:39