1
votes

I am writing web api in C#, trying to follow REST concepts, i.e. success response will be HttpStatus: 200, validation errors will be 433 and so on.

I have ExceptionFilter registered for entire application in global.asax.

My question here is, for validation errors such as during authentication, if authentication failed, then how should the message be communicated back to client (it has to be a different status code such as 433 or 401).

As of now I am throwing custom exceptions from business layer. Is it recommended to throw exceptions in case of errors?

Following is the code sample:

Logger

public class ExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        HttpResponseMessage response;
        if (context.Exception is ExceptionBase)
        {
            response = context.Request.CreateResponse(HttpStatusCode.InternalServerError,
                new ErrorDTO() { ErrorCode = (int)((ExceptionBase)context.Exception).ExceptionCode, Messages = ((ExceptionBase)context.Exception).Message });
        }
        else {
            response = context.Request.CreateResponse(HttpStatusCode.InternalServerError,
                new ErrorDTO() { ErrorCode = (int)HttpStatusCode.InternalServerError, Messages = "Error occured at server." });
        }
        context.Response = response;
        base.OnException(context);
        LogException(context);
    }

    private static void LogException(HttpActionExecutedContext context)
    {
        if (context.Exception != null)
        {
            Logger.Instance.LogError(context.ActionContext.ActionDescriptor.ActionName, context.Exception);
        }
    }

    public override Task OnExceptionAsync(HttpActionExecutedContext context, CancellationToken cancellationToken)
    {
        return base.OnExceptionAsync(context, cancellationToken);
    }
}

Business layer

public AuthorizationTokenDTO AuthenticateUser(UserDTO userDTO)
{
    User userEntity = _userRepository.GetUserWithAssociatedRole(userDTO.UserName);

    if (userEntity == null)
        throw new BLException("User does not exists.", ExceptionCode.RestApiValidation);

    ActiveDirectory activeDirectory = _activeDirectoryRepository.GetEntityById(ConfigurationSettings.GetConfigSetting<int>(ApplicationConstants.ActiveDirectoryId));

    if (activeDirectory == null)
        throw new BLException("UserName or password is incorrect.", ExceptionCode.RestApiValidation);

    using (var context = new PrincipalContext(ContextType.Domain))
    {
        string password = EncryptionUtility.Decrypt(userDTO.Password, ConfigurationSettings.GetConfigSetting<string>(ApplicationConstants.PrivateKeyForRSAEncryption));

        if (!context.ValidateCredentials(userDTO.UserName, password))
            throw new BLException("UserName or password is incorrect.", ExceptionCode.RestApiValidation);

        UserAccessTokenDTO accessToken = GenrateNewUserAccessToken(userEntity);

        if (accessToken == null)
            throw new BLException("Error occured while generating access token.", ExceptionCode.RestApiValidation);

        return GetAuthorizationTokenDTO(userEntity, accessToken);
    }
}

Is it recommended to throw exceptions for such cases? or is there a better way to write Web APIs?

1

1 Answers

0
votes

The ExceptionFilter should only catch the "unhandled" exceptions, so to speak. You could either keep those exceptions in the BusinessLayer and catch them in the controller, or return some sort of object. Then just return those responses with the appropriate HttpStatusCode from the controler.

Alternatively you could wrap your response into something that also knows about HttpStatusCode (& error message).