如何使用 Web 服务正确抛出和处理异常
Posted
技术标签:
【中文标题】如何使用 Web 服务正确抛出和处理异常【英文标题】:How to properly throw and handle exceptions with web services 【发布时间】:2015-08-11 13:03:00 【问题描述】:我在我的应用程序服务器中遵循 MVC 模式。我试图抛出一个异常,但它没有被正确抛出。
这是控制器的代码:
[HttpPost]
public IHttpActionResult PostAddSuperUser(SuperUserViewModel SU)
try
//more code
blHandler.addSuperUser((SuperUser)SU.user, SU.Password);
return Ok();
catch (EUserAlreadyExist ex)
var resp = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
resp.Content = new StringContent(string.Format("Already exist ", SU.user.Mail));
resp.ReasonPhrase = "Already exist";
throw new HttpResponseException(resp);
然后客户端调用如下:
try
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:50687/");
HttpResponseMessage response = await client.PostAsJsonAsync<SuperUserViewModel>("/api/SuperUser/PostAddSuperUser", SUVM);
if (response.IsSuccessStatusCode)
new SuccesPupUp("", "SuperUser " + SupUserName + " was added");
this.Close();
catch (HttpResponseException Exc)
new ErrorPopUp("", Exc.Message);
根据这个this,我正确地抛出了它,但是当我运行它时,我得到了这个错误
我该如何解决?
编辑:我想向客户端抛出异常,以便它可以要求用户提供新的电子邮件地址
【问题讨论】:
如您所见,它会抛出您没有捕获的HttpResponseException
,只需添加第一个捕获 - catch(HttpResponseException ex)
由于您询问的是“正确处理异常”,请注意使用 Exception 后缀而不是前缀“E”来命名异常是“最佳实践”。例如,EInvalidPassword
应该命名为 InvalidPasswordException
,是的,它更长,但更明显的是它是什么。
在catch()
中你必须处理HttpResponseException
,而不是EUserAlredyExist
,因为你不会抛出EUserAlredyExist
异常。
这个问题与***.com/questions/19287426/…有关。如果您从一个 catch 块中抛出一个异常,并且在该块之外没有 catch 块,则该异常是未处理的。
@DanielPetrovaliev,他的 blHandler.addSuperUser() 方法可能会抛出 EUserAlreadyExist
异常,这就是他捕捉它的原因。
【参考方案1】:
问题出在这段代码中(看起来很明显,但请耐心等待):
catch (EUserAlreadyExist ex)
var resp = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
resp.Content = new StringContent(string.Format("Already exist ", SU.user.Mail));
resp.ReasonPhrase = "Already exist";
throw new HttpResponseException(resp);
发生的情况是,您从上述尝试中捕获了 EUserAlreadyExist 异常,然后抛出了一个新异常,而没有伴随的 try-catch
块。所以这意味着仅仅因为你在 catch 中抛出了一个异常,它不会自动捕捉它,你必须在你的 catch 中有一个单独的 try-catch
更进一步,对此try-catch
的客户端调用也不会捕获抛出的异常,因为它们(catch
语句)捕获的异常类型与抛出的异常类型不同。
要修复它,您需要捕获在这种情况下引发的异常 HttpResponseException
异常。所以这意味着你的代码可能看起来像这样:
catch (EUserAlreadyExist ex)
try
var resp = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
resp.Content = new StringContent(string.Format("Already exist ", SU.user.Mail));
resp.ReasonPhrase = "Already exist";
throw new HttpResponseException(resp);
catch(HttpResponseException ex2)
//do something here with the exception
【讨论】:
这好像不太对劲,为什么要抛出异常然后马上捕获呢? 根据@Rodrigo 的问题和代码,这个答案在技术上应该可行,但我同意以这种方式抛出异常没有多大意义。 @Pseudonym 我想在服务器端使用我的 try catch 所做的就是将异常抛出到客户端,那么捕获我自己的异常将如何帮助我做到这一点?如我所见,如果我添加一个新的 try-catch,我会再次捕获它,但我仍然不知道如何处理它或如何将其发送到客户端【参考方案2】:服务器端代码不需要捕获异常并重新抛出它 - 有什么意义?让它冒泡给客户。这就是例外的美妙和力量。问题是 MVC 想要返回一个视图或一个 HTTP 状态码,而不是丰富的强类型异常信息。
您似乎在富客户端(客户端-服务器)情况下使用 MVC 模式,这没有任何意义。 MVC 最适合瘦客户端情况,服务器提供视图,客户端只呈现它们。当你有一个富客户端时,MVVM 模式更有意义。服务器可以提供视图模型(数据),客户端可以接管所有的用户交互、视图等。
考虑使用 MVVM 方法(使用 WCF)而不是 MVC。那么客户端和服务端都会对可以抛出的异常类型有一个共同的认识。服务器可以抛出,客户端可以捕捉,很简单。
您的客户端代码将如此简单:
try
c.AddSuperUser(SUVM);
catch (Exception e)
// etc
其中 c 是 WCF 客户端代理。
【讨论】:
以上是关于如何使用 Web 服务正确抛出和处理异常的主要内容,如果未能解决你的问题,请参考以下文章