我正在尝试构建一个可以动态调用类的成员函数并返回指定类型的通用扩展方法。对于某些背景,这是一般问题:
我正在使用 Autorest 为 swagger API 生成一些客户端库。因为 API 中的一些 GET 路由会根据响应的 HTTP 状态码返回不同的对象,所以方法调用会返回object
并且开发人员负责自己转换对象。我正在尝试创建一个方便的包装器来以通用方式进行此转换。
下面是一个典型的函数签名示例:
object IPet GetPets(string petName)
请注意,此方法可能会返回许多对象类型,具体取决于 HTTP 状态代码。例如,200 可能会返回一个Dog
对象,但 404 可能会返回一个Cat
对象。
这将通过 Autorest 生成的客户端库调用,如下所示:
AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
object pet = animalClient.Pet.GetPets("dog");
if(pet is Dog) {
Console.WriteLine("Dog!");
} else {
Console.WriteLine("Not Dog");
}
我想把这个手动铸造功能变成更直观的东西,这就是我的想法:
AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
string petType = "Dog";
Dog pet = animalClient.Pet.CallMethod<IPet, Dog, string>( (api,type) => api.GetPets(type), petType);
在这种情况下,除“Dog”类型的对象之外的任何返回都将导致引发异常。这是我的实施尝试:
public static Tout CallMethod<Tin, Tout>(this Tin client, Expression<Action<Tin, Targ>> apiCall, params object[] args)
where Tout : class {
MethodCallExpression providedMethod = apiCall.Body as MethodCallExpression;
if(providedMethod == null) {
throw new ApplicationException("Invalid method call provded");
}
var method = providedMethod.Method;
object responseData;
try {
// Call object-returning function
responseData = method.Invoke(client, args);
} catch(Exception error) {
if(error.InnerException is HttpOperationException) {
// Unknown error occurred
var ex = error.InnerException as HttpOperationException;
object content = JsonConvert.DeserializeObject(ex.Response.Content);
throw new ServiceException(ex.Response.StatusCode + ": "
+ JsonConvert.SerializeObject(content));
} else {
throw error.InnerException;
}
}
// Return formatted object if successful
if(responseData is Tout) {
return responseData as Tout;
// Otherwise throw
} else {
// Deal with known error object responses
if(responseData is ErrorResponse) {
var error = responseData as ErrorResponse;
throw new ServiceException(error);
} else {
// Unknown error occurred
throw new ServiceException("Unknown response was received: "
+ JsonConvert.SerializeObject(responseData));
}
}
}
我在这里遇到的问题是将函数和参数传递给通用扩展方法。在不知道各种 API 调用可能需要的各种可能数量的参数的情况下,我如何进行Expression<Action<Tin, Targ>>
一般定义?在我看来,我必须使用等来复制这个函数Expression<Action<T1, T2, T3>>
以适应不同长度的参数列表。
我希望人们以一种富有表现力的方式与 API 进行交互,以便很容易看到正在发生的事情。但是,这种机制应该对未来的各种 API 更改具有鲁棒性。我目前的目标是提供一种封装常见对象转换和错误检查操作的方法。有一个更好的方法吗?目前,我假设服务器端 swagger doc 无法更改。