在 WebApi 方法中出现异常时向最终用户返回错误消息

Returning an error message to an end user upon exception in WebApi methods

我目前正在构建一个 WebApi 服务,该服务与我的应用程序的后台连接,允许用户单击一个按钮并触发一系列 Web 服务请求,这些请求会触发我的 Umbraco 网站更新内容。

我的 WebApi 服务的 class 设置如下:

public class MyController : UmbracoApiController{
     // Global variable declarations go here

     private MyController(){
          // assign actual values to global variables here
     }

     public string GetAllUpdates(){
          try{
              GetCountries();
              GetAreas();
              GetCities();
              ...
          }
          catch (TimeoutException ex)
          {
               Log.Error("Web Service Timeout", ex);
               return "Failed to contact web services.";
          }
          catch (Exception ex)
          {
               Log.Error("Exception", ex);
               return "An error has occurred.";
          }
     }

     public string GetCountries(){
         try{
               // Main code here
         }
         catch(TimeoutException ex){
           Log.Error("Web Service Timeout", ex);
           return "Failed to contact web services.";
         }
         catch(Exception ex){
           Log.Error("Exception", ex);
           return "An error has occurred.";
         }
     }

     .....
}

My WebApi 方法是通过使用 AngularJS 和 HTML 调用的。当用户单击与 Web Api 方法相关的按钮时,该方法是 运行,然后在方法成功或失败时 return 向用户发送一条消息。

所以,如果我要 运行 使用相关按钮的 GetCountries() 方法,屏幕上会显示一条消息,说 "Countries updated successfully" 成功,或者它会输出其中一条消息在捕获定义中 returned。

我的主要问题是 GetAllUpdates() 方法 运行 一个接一个地包含所有其他方法。这很好,我 return 一条成功的消息说 "All content updated" 但是当在其他方法之一中遇到异常时,设置真的崩溃了。例如,GetCountries() 首先是 运行。如果此处出现异常,则不会 return 将任何消息发送到屏幕,应用程序只会转到下一个方法 GetAreas()。这会传播到最后,然后显示成功消息,给用户一种更新顺利进行的错误印象。

因此,我的问题是,如果发生异常而不修改其他方法中的 returns,我如何从链中的 运行ning 后续方法停止 GetAllUpdates() 方法?这些方法需要 return 一个字符串,以便消息在它们自己 运行 时显示在屏幕上,但是当 运行 作为 GetAllUpdates() 的一部分时,错误消息是 returned 在 catch 块中被视为方法完全成功并且用户 none 更聪明。

如有任何帮助,我们将不胜感激。

N.B。在遵循以下其中一项回复后,我更改了我的 WebApi 方法以执行以下操作:

新 CLASS

public class ResponseMessage
    {
        public bool Result { get; set; }
        public string Message { get; set; }
    }

RETURN 关于方法

            ResponseMessage response = new ResponseMessage();
            String json = string.Empty;

            response.Result = true;
            response.Message = "Content update successful";

            json = JsonConvert.SerializeObject(response);
            return json;

我的Angular代码如下:

 angular.module("umbraco")
.controller("AxumTailorMade",
function ($scope, $http, AxumTailorMade) {
    $scope.getAll = function() {
        $scope.load = true;
        $scope.info = "Retreiving updates";
        AxumTailorMade.getAll().success(function (data) {
            var response = JSON.parse(data);
            $scope.result = response.Result;
            $scope.info = response.Message;
            $scope.load = false;
        });
    };

  });

angular.module("umbraco.services").factory("AxumTailorMade", function ($http) {
    return {
        getAll: function () {
            return $http.get("/umbraco/api/axumtailormade/getallupdates");
        },
        getRegions: function () {
            return $http.get("/umbraco/api/axumtailormade/getregions");
        },
        getCountries: function () {
            return $http.get("/umbraco/api/axumtailormade/getcountries");
        },
        getAreas: function () {
            return $http.get("/umbraco/api/axumtailormade/getareas");
        },
        getCities: function () {
            return $http.get("/umbraco/api/axumtailormade/getcities");
        },
        getCategories: function () {
            return $http.get("/umbraco/api/axumtailormade/getcategories");
        },
        getPackages: function () {
            return $http.get("/umbraco/api/axumtailormade/getpackages");
        },
        getHotels: function () {
            return $http.get("/umbraco/api/axumtailormade/gethotels");
        },
        getActivities: function () {
            return $http.get("/umbraco/api/axumtailormade/getactivities");
        }
    };
})

而不是 return 从您的方法中获取字符串,您可以 return 一个包含客户端结果和消息的简单对象吗?

public class ResponseMessage
{
    public bool Result { get; set; }
    public string Message { get; set; }
}

然后在执行 GetAllUpdates() 时,您将检查 GetCountries()GetAreas()GetCities() 的结果并附加任何错误消息。

public ResponseMessage GetAllUpdates()
{
    ResponseMessage response;

    response = GetCountries();
    if (!response)
    {
        return response;
    }

    response = GetAreas();
    if (!response)
    {
        return response;
    }

    response = GetCities();
    if (!response)
    {
        return response;
    }

    response.Result = true;
    response.Message = "Successful";

    return response;
}

public ResponseMessage GetCountries()
{
    var response = new ResponseMessage();

    try
    {
        // Your main code here, call services etc

        response.Result = true;
        response.Message = "GetCountries successful";
    }
    catch (TimeoutException ex)
    {
        Log.Error("Web Service Timeout", ex);
        response.Result = false;
        response.Message = "Failed to contact web services.";
    }
    catch (Exception ex)
    {
        Log.Error("Exception", ex);
        response.Result = false;
        response.Message = "An error has occurred.";
    }

    return response;
}

它需要更多的努力和代码,但也会让您更好地控制流程以及 return 发送给客户端的内容。您还可以对其进行很多优化,我敢肯定,我只是为您提供了一个通用方法...

You can update your `GetAllUpdate` method something like this

public string GetAllUpdates(){
          try{
              var result = GetCountries();

              if(string.isNullOrEmpty(result)
               return result;


              result  = GetAreas();

              if(string.isNullOrEmpty(result)
               return result;

              result  = GetCities();

              if(string.isNullOrEmpty(result)
               return result;

              ...

              result = "All updated successfully";

              return result;

          }
          catch (TimeoutException ex)
          {
               Log.Error("Web Service Timeout", ex);
               return "Failed to contact web services.";
          }
          catch (Exception ex)
          {
               Log.Error("Exception", ex);
               return "An error has occurred.";
          }
     }

我建议你实际上摆脱 GetAllUpdates() 方法,并从 AngularJS 中单独调用特定的 Get****() 方法。

这会有以下优点:

  1. 您可以并行执行这些方法(更好的性能),并单独处理 return 值(或可能的错误消息)。
  2. 据我了解 - GetCountries() 失败并不意味着 GetAreas() 应该失败或不应该执行;只是 GetAllUpdates() 方法需要这个。 因此,您可以独立处理每个错误,同时让尽可能多的方法成功。

如果您不want/need并行调用特定的方法,在我看来,一个接一个地调用它们会更好,将对每个后续方法的调用挂在其前任的承诺上(参见 "Chaining Promises" 此处:https://docs.angularjs.org/api/ng/service/$q)

最后 - 我不会 return 来自 WebAPI 方法的字符串。相反 - 你应该 return 一个 HTTP 200 代码来表示成功(即使方法无效,并且不做任何特殊的事情),或者一个特定的 HTTP 代码来表示发生的任何错误。 在 Angular 中,您可以使用 .catch() 来处理异常。这样您的消息将不会在 WebAPI 代码中决定 - 而是在客户端。

How do you return status 401 from WebAPI to AngularJS and also include a custom message?

编辑:

如果客户端选项不可行,我将从服务器代码中的嵌套 try/catch 块进行调用:

 public string GetAllUpdates()
 {
      try
      {
          GetCountries();
          try 
          {
              GetAreas();
              // ... and so on
          }
          catch(Exception ex)
          {
              return "Error getting areas";
          }

      }
      catch(Exception ex) 
      {
          return "Error getting countries";
      }

或者,您可以将调用和相应的错误消息放入列表中:

var calls = new List<Action> { GetCountries, GetAreas, Get... };
var errorMessages = new List<string> { "Error getting countries", "Error getting areas", "Error getting ..."} ;

然后在for循环中调用方法:

for(var i = 0; i < calls.Count; i++) 
{
    try 
    {
        var call = calls[i];
        call();
    }
    catch(Exception ex) 
    {
        return errorMessages[i];
    } 
}

有道理吗? (抱歉,如果代码无法编译,不幸的是我没有在编译器中检查它)