SSE Plugin: event-unregister causes errors

When the SSE feature is enabled, we noticed many errors in our logs: ContentType not supported ‘text/plain;charset=UTF-8’ (from POST /event-unregister?id?foo)

We have no text/plain serializer registered, as we do not need that for our endpoints. Unfortunately the native implementation of SSE in browsers sends the /event-unregister request with accept / and not application/json when the browser window is closed. We added a global text/plain serializer so that the response for event-unregister is properly generated (regardless of the accept header, the SSE plugin seems to send the response as json) as a workaround.

Is there a better solution than defining a global serializer for text/plain? Imho adding the SSE plugin should take care of that and not require any other changes to the apphost.

There is no generic plain-text serializer, the response type would only be used when returning a text body.

I’ve updated the event unregister API to force returning a JSON response in this commit which is now available from the latest v5.10.5 that’s now available on MyGet.

But it should be returning the default JSON ContentType, do you have features disabled in your AppHost configuration?

This is the normal Request/Response of calling /event-unregister


POST /event-unregister?id=dGVh0F82gtyz3M3QC2T4 HTTP/1.1
Connection: keep-alive
Content-Length: 0
Accept: */*
DNT: 1
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Mobile Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: ss-pid=VGdSVvVx9nQslRMNBxTf; ss-id=GfJUhb3uZ5R0MffRK6SA


HTTP/1.1 200 OK
Server: nginx/1.17.5
Date: Fri, 19 Feb 2021 09:51:27 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Allow, Authorization
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
X-Powered-By: ServiceStack/5.104 NetCore/Linux

{"userId":"-13","isAuthenticated":"false","displayName":"user13","channels":"home","createdAt":"1613728281284","profileUrl":"data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns=''%3E %3Cstyle%3E .path%7B%7D %3C/style%3E %3Cg id='male-svg'%3E%3Cpath fill='%23556080' d='M1 92.84V84.14C1 84.14 2.38 78.81 8.81 77.16C8.81 77.16 19.16 73.37 27.26 69.85C31.46 68.02 32.36 66.93 36.59 65.06C36.59 65.06 37.03 62.9 36.87 61.6H40.18C40.18 61.6 40.93 62.05 40.18 56.94C40.18 56.94 35.63 55.78 35.45 47.66C35.45 47.66 32.41 48.68 32.22 43.76C32.1 40.42 29.52 37.52 33.23 35.12L31.35 30.02C31.35 30.02 28.08 9.51 38.95 12.54C34.36 7.06 64.93 1.59 66.91 18.96C66.91 18.96 68.33 28.35 66.91 34.77C66.91 34.77 71.38 34.25 68.39 42.84C68.39 42.84 66.75 49.01 64.23 47.62C64.23 47.62 64.65 55.43 60.68 56.76C60.68 56.76 60.96 60.92 60.96 61.2L64.74 61.76C64.74 61.76 64.17 65.16 64.84 65.54C64.84 65.54 69.32 68.61 74.66 69.98C84.96 72.62 97.96 77.16 97.96 81.13C97.96 81.13 99 86.42 99 92.85L1 92.84Z'/%3E%3C/g%3E%3C/svg%3E"}

I’m assuming something is off in your AppHost configuration or Ajax request, what API are you using to unregister and what does your HTTP Request/Response look like?

From the sounds of it, it’s explicitly requesting a plain/text response which would be expected to fail to serialize (though the unregister operation would still have been executed).

We do not have any features disabled in the AppHost configuration.

When the browser is closed, it sends

POST /event-unregister?id=foo HTTP/1.1
Accept: */*

The request is properly handled by Servicestack and the unregister takes place. Once SS tries to generate (serialize) the response (which i guess the browser ignores anyway as its being closed) an exception is thrown, because it defaults to plain/text and there is no serializer configured.

Your provided commit should force it to be serialized as json. So we won’t have to make changes to the AppHost. Thanks a lot for the quick response & the fix. Much appreciated.

This uses the same Accept: */* as the request posted above, which returns the response in the default content type, which if not specified sets it to use JSON by default. I dislike the fix because it shouldn’t be necessary.

What does this print for you?

AfterInitCallbacks.Add(_ => 
  $"{nameof(Config.DefaultContentType)} = {Config.DefaultContentType}".Print());

DefaultContentType = application/json

ok, couldn’t tell you why it’s not returning the default content type if the request doesn’t specify one. I’m assuming there’s some other interference somewhere.

I’ll create a repro and get back to you.

1 Like