java-sdk icon indicating copy to clipboard operation
java-sdk copied to clipboard

Fix Content-Type mismatch in publishEvent call

Open MregXN opened this issue 2 years ago • 5 comments

Description

Please reference the issue #779

If contentType is set as ""text/plain" serialization will be skipped.

MregXN avatar May 20 '23 16:05 MregXN

Hi @artursouza , if I modify the serialization method of the serializer (to exclude quotation marks when serializing a string), I will also need to modify the deserialization method and its corresponding tests, otherwise I may fail to pass most of the tests.

MregXN avatar May 26 '23 09:05 MregXN

I will need to think this through. It is not a simple change without breaking existing apps.

artursouza avatar May 31 '23 17:05 artursouza

I will need to think this through. It is not a simple change without breaking existing apps.

After I debugged step by step, it is found that this bug is not directly related to the java-sdk serializer. The reason can be divided into the following two parts:

  1. When publishing data, in addition to using the serializer, the data will be encapsulated into a cloudenent envelop in dapr runtime, and the data will be further encapsulated according to the dataContentType set by the user:
// NewCloudEventsEnvelope returns a map representation of a cloudevents JSON.
func NewCloudEventsEnvelope(id, source, eventType, subject string, topic string, pubsubName string,
	dataContentType string, data []byte, traceParent string, traceState string,
) map[string]interface{} {
	....

	var ceData interface{}
	ceDataField := DataField
	var err error
	if contribContenttype.IsJSONContentType(dataContentType) {
		err = unmarshalPrecise(data, &ceData)
	} else if contribContenttype.IsBinaryContentType(dataContentType) || contribContenttype.IsCloudEventProtobuf(dataContentType, data) {
		ceData = base64.StdEncoding.EncodeToString(data)
		ceDataField = DataBase64Field
	} else {
		ceData = string(data)
	}

	if err != nil {
		ceData = string(data)
	}

	....
}

And there are similar operations when using gRPC protocal. Can find more details in dapr/pkg/runtime/runtime.go publishMessageGRPC()

So the string "This is message #0" will store differently in this envelop if user set the dataContentType as text/plain instead of application/json .

  1. But when subscribe there is no further process of the data according to the dataContentType.

As http subscriber controller for example, it uses the annotation @RequsetBody to parse the data to CloudEvent in default way. But there is no further process of the data according to the dataContentType

...
    @Topic(...)
    @PostMapping(...)
    public Mono<ResponseEntity> getCheckout(@RequestBody(required = false) CloudEvent<Order> cloudEvent) {
        return Mono.fromSupplier(() -> {
            try {
                logger.info("Subscriber received: " + cloudEvent.getData().getOrderId());
                return ResponseEntity.ok("SUCCESS");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }
...

gPRC subscriber is the same. User always use the data directly but not porcess it according dataContentType.

So we need to add more data processing instead of modifying the java-sdk serizer when implementing subscriber. Or directly add new methods to CloudEvent (for http subscriber) and TopicEventRequest (for gRPC subscriber) to parse data according to dataContentType.

MregXN avatar Jun 01 '23 04:06 MregXN

Those are great insights. I think there is a way for CloudEvent to declare a custom serializer for Jackson. I think it is acceptable to have an indirect dependency on Jackson as long as people can still deserialize it without it if they decide to.

artursouza avatar Jun 01 '23 04:06 artursouza

gentle ping @MregXN, any updates?

cicoyle avatar Jun 19 '24 14:06 cicoyle

I am closing this PR due inactivity @cicoyle

salaboy avatar Mar 19 '25 18:03 salaboy