我改写了我的问题,因为我认为这太过于罗嗦,也许我想要实现的目标已经丢失了。
我在记事本中写了这个代码,所以它可能有错误,有些东西可能不太好,但它是为了说明我看到的选项。
// 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等)
答案 0 :(得分:1)
对于简短的回复感到抱歉,但这是我的一般规则 - 如果发生了您可能发生的异常,请处理它 - 通过重试或告诉用户出错的地方并给予他们修复它的选项。
如果发生了意外的异常,如果你可以处理它(例如你可以重试的超时)尝试处理它,否则就出去 - 只要想想任何MS应用程序做什么 - 例如办公室 - 你得到一个道歉,出了问题,应用程序结束了。最好是优雅地结束而不是潜在地破坏数据并使事情陷入困境。
答案 1 :(得分:0)
This是一篇关于Java特定概念和示例的文章,但这里的广泛原则是要走的路。
区分灾难性和不可恢复的故障异常,以及非常可恢复的意外异常。让故障“冒泡”到你正确处理的断层屏障。例如,您可能会记录错误,向某人发送电子邮件或将消息发送到消息队列,并向用户显示一个信息丰富的错误页面。
无论您做什么,请务必保留来源的所有异常信息。
希望有所帮助。
答案 2 :(得分:0)
只要您的代码确定某些内容出错,就会抛出异常。
您始终需要处理最终用户直接调用的方法中的异常。这是为了迎合您的代码没有具体处理的意外错误。您的通用处理代码通常会记录错误,可能包括也可能不包括让用户知道发生了意外错误。
但是,如果您提前遇到错误,您通常会希望在代码中处理这些较低的错误,接近它们发生的位置,以便您的应用程序可以从错误中“恢复”并继续。
答案 3 :(得分:0)
我认为,只要您需要从方法返回失败的详细信息,同时能够为您正在调用的方法使用理想的返回类型,异常就很有用。
你在问题中说:
现在对我来说,我尝试将错误消息返回给控制器 并尽量不要在控制器中捕捉任何东西。
如果服务方法理想情况下应该返回Venue对象,那么如何将此潜在错误消息返回给控制器?一个out参数?将返回类型更改为具有错误消息属性的内容?
如果您正在执行这些选项中的任何一个,我认为您正在重新发明轮子...即创建一种在已存在的情况下返回异常信息的方法。
最后,例外是对出错的强类型表示。如果您返回错误消息,那么发送回用户就可以了,但如果您需要根据错误的详细信息以编程方式执行不同的操作,那么您不希望打开魔术字符串。
例如,区分授权错误和未找到错误是不是很方便,以便您可以向用户返回最合适的http状态代码?
不要忘记,Exception类有一个Message属性,如果你想以这种方式使用它,你可以简单地返回给用户
答案 4 :(得分:0)
为了确保我理解这个问题,您正在创建一个Web服务,并想知道何时处理以及何时抛出异常。
在这种情况下,我强烈建议您捕获所有异常。 “未处理”异常是非常糟糕的形式。在网站上,通过公开您不希望公众看到的内部信息,他们会产生从无意义到危险的显示。
如果这是一个大小合适的程序,我建议您创建自己的MyException类,该类派生自System.Exception。这样做的目的是为您提供一个特定于您的应用程序的附加信息。以下是我想添加到MyException类的一些典型内容:
捕获所有异常并创建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异常。这会生成错误编号 - 我使用它。
我想更多地延伸一下。