Skip to content
This repository has been archived by the owner on Sep 10, 2021. It is now read-only.

Commit

Permalink
Added better exception details
Browse files Browse the repository at this point in the history
- Moved to 0.2.1
- Added RpcException.Details which contains a full dump of the exception and inner exceptions (may be null)
  • Loading branch information
alandoherty committed May 22, 2019
1 parent d7af680 commit fbb79cf
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 44 deletions.
10 changes: 7 additions & 3 deletions samples/Example.General/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Test001 : ITest001
public async Task<string> Login(LoginRequestMsg login) {
Console.WriteLine($"Worker ({_uuid}) - Username: {login.Username} Password: {login.Password}");
RpcContext context = RpcContext.Current;

return "Wow";
}

Expand Down Expand Up @@ -85,8 +85,12 @@ static async Task AsyncMain(string[] args) {
Console.WriteLine();
}

// detaches the service
await TestNode.DetachAsync(service).ConfigureAwait(false);
var proxy = TestNode.Proxy<ITest001>("auth:login");
string a = await proxy.Login(new LoginRequestMsg()
{
Username = "wow",
Password = "wow"
});

await Task.Delay(50000);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Holon/Holon.csproj
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<Version>0.2.0</Version>
<Version>0.2.1</Version>
<Authors>Alan Doherty</Authors>
<Company>Alan Doherty</Company>
<Description>A minimal service and event bus with additional support for RPC</Description>
<Copyright>BattleCrate Ltd 2018</Copyright>
<PackageProjectUrl>https://github.com/alandoherty/holon-net</PackageProjectUrl>
<RepositoryUrl>https://github.com/alandoherty/holon-net</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<AssemblyVersion>0.2.1.0</AssemblyVersion>
<PackageLicenseUrl>https://github.com/alandoherty/holon-net/blob/master/LICENSE</PackageLicenseUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIconUrl>https://s3-eu-west-1.amazonaws.com/assets.alandoherty.co.uk/github/holon-net-nuget.png</PackageIconUrl>
<FileVersion>0.2.0.0</FileVersion>
<FileVersion>0.2.1.0</FileVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
18 changes: 9 additions & 9 deletions src/Holon/Remoting/RpcBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ private async Task ApplyAsync(RpcHeader header, Envelope envelope) {
try {
req = serializer.DeserializeRequest(envelope.Body, (i, o) => RpcArgument.FromMember(GetMember(i, o)));
} catch (KeyNotFoundException) {
res = new RpcResponse("InterfaceNotFound", "The interface or operation could not be found");
res = new RpcResponse("InterfaceNotFound", "The interface or operation could not be found", null);
} catch(Exception ex) {
res = new RpcResponse("ArgumentInvalid", string.Format("The request format is invalid: {0}", ex.Message));
res = new RpcResponse("ArgumentInvalid", string.Format("The request format is invalid: {0}", ex.Message), null);
}

// apply single request
Expand All @@ -90,7 +90,7 @@ private async Task ApplyAsync(RpcHeader header, Envelope envelope) {
if (req != null)
memberInfo = GetMember(req.Interface, req.Operation);
} catch (KeyNotFoundException) {
res = new RpcResponse("OperationNotFound", "The interface or operation could not be found");
res = new RpcResponse("OperationNotFound", "The interface or operation could not be found", null);
}

// get operation information
Expand All @@ -107,7 +107,7 @@ private async Task ApplyAsync(RpcHeader header, Envelope envelope) {

// check if they have a response ID if no reply isn't enabled
if (!noReply && envelope.ID == Guid.Empty)
res = new RpcResponse("InvalidOperation", "The envelope does not specify a correlation ID");
res = new RpcResponse("InvalidOperation", "The envelope does not specify a correlation ID", null);

// apply request if we don't have a response already, typically an error
if (res == null) {
Expand Down Expand Up @@ -192,7 +192,7 @@ private MemberInfo GetMember(string @interface, string operation) {
private async Task<RpcResponse> ApplyRequestAsync(RpcRequest req, MemberInfo member) {
// find interface behaviour
if (!_behaviours.TryGetValue(req.Interface, out Binding binding))
return new RpcResponse("InterfaceNotFound", "The interface binding could not be found");
return new RpcResponse("InterfaceNotFound", "The interface binding could not be found", null);

// get method info
MethodInfo operationMethod = member as MethodInfo;
Expand All @@ -205,7 +205,7 @@ private async Task<RpcResponse> ApplyRequestAsync(RpcRequest req, MemberInfo mem
for (int i = 0; i < methodArgs.Length; i++) {
if (!req.Arguments.TryGetValue(methodParams[i].Name, out methodArgs[i])) {
if (!methodParams[i].IsOptional)
return new RpcResponse("ArgumentRequired", string.Format("The argument {0} is not optional", methodParams[i].Name));
return new RpcResponse("ArgumentRequired", string.Format("The argument {0} is not optional", methodParams[i].Name), null);
}
}

Expand All @@ -216,9 +216,9 @@ private async Task<RpcResponse> ApplyRequestAsync(RpcRequest req, MemberInfo mem
methodResult = operationMethod.Invoke(binding.Behaviour, methodArgs);
await ((Task)methodResult).ConfigureAwait(false);
} catch (RpcException ex) {
return new RpcResponse(ex.Code, ex.Message);
return new RpcResponse(ex.Code, ex.Message, ex.ToString());
} catch (Exception ex) {
return new RpcResponse("Exception", ex.ToString());
return new RpcResponse("Exception", ex.Message, ex.ToString());
}

// check if the operation returns anything
Expand Down Expand Up @@ -416,7 +416,7 @@ public Task<InterfaceInformation> GetInterfaceInfo(string @interface) {

lock (_behaviour._behaviours) {
if (!_behaviour._behaviours.TryGetValue(@interface, out binding) || !binding.AllowIntrospection)
throw new RpcException("InterfaceNotFound", "The interface does not exist");
throw new RpcException("InterfaceNotFound", "The interface does not exist", null);
}

return Task.FromResult(binding.Introspection);
Expand Down
14 changes: 13 additions & 1 deletion src/Holon/Remoting/RpcError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public sealed class RpcError
#region Fields
private string _code;
private string _message;
private string _details;
#endregion

#region Properties
Expand All @@ -32,6 +33,15 @@ public string Message {
return _message;
}
}

/// <summary>
/// Gets the error details.
/// </summary>
public string Details {
get {
return _details ?? _message;
}
}
#endregion

#region Constructors
Expand All @@ -40,9 +50,11 @@ public string Message {
/// </summary>
/// <param name="code">The code.</param>
/// <param name="message">The message.</param>
internal RpcError(string code, string message) {
/// <param name="details">The details.</param>
internal RpcError(string code, string message, string details) {
_code = code;
_message = message;
_details = details;
}
#endregion
}
Expand Down
36 changes: 14 additions & 22 deletions src/Holon/Remoting/RpcException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class RpcException : Exception
{
#region Fields
private string _code;
private string _details;
#endregion

#region Properties
Expand All @@ -22,45 +23,36 @@ public string Code {
return _code;
}
}
#endregion

#region Constructors
/// <summary>
/// Creates a new RPC excpetion with the provided code and message.
/// Gets the details.
/// </summary>
/// <param name="code">The code.</param>
/// <param name="message">The message.</param>
public RpcException(string code, string message)
: base(message) {
_code = code;
public string Details {
get {
return _details;
}
}
#endregion

#region Constructors
/// <summary>
/// Creates a new RPC exception with the provided code, message and inner exception.
/// Creates a new RPC excpetion with the provided code and message.
/// </summary>
/// <param name="code">The code.</param>
/// <param name="message">The message.</param>
/// <param name="innerException">The inner exception.</param>
public RpcException(string code, string message, Exception innerException)
: base(message, innerException) {
/// <param name="details">The details.</param>
public RpcException(string code, string message, string details)
: base(message) {
_code = code;
_details = details;
}

/// <summary>
/// Creates a new RPC exception with the RPC error object.
/// </summary>
/// <param name="error">The error.</param>
public RpcException(RpcError error)
: this(error.Code, error.Message) {
}

/// <summary>
/// Creates a new RPC exception with the RPC error object and inner exception.
/// </summary>
/// <param name="error">The error.</param>
/// <param name="innerException">The inner exception.</param>
public RpcException(RpcError error, Exception innerException)
: this(error.Code, error.Message, innerException) {
: this(error.Code, error.Message, error.Details) {
}
#endregion
}
Expand Down
5 changes: 3 additions & 2 deletions src/Holon/Remoting/RpcResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ internal RpcResponse(object data, Type type) {
/// </summary>
/// <param name="code">The code.</param>
/// <param name="message">The message.</param>
internal RpcResponse(string code, string message) {
_error = new RpcError(code, message);
/// <param name="details">The details.</param>
internal RpcResponse(string code, string message, string details) {
_error = new RpcError(code, message, details);
_data = null;
}

Expand Down
8 changes: 6 additions & 2 deletions src/Holon/Remoting/Serializers/ProtobufRpcSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public RpcResponse DeserializeResponse(byte[] body, Type responseType) {
if (msg.IsSuccess)
return new RpcResponse(msg.Data.GetData(responseType), responseType);
else
return new RpcResponse(msg.Error.Code, msg.Error.Message);
return new RpcResponse(msg.Error.Code, msg.Error.Message, msg.Error.Details);
}
}

Expand Down Expand Up @@ -106,7 +106,8 @@ public byte[] SerializeResponse(RpcResponse response) {
} else {
res.Error = new ErrorMsg() {
Code = response.Error.Code,
Message = response.Error.Message
Message = response.Error.Message,
Details = response.Error.Details
};
}

Expand Down Expand Up @@ -281,5 +282,8 @@ class ErrorMsg

[ProtoMember(2, IsRequired = true)]
public string Message { get; set; }

[ProtoMember(3)]
public string Details { get; set; }
}
}
9 changes: 8 additions & 1 deletion src/Holon/Remoting/Serializers/XmlRpcSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,13 @@ public RpcResponse DeserializeResponse(byte[] body, Type dataType) {
// find the code and message
XElement errCode = err.Element(XName.Get("Code"));
XElement errMsg = err.Element(XName.Get("Message"));
XElement errDetails = err.Element(XName.Get("Details"));

if (errCode == null || errMsg == null)
throw new InvalidDataException("Invalid XML document, error missing Code and Message elements");

// get data
return new RpcResponse(errCode.Value, errMsg.Value);
return new RpcResponse(errCode.Value, errMsg.Value, errDetails == null ? null : errDetails.Value);
}
}
}
Expand Down Expand Up @@ -223,6 +224,12 @@ public byte[] SerializeResponse(RpcResponse response) {
writer.WriteEndElement();
writer.WriteStartElement("Message");
writer.WriteValue(response.Error.Message);

if (response.Error.Details != null)
{
writer.WriteStartElement("Details");
writer.WriteValue(response.Error.Details);
}
}

// finish document
Expand Down

0 comments on commit fbb79cf

Please sign in to comment.