Encrypted client causing exception with Delete

I’ve got some code that used the encrypted C# client and it works absolutely fine with Get/Post etc requests, no issues returning data. However, when the exact same code uses a Delete request, I get the following exception:

System.ArgumentNullException: Value cannot be null. (Parameter ‘s’)
at System.Convert.FromBase64String(String s)
at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, Object request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 62
at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, IReturn1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 82 at ServiceStack.ServiceClientExtensions.Delete[TResponse](IEncryptedClient client, IReturn1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 227
at MyService.Delete(DeleteRequest request) in /Users/ikaney/Development/MyService/ServiceInterface/MyService.cs:line 297

If you need any further info, let me know. Thanks.

Yeah can you please provide a Request DTO this fails on and the client code you’re using to send the Request DTO?

DTO is slightly strange, I had to return something otherwise I couldn’t compile. I’ve had to change names where necessary.

[DataContract]
[Route("/test/{Id}", "DELETE")]    
public class DeleteRequest : IReturn<bool>
{
     [DataMember(Order = 1)] public string Id { get; set; }
}

Client code, I’ve had to change some names:

var help = TryResolve<AppConfig>().Current.Value["serviceKey"];
// setup encrypted json service client                
var client = new JsonServiceClient(TryResolve<AppConfig>().Current.Value["serverAddress"]);
IEncryptedClient encryptedClient = client.GetEncryptedClient(
System.Text.Encoding.UTF8.GetString(
Convert.FromBase64String(TryResolve<AppConfig>().Current.Value["serviceKey"])));
try {
	var test = encryptedClient.Delete(new DeleteRequest {
		Id = request.Id
	});
} catch (WebServiceException webEx) {
	throw new HttpError(HttpStatusCode.ServiceUnavailable, "Error1");
} catch (Exception ex) {
	throw new HttpError(HttpStatusCode.ServiceUnavailable, "Error2");
}

Yeah you should only return reference types in services. I believe Encrypted Messaging has a further restriction in requiring Response DTOs. Can you either change your Service to return a Response DTO like EmptyResponse or void and change your Request DTO to implement IReturnVoid.

I tried IReturnVoid initially, but I can’t compile the service, I get the error (on the .Delete line)

(typed out so may have typos)
The type arguments for method 'TResponse ServiceStack.ServiceClientExtensions.Delete<TResponse>(this IEncryptedClient, IReturn<TResponse>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

When compiling I get:

MyService.cs(297, 40): [CS1929] 'IEncryptedClient' does not contain a definition for 'Delete' and the best extension method overload 'OrmLiteWriteApi.Delete<DeleteRequest>(IDbConnection, DeleteRequest)' requires a receiver of type 'IDbConnection'

Yeah the IEncryptedClient API and all its REST API overloads all expect a Response DTO, so use EmptyResponse or you can create your own Wrapper Response DTO around your bool result.

Made the changes, however I’m still seeing an exception occur:

System.ArgumentNullException: Value cannot be null. (Parameter ‘s’)
at System.Convert.FromBase64String(String s)
at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, Object request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 62
at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, IReturn `1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 82 at ServiceStack.ServiceClientExtensions.Delete[TResponse](IEncryptedClient client, IReturn` 1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 227
at MyService.Delete(DeleteRequest request) in /Users/ikaney/Development/MyService/ServiceInterface/MyService.cs:line 297

Just for clarity, I don’t need a return bool result since I usually return the ‘nocontent’ status for delete’s, I just did a bool to get it to compile.

So what’s an exact Request DTO I can use to repro this issue?

The revised one after your comments is:

[DataContract]
[Route("/test/{Id}", "DELETE")]    
public class DeleteRequest : IReturn<EmptyResponse>
{
     [DataMember(Order = 1)] public string Id { get; set; }
}

The code runs fine on the server that this request triggers, here it is for completeness:

public async void Delete(DeleteRequest request)
{
     await ServerEvents.UnRegisterAsync(request.Id);
}

The Service needs to return what it says it does in IReturn<T>, e.g. can you change it to:

public async Task<object> Delete(DeleteRequest request)
{
     await ServerEvents.UnRegisterAsync(request.Id);
     return new EmptyResponse();
}

Note you should never use async void, use async Task for void async methods instead.

Ok, made those changes, getting a slightly different exception now:

System.NullReferenceException: Object reference not set to an instance of an object.
   at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, Object request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 62
   at ServiceStack.EncryptedServiceClient.Send[TResponse](String httpMethod, IReturn`1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 82
   at ServiceStack.ServiceClientExtensions.Delete[TResponse](IEncryptedClient client, IReturn`1 request) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack.Client\EncryptedServiceClient.cs:line 227
   at MyService.Delete(DeleteRequest request) in /Users/ikaney/Development/MyService/MyService.cs:line 297

Wasn’t able to repro it in this commit.

Are you using the latest ServiceStack version?

I’m using the latest released version 5.8.0.

Not sure then, can you confirm it still fails with await Task.Yield() or await Task.Delay(1) so we can rule out the ServerEvents API call.

Found the issue in my code, thank you so much for your time.

In my delete calls I’ve tried to make them behave as per other REST solutions and use the following line of code to get the right status code:
base.Response.StatusCode = (int)HttpStatusCode.NoContent;

This causes the exception in the client.

1 Like

ok great, glad you’ve resolved it.