Most 1.x services run unchanged. Read the first list before you build, and the second before you deploy next to existing clients.
- Session parameter. The session parameter is spelled
sessionIdeverywhere; a named argumentsessionID:must be updated. - MethodInfo names.
RpcMethod.FromMethodisFromMethodInfoandRpcInterfaceMethod.MethodisMethodInfo(both were new in the 2.0 preview). - Serializer.
JsonRpcProcessor.Process(…, JsonSerializerSettings)is gone from the core. UseConfig.SetSerializer(new NewtonsoftJsonRpcSerializer(settings))from the Newtonsoft package, or the helper overloads there that take the settings. - Overloads. The default-session string overloads that take a serializer take it first:
Process(serializer, json, context)andProcessSync(serializer, json, context).ProcessSync(sessionId, json, context, serializer)makescontextrequired, soProcessSync(json, null)still means the default session.ProcessandProcessAsyncdo not:Process(json, null)no longer compiles (it is ambiguous with theJsonRpcStateAsyncoverload), andProcessAsync(json, null)binds to the session overload withjsonas the session id and a null document, which throwsArgumentNullException. WriteProcess(json),Process(json, context: null)orProcessAsync(json, context: null). - DTOs.
JsonRequest,JsonResponseandJsonRpcExceptionare plain DTOs without Json.NET attributes.JsonRequest.Paramsis the active serializer's object model, so cast toJObject/JArrayonly when the Json.NET serializer is active. - SMD.
SMD.Servicesis anSMDServiceCollection(anIDictionary<string, SMDService>) instead of aDictionary<string, SMDService>, and its setter is gone. Every mutation through it updates the dispatch table at once, so a removed method is unreachable immediately.SMD.Typesis nowDictionary<int, Dictionary<string, object>>and a process-wide registry (it was reset whenever a session was created).
- Version member. The
jsonrpcmember is checked (Config.VersionPolicy, defaultLenient): a missing member is still accepted, but"jsonrpc":"1.0"or a non-string value is now-32600. SetIgnorefor the 1.x behaviour. - Parse errors. Requests nested deeper than 64 levels are
-32700(configurable per serializer, see Nesting depth). Invalid UTF-8 and non-strict JSON (unless the serializer is lenient) are-32700as well. - Batches. The empty-batch error code is the spec's
-32600(it was3200). Batches made only of notifications produce an empty response instead of[]with a dangling comma. A batch always answers with a JSON array when it produces at least one response; a one-request batch is no longer unwrapped to a bare response object. - Notifications. A notification (a request without an
id) never gets a wire response, whatever its outcome: method not found, binding failure or an exception in the method produce nothing on the wire (the error handler still runs server-side). An invalid request object is not a notification and still gets-32600with"id":null. - Exceptions. An unhandled exception is
-32603withdata: nullby default; 1.x sent the exception's type, message and stack trace.Config.IncludeExceptionDetails = truesends the full description; an error handler can author something in between. See Exception disclosure. - Conversion errors. A parameter value the serializer cannot convert (
"abc"for anint,"not-a-guid"for aGuid) is-32602withdata = {"reason":"conversion","parameter":…,"index":…,"expectedType":…}; it was-32603with the exception. An exception of the same type thrown inside the method is still-32603. A type the built-in serializer cannot handle at all stays-32603(now aNotSupportedException). - Method not found.
-32601'sdatais{"method":"<name>"}instead of the fixed sentence, and a method-not-found error for a notification now reaches the error handler (the wire still gets nothing). - Named parameters. They are checked against the method's parameter list: a supplied name that matches no parameter, or a name supplied twice, is
-32602(it used to be ignored, sooptional(int a = 9)called with{"typo":4}returned 9). Defaults fill only the names that are absent. - Dates and non-finite numbers.
DateTimeandDateTimeOffsetare written the way Json.NET writes them by every serializer (fraction only when non-zero,Z/offset/nothing byKind);NaNand the infinities are written as the quoted strings"NaN","Infinity","-Infinity"and read back from them.
- Async methods. Task-returning methods are supported again through
ProcessAsync, together withValueTaskandValueTask<T>. SynchronousProcess/ProcessSyncreject them at call time without invoking them.async voidremains rejected at registration. See Asynchronous methods and cancellation. - Request id. The invocation frame also carries the request id:
Handler.RpcRequestId()/JsonRpcContext.CurrentRequestId(),Handler.RpcRequestIdKind()andHandler.RpcRequestIdRaw(), see The request id. - Binding.
ServiceBinder.BindMethod(sessionId, name, delegate)registers any delegate; it refuses a name that is already registered, unlikeHandler.RegisterFuction, which keeps replacing silently. - Pre-process handlers. A pre-process handler may replace
JsonRequest.Method,ParamsorId; the replaced request is what gets dispatched (as in 1.x). Assign a newParamsvalue rather than editing the serializer's object model in place: a request the handler leaves untouched is dispatched straight from the request bytes. - Context.
JsonRpcContext.Current()/Handler.RpcContext()andJsonRpcContext.SetExceptionare per invocation: a method that synchronously processes another request throughJsonRpcProcessorgets its own context and exception state back afterwards. - Sessions. A request for a session id that was never registered no longer creates the session; it answers
-32601. Bind services or callHandler.GetSessionHandler(sessionId)before serving a session.Config.SetBeforeProcessHandler(sessionId, …)is nowConfig.SetPreProcessHandler(sessionId, …)(the old name still compiles, with an obsolete warning), andConfig.SetPostProcessHandler(sessionId, …)exists. JsonRpcService. The AspNetCore host binds a subclass to the configured session even when that is the default session; a subclass can passbase(false)to skip binding itself.Handler.Handle(JsonRequest)still works; it round-trips the request through the serializer and the boxed path.
Every change is recorded per version in the changelog.