在Web Api方法中异常时向最终用户返回错误消息

时间:2015-02-03 11:54:22

标签: c# angularjs asp.net-web-api try-catch

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

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

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.";
         }
     }

     .....
}

通过使用AngularJS和HTML调用我的WebApi方法。当用户单击与Web Api方法相关的按钮时,将运行该方法,然后在方法成功或失败时将消息返回给用户。

因此,如果我使用相关按钮运行GetCountries()方法,屏幕上会显示一条消息,表示“国家已成功更新成功”,或者输出其中一条消息定义

我的主要问题是GetAllUpdates()方法一个接一个地运行所有其他方法。这很好,我在成功时返回一条消息,说“所有内容都已更新”,但是当其他方法中出现异常时,设置确实崩溃了。例如,首先运行GetCountries()。如果此处引发异常,则不会向屏幕返回任何消息,应用程序只会移动到下一个方法GetAreas()。这会传播到最终显示成功消息的结尾,从而给用户一个错误的印象,即更新进展顺利。

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

非常感谢任何帮助。

N.B。在完成下面的一个响应之后,我改变了我的WebApi方法来执行以下操作:

NEW CLASS

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

返回方法

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

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

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

我的角色代码如下:

 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");
        }
    };
})

3 个答案:

答案 0 :(得分:2)

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

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;
}

这需要更多的努力和代码,但也会让您更好地控制流量以及返回给客户端的内容。你也可以对它进行很多优化我很确定,我只是为你找到了一个通用的方法......

答案 1 :(得分:0)

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.";
          }
     }

答案 2 :(得分:0)

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

这将具有以下优点:

  1. 您可以并行执行这些方法(更好的性能),并分别处理返回值(或可能的错误消息)。
  2. 据我所知 - GetCountries()失败的事实并不意味着GetAreas()应该失败或不应该被执行;它只是需要这个的GetAllUpdates()方法。 因此,您可以独立处理每个错误,同时让尽可能多的方法成功。
  3. 如果你不想/需要并行调用特定的方法,在我看来,一个接一个地调用它们仍然会更好,将其调用挂在其前一个承诺的后续版本上(参见“链接承诺”https://docs.angularjs.org/api/ng/service/ $ q)

    最后 - 我不会从WebAPI方法返回一个字符串。相反 - 您应该返回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];
        } 
    }
    

    有意义吗? (对不起,如果代码没有编译,我不会在编译器中检查它)