何时抛出异常?何时处理异常(服务层,控制器)在哪里?

时间:2013-10-10 00:12:54

标签: c# exception

我改写了我的问题,因为我认为这太过于罗嗦,也许我想要实现的目标已经丢失了。

我在记事本中写了这个代码,所以它可能有错误,有些东西可能不太好,但它是为了说明我看到的选项。

// I wrap all code send back from service layer to controller in this class.
  public class ResponseResult
    {

        public ResponseResult()
        {
            Errors = new Dictionary<string, string>();
            Status = new ResponseBase();
        }

        public void AddError(string key, string errorMessage)
        {
            if (!Errors.ContainsKey(key))
            {
                Errors.Add(key, errorMessage);
            }
        }

        public bool IsValid()
        {
            if (Errors.Count > 0)
            {
                return false;
            }

            return true;
        }


        public Dictionary<string, string> Errors { get; private set; }


        public ResponseBase Status { get; set; }
    }

    public class ResponseResult<T> : ResponseResult
    {

        public T Response { get; set; }
    }

    public class ResponseBase
    {
        public HttpStatusCode Code { get; set; }
        public string Message { get; set; }
    }

选项1 (我现在使用的是什么)

//controller
    public HttpResponseMessage GetVenue(int venueId)
            {
                if (venueId == 0)
                {
                    ModelState.AddModelError("badVenueId", "venue id must be greater than 0");

                if (ModelState.IsValid)
                {
                    var venue = venueService.FindVenue(venueId);
                    return Request.CreateResponse<ResponseResult<Venue>>(venue.Status.Code, venue);
                }

                // a wrapper that I made to extract the model state and try to make all my request have same layout.
                var responseResult = new ResponseResultWrapper();
                responseResult.Status.Code = HttpStatusCode.BadRequest;
                responseResult.Status.Message = GenericErrors.InvalidRequest;
                responseResult.ModelStateToResponseResult(ModelState);

                return Request.CreateResponse<ResponseResult>(responseResult.Status.Code, responseResult);
            }

// service layer        
             public ResponseResult<Venue> FindVenue(int venueId)
            {
                ResponseResult<Venue> responseResult = new ResponseResult<Venue>();

                try
                {
                    // I know this check was done in the controller but pretend this is some more advanced business logic validation.
                    if(venueId == 0)
                    {
                       // this is like Model State Error in MVC and mostly likely would with some sort of field.
                       responseResult.Errors.Add("badVenueId", "venue id must be greater than 0");
                       responseResult.Status.Code = HttpStatusCode.BadRequest;
                    }

                    var venue = context.Venues.Where(x => x.Id == venueId).FirstOrDefault();

                    if(venue == null)
                    {
                        var foundVenue = thirdPartyService.GetVenue(venueId);

                        if(foundVenue == null)
                        {
                           responseResult.Status.Code = HttpStatusCode.NotFound;
                           responseResult.Status.Message = "Oops could not find Venue";

                           return responseResult;
                        }
                        else
                        {
                           var city = cityService.FindCity(foundVenue.CityName);

                           if(city == null)
                           { 
                              city = cityService.CreateCity(foundVenue.CityName);

                              if(city.Response == null)
                              {
                                 responseResult.Status.Code = city.Status.Code;
                                 responseResult.Status.Message = city.Status.Message;

                                 return responseResult;
                              }

                              CreateVenue(VenueId, city.Response, foundVenue.Name);

                               responseResult.Status.Code = HttpStatusCode.Ok;
                               // I don't think I would return a success message here as the venue being displayed back to the user should be good enough.
                               responseResult.Status.Message = "";

                               reponseResult.Response = foundVenue;
                           }
                        }

                        return responseResult;
                    }

                }
                catch (SqlException ex)
                {
                    ErrorSignal.FromCurrentContext().Raise(ex);
                    responseResult.Status.Code = HttpStatusCode.InternalServerError;
                    responseResult.Status.Message = GenericErrors.InternalError;

                    // maybe roll back statement here depending on the method and what it is doing.
                }
               // should I catch this, I know it should be if you handle it but you don't want nasty messages going back to the user.
                catch (InvalidOperationException ex)
                {
                    ErrorSignal.FromCurrentContext().Raise(ex);
                    responseResult.Status.Code = HttpStatusCode.InternalServerError;
                    responseResult.Status.Message = GenericErrors.InternalError;
                }
               // should I catch this, I know it should be if you handle it but you don't want nasty messages going back to the user.
                catch (Exception ex)
                {
                    ErrorSignal.FromCurrentContext().Raise(ex);
                    responseResult.Status.Code = HttpStatusCode.InternalServerError;
                    responseResult.Status.Message = GenericErrors.InternalError;
                }

                return responseResult;
            }

// another service layer. 

        // it is ResponseResult<City> and not city because I could have a controller method that directly calls this method.
            // but I also have a case where my other method in another service needs this as well.
            public ResponseResult<City> CreateCity(string CityName)
            {
               ResponseResult<City> responseResult = new ResponseResult<City>();
               try
               {
                   City newCity = new City {  Name = "N" };
                   context.Cities.Add(newCity);
                   context.SaveChanges();

                    responseResult.Status.Code = HttpStatusCode.Ok;
                    responseResult.Status.Message = "City was succesfully added";
               }           
               // same catch statmens like above
               catch (SqlException ex)
                {
                    ErrorSignal.FromCurrentContext().Raise(ex);
                    responseResult.Status.Code = HttpStatusCode.InternalServerError;
                    responseResult.Status.Message = GenericErrors.InternalError;

                    // maybe roll back statement here depending on the method and what it is doing.
                }
                return responseResult;
            }

正如您所看到的那样,这些方法都包含在状态代码中,因为它们可以由公共控制器直接调用。 FindCity()和CreateVenue()也可能有这种包装。

选项2

   public HttpResponseMessage GetVenue(int venueId)
        {
            try
            {
                if (venueId == 0)
                {
                    ModelState.AddModelError("badVenueId", "venue id must be greater than 0");

                if (ModelState.IsValid)
                {
                    var venue = venueService.FindVenue(venueId);
                    return Request.CreateResponse<ResponseResult<Venue>>(HttpSatusCode.Ok, venue);
                }

                // a wrapper that I made to extract the model state and try to make all my request have same layout.
                var responseResult = new ResponseResultWrapper();
                responseResult.Status.Code = HttpStatusCode.BadRequest;
                responseResult.Status.Message = GenericErrors.InvalidRequest;
                responseResult.ModelStateToResponseResult(ModelState);

                return Request.CreateResponse<ResponseResult>(responseResult.Status.Code, responseResult);
            }
            catchcatch (SqlException ex)
            {
               // can't remember how write this and too tried to look up.
               return Request.CreateResponse(HttpStatusCode.InternalServerError;, "something here");
            }
        }

 public Venue FindVenue(int venueId)
        {
            try
            {
                // how to pass back business logic error now without my wrapper?
                if(venueId == 0)
                {
                   // what here?
                }

                var venue = context.Venues.Where(x => x.Id == venueId).FirstOrDefault();

                if(venue == null)
                {
                    var foundVenue = thirdPartyService.GetVenue(venueId);

                    if(foundVenue == null)
                    {
                       // what here?
                    }
                    else
                    {
                       var city = cityService.FindCity(foundVenue.CityName);

                       if(city == null)
                       { 
                          city = cityService.CreateCity(foundVenue.CityName);

                          if(city  == null)
                          {
                             // what here?
                          }

                          CreateVenue(VenueId, city.Response, foundVenue.Name);


                       }
                    }

                    return venue;
                }

            }
            catch (SqlException ex)
            {
                // should there be a try catch here now? 
                // I am guessing I am going to need to have this here if I need to do a rollback and can't do it in the controller

                // throw exception here. Maybe this won't exist if no rollback is needed.
            }
            return null;
        }

        public City CreateCity(string CityName)
        {
           // if it crashes something I guess will catch it. Don't think I need to rollback here as only one statement being sent to database.
            City newCity = new City {  Name = "N" };
            context.Cities.Add(newCity);
            context.SaveChanges();

            return newCity;            
        }

正如您在选项2中看到的那样,我可能仍然需要将其包装在try catch中以进行回滚,我不知道如何处理高级业务验证。

另外在捕捉控制器中的所有内容并发回香草对象(没有我的包装)时,我不确定如何做细粒HttpStatus代码(比如notFound,Create等)

6 个答案:

答案 0 :(得分:1)

对于简短的回复感到抱歉,但这是我的一般规则 - 如果发生了您可能发生的异常,请处理它 - 通过重试或告诉用户出错的地方并给予他们修复它的选项。

如果发生了意外的异常,如果你可以处理它(例如你可以重试的超时)尝试处理它,否则就出去 - 只要想想任何MS应用程序做什么 - 例如办公室 - 你得到一个道歉,出了问题,应用程序结束了。最好是优雅地结束而不是潜在地破坏数据并使事情陷入困境。

答案 1 :(得分:0)

This是一篇关于Java特定概念和示例的文章,但这里的广泛原则是要走的路。

区分灾难性和不可恢复的故障异常,以及非常可恢复的意外异常。让故障“冒泡”到你正确处理的断层屏障。例如,您可能会记录错误,向某人发送电子邮件或将消息发送到消息队列,并向用户显示一个信息丰富的错误页面。

无论您做什么,请务必保留来源的所有异常信息。

希望有所帮助。

答案 2 :(得分:0)

只要您的代码确定某些内容出错,就会抛出异常。

您始终需要处理最终用户直接调用的方法中的异常。这是为了迎合您的代码没有具体处理的意外错误。您的通用处理代码通常会记录错误,可能包括也可能不包括让用户知道发生了意外错误。

但是,如果您提前遇到错误,您通常会希望在代码中处理这些较低的错误,接近它们发生的位置,以便您的应用程序可以从错误中“恢复”并继续。

答案 3 :(得分:0)

我认为,只要您需要从方法返回失败的详细信息,同时能够为您正在调用的方法使用理想的返回类型,异常就很有用。

你在问题​​中说:

  

现在对我来说,我尝试将错误消息返回给控制器   并尽量不要在控制器中捕捉任何东西。

如果服务方法理想情况下应该返回Venue对象,那么如何将此潜在错误消息返回给控制器?一个out参数?将返回类型更改为具有错误消息属性的内容?

如果您正在执行这些选项中的任何一个,我认为您正在重新发明轮子...即创建一种在已存在的情况下返回异常信息的方法。

最后,例外是对出错的强类型表示。如果您返回错误消息,那么发送回用户就可以了,但如果您需要根据错误的详细信息以编程方式执行不同的操作,那么您不希望打开魔术字符串。

例如,区分授权错误和未找到错误是不是很方便,以便您可以向用户返回最合适的http状态代码?

不要忘记,Exception类有一个Message属性,如果你想以这种方式使用它,你可以简单地返回给用户

答案 4 :(得分:0)

为了确保我理解这个问题,您正在创建一个Web服务,并想知道何时处理以及何时抛出异常。

在这种情况下,我强烈建议您捕获所有异常。 “未处理”异常是非常糟糕的形式。在网站上,通过公开您不希望公众看到的内部信息,他们会产生从无意义到危险的显示。

如果这是一个大小合适的程序,我建议您创建自己的MyException类,该类派生自System.Exception。这样做的目的是为您提供一个特定于您的应用程序的附加信息。以下是我想添加到MyException类的一些典型内容:

  1. 一个ID号,可帮助我找到问题发生的代码中的位置。
  2. 一种“LogMessage”方法,可以将异常记录到Windows事件日志中。是否记录以及您编写的日志取决于您要记录的内容以及情况的严重程度。
  3. 已记录显示异常的指示符,因此即使多次调用上述方法,上述方法也不会记录两次。
  4. 根据具体情况可能有用的任何其他内容。
  5. 我还想将消息文本放在外部资源文件中,比如XML文档,并将它们键入您指定的错误号。这允许您更改错误文本以提高清晰度,而无需重新部署应用程序。
  6. 捕获所有异常并创建MyException类型的新实例,并将原始异常放入内部异常属性中。在我的应用程序的第一级下面,我总是抛出一个MyException实例而不是原始异常。

    在顶层(应用程序级别),永远不要让异常处理,永远不要抛出自己的异常。更好的方法是在数据协定中返回错误代码和消息。这样客户端应用程序将只获得您希望他们看到的内容。他们需要担心的唯一例外是您的范围之外的那些例外,即配置错误或通信失败。换句话说,如果他们能够调用您的服务并且网络保持连接,您应该给他们一个他们可以解释的响应。

    希望这有帮助。

    PS我没有包含示例异常,因为我确信有一点搜索会发现很多。如果你想让我提出一个简单的样本,请发帖。

答案 5 :(得分:-2)

在各个级别使用try catch并将其冒泡。 (可选)将错误记录在文件或数据库中。我使用文本文件 - 制表符分隔。在每个级别捕获 1.模块名称(使用C#提供的方法来获取此信息) 2.方法名称 3.执行代码(用户创建 - “连接到数据库”) 4.错误号码 5.错误说明 6.执行代码(用户创建 - “访问数据库”) 7.最终用户的错误号 8.最终用户的错误描述 此外,我还传递了一个唯一的标识符,如Web上的会话ID,登录用户ID,用户名(如果有)

我总是有Exception catch块。在这里,我将错误号设置为-0,将异常对象的消息设置为错误描述。如果它与SQL Server相关 - 我捕获SQL异常。这会生成错误编号 - 我使用它。

我想更多地延伸一下。