I do like this approach, it is totally acceptable.
Take a look at the Flickr API. They are using error codes and error messages in their api response too.
http://www.flickr.com/services/api/
http://www.flickr.com/services/api/flickr.activity.userComments.html
(example api method)
Serving error codes and error messages instead of exceptions is imho much better when dealing with multiple (unknown) client frameworks (.net, java, etc..) If an client api-implementation is wrong because it is using a wrong format, it's much better to display the error message from the server ("Invalid date format. Must be ddMMyyy.") instead of getting an weird "FormatException" (+stacktrace).
Having error ids and error messages:
- improves api usability
- improves maintainability
- improves user / developer experience
API User(Developer) could reference easily to their problem and that will save your time. It would be much easier for you to track down a possible bug/taskswith a specific errorId/errorMessage instead of having "Hey, I'm getting those weird Exceptions when calling XYZ. What am I doing wrong?"
Edit based upon request:
Let's say we have a "BaseResponse" (similar to your APIResponse, comments are stripped out for better readability)
public class BaseResponse
{
public int ErrorId { get; set; }
public string ErrorMessage { get; set; }
public bool Successful { get; set; }
}
And we're having an GetDocumentResponse (implemented by DocumentService, see below)
public sealed class GetDocumentsResponse : BaseResponse
{
public IList<Document> Documents { get; set; }
}
DocumentService Contract
[ServiceContract]
public interface IDocumentService
{
[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
GetDocumentsResponse GetDocuments(string date);
}
DocumentService Implementation (simplified)
public GetDocumentsResponse GetDocuments(string date)
{
bool isFormatValid = // parse Date
if (!isFormatValid)
{
// Response = static class, provides generic responses and sets properties of response
return Response.ProvideFault<GetDocumentsResponse>(100, "The date-format is not valid. Valid: mmddyyyy");
// return new GetDocumentsResponse { Successful = false, ErrorId: 100, ErrorMessage = "The date-format is not valid. Valid: mmddyyyy"};
}
var documents = this.documentRepository.GetDocuments(...)
return new GetDocumentsResponse { Successful = true, Documents = documents };
}
So, let's say I'm a developer and I want to use this api.
1. - Making an valid api call & getting an valid response from server
(JSON Response)
{
"ErrorId":0,
"ErrorMessage":null,
"Successful":true,
"Documents":[
{
"DocumentId":13,
"Name":"xyz"
}
]
}
2. - Making an invalid api call & getting an invalid response from server
{
"ErrorId":100,
"ErrorMessage":"The date-format is invalid. Valid: mmddyyyy",
"Successful":false,
"Documents":[
]
}
So, for the first call I've done everything right.
The second call says clearly what is wrong, I do not have to research some .NET specific exceptions (or better: wcf faults) if I'm a java developer. In a few seconds after getting the response I do know what went wrong and why it went wrong. I do not have to research documentation or much worse to ask YOU about the current implementation of GetDocuments and why my date format is invalid. It saves you and me a lot of time.
Best Answer
You're looking for the Adapter Pattern. I always wrap an external API otherwise it leaks into the rest of the application (your problem with error codes is a good example).