Should an API controller attempt to catch all 500 internal server errors? - asp.net-web-api2

I apologize if this has been asked before, please link me to it. I am having a hard time finding discussion as to whether it is an acceptable practice to catch all internal server errors (500) in an API.
I have seen some arguments for it and some against it.
I've always heard that one should send a status instead of errors when possible. However, I do see how the 500 is semantically taking responsibility for a failed request and letting a client know that their request may not be at fault. But a status can convey that as well and then the 500 is just reserved for application container level errors not errors in the database or a class library.
Is there an accepted standard or is this an opinion topic?
e.g.
public HttpResponseMessage GetUserRoles()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, AuthorizationService.GetUserRoles());
}
catch (SqlException sqle)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "A data error occured. Contact IT Support.");
}
catch (Exception e)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "An error occured. Contact IT Support.");
}
}

A good practice is to always return the same structure
public interface IResponse<T>
{
MsgType MsgType { get; }
string Msg { get; }
T Result { get; }
string Origin { get; }
}
Then you can have a MasterApiController with these methods
[NonAction]
public IHttpActionResult ResponseOk<T>(T result)
{
return Ok<IResponse>(ResponseFactory.Create<T>(result, "", "Negocio"));
}
[NonAction]
public IHttpActionResult ResponseEx(Exception ex, string msg = "Un error ha ocurrido.")
{
return ResponseMessage(Request.CreateResponse<IResponse>(HttpStatusCode.InternalServerError, ResponseFactory.Create(msg, ex, "Negocio")));
}
And in your controller that inherits from the master one you call this
[HttpGet]
[Route("Api/Alumno/Search")]
public dynamic Search(string codigo, string nombre, string estado, int? curso, int? paralelo)
{
return ResponseOk<dynamic>(result);
}
For all your non controlled exceptions you can have an action filter and manage them
public class ErrorLoggingFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Exception
}
}

Related

How to provide sample response in Swagger when using common Response structure?

I am using asp.net core 3.1 with Swashbuckle 5.6. I am using a common class ApiResponse to standardize the response structure. So for both http status codes 404 and 500, my response structure will use same class.
But in the generated swagger documentation, I want to provide different examples for different response codes. If I use typeof(ApiResponse) with either ProducesResponseType or SwaggerResponse, it will end up showing same "Example value" for both 404 and 500 status codes. I tried providing sample in the XML documentation. But that does not come with schema.
ApiResponse class structure is same as that used in below link.
https://www.devtrends.co.uk/blog/handling-errors-in-asp.net-core-web-api
public class ApiResponse
{
public int StatusCode { get; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; }
public ApiResponse(int statusCode, string message = null)
{
StatusCode = statusCode;
Message = message ?? GetDefaultMessageForStatusCode(statusCode);
}
private static string GetDefaultMessageForStatusCode(int statusCode)
{
switch (statusCode)
{
...
case 404:
return "Resource not found";
case 500:
return "An unhandled error occurred";
default:
return null;
}
}
}
Both statusCode and Message will be different for 404 and 500.
I have another similar issue with Ok Response also. By using generics, I am able to get proper example for class type. But for status code and Message, I am unable to provide specific values.
public class ApiResponseOk<T> : ApiResponse
{
public T Result { get; }
public ApiResponseOk()
{
}
public ApiResponseOk(T result, string message = null)
: base(200, message)
{
Result = result;
}
}
Please let me know how can I provide separate examples when using same type for response.
Thanks!

MVC 3/EF/SQL Handling Connections, Disposal and Timeouts

Currently, this is how I am handling data within my MVC 3 application. Being pretty new to both MVC 3 and the Entity Framework I am not quite sure this is the best approach to handling data within the application. In fact, the call to check UserExists below sometimes gives a SQLConnectionTimeout issue which seems to be completely random. I've tried tracing the problem through SQL profiler and it appears that the timeout occurs right after the connection is being made from EF -> SQL.
I thought I had this solved in another question here on SO but it popped back up, so I wanted to get everyone's opinion on whether or not below is the best way to attempt data handling in my application or is there a better way that may solve the timeout issue.
Here is a link to the other article if it helps: MVC 3/EF/SQL Server strange connection timeout issue
So to summarize my question(s):
Is the code below acceptable?
Should it work fine?
Is there a better way?
Will unnecessary connections to SQL remain open from EF? (SQL Profiler makes it look like it stays open a while even after the using statement has exited)
Any idea on the timeout issue I posted in my other article?
Note: The repository implements IDisposable and has the dispose method listed below. It creates a new instance of the entity context in the repository constructor.
Controller (LogOn using Custom Membership Provider):
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
User newUser = new User();
using (AccountRepository repo = new AccountRepository())
{
newUser = repo.GetUser(model.UserName);
...
}
}
Membership Provider ValidateUser:
public override bool ValidateUser(string username, string password)
{
using (AccountRepository repo = new AccountRepository())
{
try
{
if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
return false;
string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");
bool exists = false;
exists = repo.UserExists(username, hash);
return exists;
}catch{
return false;
}
}
}
Account Repository Methods for GetUser & UserExists:
Get User:
public User GetUser(string userName)
{
try
{
return entities.Users.SingleOrDefault(user => user.UserName == userName);
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
User Exists:
public bool UserExists(string userName, string userPassword)
{
if (userName == "" || userPassword == "")
throw new ArgumentException(InvalidUsernamePassword);
try
{
bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
return exists;
}
catch (Exception Ex)
{
throw new Exception("An error occurred: " + Ex.Message);
}
}
Repository Snippets (Constructor, Dispose etc):
public class AccountRepository : IDisposable
{
private DbContext entities;
public AccountRepository()
{
entities = new DbContext();
}
...
public void Dispose()
{
entities.Dispose();
}
}
Thanks everyone - I realize that this question crit's you for over 9000 with a giant wall of text!
We generally follow the pattern of controlling the instantiation and disposal of the context using an IActionFilter and providing a mechanism to inject that into dependent classes (using Ninject).
If you're not using dependency injection / IoC you can get away with a base controller a little like the following:
using System;
using System.Diagnostics;
using System.Linq;
using System.Transactions;
using System.Web.Mvc;
public class ControllerBase : Controller
{
private ContextState contextState;
protected EntityContext Context
{
get { return this.contextState.Context; }
}
protected TransactionScope TransactionScope
{
get { return this.contextState.TransactionScope; }
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
IsolationLevel isolationLevel = filterContext.ActionDescriptor
.GetCustomAttributes(typeof(UnitOfWorkAttribute), false)
.Cast<UnitOfWorkAttribute>()
.Select(a => a.IsolationLevel)
.DefaultIfEmpty(IsolationLevel.ReadCommitted)
.First();
Trace.TraceInformation("Creating database context & transaction scope with isolation {0}.", isolationLevel);
this.contextState = new ContextState
{
TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = isolationLevel }),
Context = new EntityContext()
};
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
try
{
if (filterContext.Exception == null)
{
Trace.TraceInformation("Commiting transaction scope.");
this.contextState.TransactionScope.Complete();
}
else
{
Trace.TraceInformation("Rolling back transaction scope.");
}
}
finally
{
try
{
Trace.TraceInformation("Disposing database context.");
this.contextState.Context.Dispose();
}
catch (Exception e)
{
Trace.TraceError("Failed to dispose database context. {0}", e);
}
try
{
Trace.TraceInformation("Disposing transaction scope.");
this.contextState.TransactionScope.Dispose();
}
catch (Exception e)
{
Trace.TraceError("Failed to dispose transaction scope. {0}", e);
}
this.contextState = null;
}
}
private class ContextState
{
public EntityContext Context { get; set; }
public TransactionScope TransactionScope { get; set; }
}
}
/// <summary>
/// Marks an MVC action as requiring a particular <see cref="IsolationLevel" /> when a transaction is
/// created for it.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class UnitOfWorkAttribute : Attribute
{
private readonly IsolationLevel isolationLevel;
public UnitOfWorkAttribute(IsolationLevel isolationLevel)
{
this.isolationLevel = isolationLevel;
}
/// <summary>
/// Gets an <see cref="IsolationLevel" /> value indicating the isolation level
/// a transaction should use.
/// </summary>
public IsolationLevel IsolationLevel
{
get
{
return this.isolationLevel;
}
}
}
Here we create an instance of your context and a transaction scope just before an action executes and then we cleanup once the action has finished up.
In your derived controller you can then do the following...
public class HomeController : ControllerBase
{
public ActionResult Index()
{
using (AccountRepository accountRepository = new AccountRepository(this.Context))
{
// do stuff
}
return View();
}
}
Passing the context into your repository is a little messy and can be tidied up using something like Ninject to inject the dependency rather than you providing it. http://stevescodingblog.co.uk/dependency-injection-beginners-guide/ provides a pretty reasonable starting point if you're interested.
You can also mark up an action with the UnitOfWorkAttribute to control creation of the transaction used by the context. It's recommended not to use implicit transaction when doing database work (http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions) so we always create a transaction scope when executing the action. This has little overhead because, unless the connection is opened, the transaction scope doesn't do much.
Edit: Just to answer another of your questions...
Will unnecessary connections to SQL remain open from EF? (SQL Profiler makes it look like it stays open a while even after the using statement has exited)
Most likely reason here is connection pooling. ADO.NET will maintain open connections for a period of time which makes subsequent calls more efficient because you don't have the latency of opening the connection.
Cheers,
Dean

How to intercept WCF faults and return custom response instead?

Consider the following very basic WCF service implementation:
public enum TransactionStatus
{
Success = 0,
Error = 1
}
public class TransactionResponse
{
public TransactionStatus Status { get; set; }
public string Message { get; set; }
}
[ServiceContract]
[XmlSerializerFormat]
public interface ITestService
{
[OperationContract]
TransactionResponse DoSomething(string data);
}
public class TestService : ITestService
{
public TransactionResponse DoSomething(string data)
{
var result = ProcessData(data); // may throw InvalidOperationException
return new TransactionResponse()
{
Status = TransactionStatus.Success,
Message = result
};
}
private string ProcessData(string data)
{
if (data = "foobar")
throw new InvalidOperationException();
return data;
}
}
In the instance that the DoSomething method does throw an InvalidOperationException, I would like to intercept the fault and return a TransactionResponse object, rather than have WCF raise a FaultException with the client. How can I do this without surrounding each method body in a huge try catch statement? Is there some where I can hook into? Can I do this with some sort of attribute or something? An example of how I would like to handle it can be demonstrated using ASP.NET MVC:
public class ApiController : BaseController
{
protected override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
var message = HttpContext.IsDebuggingEnabled ? ex.ToString() : ex.Message;
_logger.Error("Error processing request for controller {0}, action {1}",
filterContext.RequestContext.RouteData.Values["controller"],
filterContext.RequestContext.RouteData.Values["action"]);
_logger.Error(ex.ToString());
filterContext.ExceptionHandled = true;
filterContext.Result = ToXml(new ApiResult(false)
{
Message = message
});
}
// ...
}
Using the above method in MVC, I can ensure that no matter which controller action throws an exception, I can handle it and return an appropriately formatted ActionResult containing the necessary info. Is there a way to do this kind of thing with WCF?
Check out the WCF IErrorHandler interface - it allows you to centrally define one way in your service implementation to catch all exceptions and either swallow them, or convert them to WCF-friendly SOAP exceptions. This will make sure the channel between the client and the server isn't faulted, e.g. it can still be used after this call failed.
I don't understand why you'd want to "catch" the SOAP faults and convert those to something else, though.... nor do I know of any support that WCF would give you. The basic assumption is: catch .NET exceptions and convert them into interoperable SOAP faults

Impossible to have two sagas that handle the same message type

I have 2 different sagas (I mean saga types) that handle the same message.
public class AttachMessageToBugSaga : TpSaga<AttachMessageToBugSagaData>, IAmStartedByMessages<MessageIsNotAttached>, IHandleMessages<MessageAttachedToGeneralMessage>
{
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<MessageAttachedToGeneralMessage>(
saga => saga.Id,
message => message.SagaId
);
}
public void Handle(MessageIsNotAttachedToBug message)
{
Send(new AttachMessageToGeneralCommand { MessageId = 66, GeneralId = 13 });
}
public void Handle(MessageAttachedToGeneralMessage message)
{
//do some stuf fhere
}
}
public class AttachMessageToBugSagaData : IContainSagaData
{
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
}
public class AttachMessageToRequestSaga : TpSaga<AttachMessageToRequestSagaData>, IAmStartedByMessages<MessageIsNotAttachedToRequest>, IHandleMessages<MessageAttachedToGeneralMessage>
{
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<MessageAttachedToGeneralMessage>(
saga => saga.Id,
message => message.SagaId
);
}
public void Handle(MessageIsNotAttachedMessageToRequest message)
{
//do some stuff here
}
public void Handle(MessageAttachedToGeneralMessage message)
{
//do some stuff here
}
}
public class AttachMessageToRequestSagaData : IContainSagaData
{
public Guid Id { get; set; }
public string Originator { get; set; }
public string OriginalMessageId { get; set; }
}
When I run the sample I get an exception :
System.InvalidCastException: Unable to cast object of type 'MyCustomPlugin.AttachMessageToGeneralSagaData' to type 'MyCustomPlugin.AttachMessageToRequestSagaData'.
I understand why it happens, but I still need some workaround. I tried to implement my own IFindSagas class :
public class SagaFinder : IFindSagas<AttachMessageToGeneralSagaData>.Using<MessageAttachedToGeneralMessage>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageAttachedToGeneralMessage>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageIsNotAttachedToRequest>,
IFindSagas<AttachMessageToRequestSagaData>.Using<MessageIsNotAttachedToBug>
{
AttachMessageToGeneralSagaData IFindSagas<AttachMessageToGeneralSagaData>.Using<MessageAttachedToGeneralMessage>.FindBy(MessageAttachedToGeneralMessage message)
{
return ObjectFactory.GetInstance<AttachMessageToGeneralSagaData>();
}
AttachMessageToRequestSagaData IFindSagas<AttachMessageToRequestSagaData>.Using<MessageAttachedToGeneralMessage>.FindBy(MessageAttachedToGeneralMessage message)
{
return ObjectFactory.GetInstance<AttachMessageToRequestSagaData>();
}
public AttachMessageToRequestSagaData FindBy(MessageIsNotAttachedToRequest message)
{
return new AttachMessageToRequestSagaData();
}
public AttachMessageToRequestSagaData FindBy(MessageIsNotAttachedToBug message)
{
return new AttachMessageToRequestSagaData();
}
}
But I do not get into my finders for "MessageAttachedToGeneralMessage".
Please tell me if there is some other workaround, or how to make this example working.
I'm not sure that having more than one Saga within the same process boundary works very well - at least, I've had problems with it too. It's probably better (in general) to have Sagas separated into two different processes anyway, because otherwise it would cause a lot of locking and potentially deadlocks on your saga storage.
Is your message that is handled by 2 Sagas Sent or Published? If it's published (or can be made to), it would be easy to separate the Sagas into two separate assemblies. Just be sure to manually call Bus.Subscribe() for the message type in each Saga, since Sagas don't auto-subscribe to messages listed in the app.config.
If your message is Sent, and there's nothing you can do to change it, then create a central handler for your existing message type that either Publishes a second message type to go to both Sagas, or Sends two separate messages to each saga.
Finally (after digging into the source code) I've found the solution. It seems the only way is to implement my own SagaPersister, where I can do anything I want.
Default implementation in NserviceBus of InMemorySagaPersister has the following code :
T ISagaPersister.Get<T>(Guid sagaId)
{
ISagaEntity result;
data.TryGetValue(sagaId, out result);
return (T)result;
}
And exception occurs while casting .

Wcf exception handling throw an error

Hi I have a problem handling exceptions in wcf.
I have a service like this one:
[ServiceContract]
public interface IAddressService
{
[OperationContract]
[FaultContract(typeof(ExecuteCommandException))]
int SavePerson(string idApp, int idUser, Person person);
}
I am calling the SavePerson() on the service in the WCFTestClient utility.
The SavePerson() implementation is:
public int SavePerson(string idApp, int idUser, Person person)
{
try
{
this._savePersonCommand.Person = person;
this.ExecuteCommand(idUser, idApp, this._savePersonCommand);
return this._savePersonCommand.Person.Id;
}
catch (ExecuteCommandException ex)
{
throw new FaultException<ExecuteCommandException>(ex, new FaultReason("Error in 'SavePerson'"));
}
}
But I get this error:
Failed to invoke the service. Possible
causes: The service is offline or
inaccessible; the client-side
configuration does not match the
proxy; the existing proxy is invalid.
Refer to the stack trace for more
detail. You can try to recover by
starting a new proxy, restoring to
default configuration, or refreshing
the service.
if I change the SavePerson method and instead of:
catch (ExecuteCommandException ex)
{
throw new FaultException<ExecuteCommandException>(ex, new FaultReason("Error in 'SavePerson'"));
}
I do
catch(Exception)
{
throw;
}
I don't get the above error, but I only get the exception message and no inner exception.
What am I doing wrong?
When you define the fault contract:
[FaultContract(typeof(ExecuteCommandException))]
you must not specify an exception type. Instead, you specify a data contract of your choice to pass back any values that you deem necessary.
For example:
[DataContract]
public class ExecuteCommandInfo {
[DataMember]
public string Message;
}
[ServiceContract]
public interface IAddressService {
[OperationContract]
[FaultContract(typeof(ExecuteCommandInfo))]
int SavePerson(string idApp, int idUser, Person person);
}
catch (ExecuteCommandException ex) {
throw new FaultException<ExecuteCommandInfo>(new ExecuteCommandInfo { Message = ex.Message }, new FaultReason("Error in 'SavePerson'"));
}