1
votes

I have an API application that works perfectly on Visual Studio 2015 debugger; I am trying to publish my application on IIS (I have installed the server by Control panel Turn on/off Windows features).

My application has a security system to check that any request is from a user registered, any request pass first throw ApiAuthorizationFilter, resolve the token that is composed by Base64 string, it comes in the header as Authorization and returns the specific HttpStatusCode with JSON.

The Exception Occurs after deploy, if I run a test on the application that is at IIS throws NullReferenceException at Filter.ApiAuthorizationFilter, but if I run the same application on Visual Studio - it works.

This is the error message:

500: Internal Server Error "Message": "An error has occurred." "ExceptionMessage": "Object reference not set to an instance of an object." "ExceptionType": "System.NullReferenceException" "StackTrace": " at API.CARDS.Models.Filter.ApiAuthorizationFilter.OnAuthorization(HttpActionContext actionContext) at System.Web.Http.Filters.AuthorizationFilterAttribute.OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"

Here is the code:

ApiAuthorizationFilter

public class ApiAuthorizationFilter : AuthorizationFilterAttribute
{
    [Inject]
    public IEncryptionSystemService service { get; set; }

    [Inject]
    public IUserService userService { get; set; }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        //Hace una petición (request) al actionContext
        HttpRequestMessage request = actionContext.Request;
        try
        {
            //Establece la cultura (Ejemplo: es-MX) para los mensajes i18n
            string culture = actionContext.Request.Headers.AcceptLanguage.ToString();
            if (culture.Length == 5)
            {
                Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
            }
            //Crea un token con los valores que se obtienen del actionContext, mismos que serán las credenciales para accesar.
            string token = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault().Replace("Credentials", "").Trim();
            //Desencripta las credenciales con el servicio del tipo IEncryptionService.
            string[] userPass = service.DecryptText(token);
            User model = new User { email = userPass[0], password = userPass[1] };
            ////Verifica que sean correctas las credenciales que ser obtuvieron del token.
            AuthorizationResult result = userService.LoginUser(model);
            switch (result)
            {
                case AuthorizationResult.ACCESS_GRANTED:
                    User user = new User { email = model.email };
                    ApiIdentity identity = new ApiIdentity(user);
                    ApiPrincipal principal = new ApiPrincipal(identity);
                    Thread.CurrentPrincipal = principal;
                    break;
                case AuthorizationResult.ACCESS_DENIED:
                    actionContext.Response = request.CreateResponse(HttpStatusCode.NotFound, "ErrorResources.NotFound");
                    break;
                case AuthorizationResult.PERMISSION_DENIED:
                    actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized, "ErrorResources.Unauthorized");
                    break;
            }
        }
        catch (Exception e)
        {
            throw;
            //Si existe un error durante la petición, regresa un estatus de InternalServerError (Error interno del Servidor).
            //actionContext.Response = request.CreateErrorResponse(HttpStatusCode.InternalServerError, string.Format("ErrorResources.AuthenticationError ---- {0}",e.Message));

        }
    }
}

Image response from the debugger

Image response from IIS

1
Publish the PDB's so you can at least see the line number. I suspect your DI is not working.leppie
where can I find the PDB's if my visual studio has the default configuration? and Do you mean the Dependency Injection? I have the same feeling about the NInject, but I don't know why it works only on DebugGerardo E Martínez

1 Answers

3
votes

I found the answer after a big research!, This answer is for API Application with NInject, Leppie was right the issue was around Dependency Injection Configuration.

First I had to clean my packets installed from NuGet, I had to reinstall Ninject with these packets.

  • Ninject
  • Ninject.web.WebApi
  • Ninject.web.WebApi.WebHost -- I was missing this one, this is required for IIS support
  • Ninject.MVC3
  • Ninject.web.Common
  • Ninject.web.Common.WebHost -- Update this one to last version (3.2.3), this is required for IIS support

After that check your class NInjectWebCommon.cs, in the method called CreateKernel() I had this line

GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

You need to remove it, because this will throw

Error activating ModelValidatorProvider using binding from ModelValidatorProvider to NinjectDefaultModelValidatorProvider

If everything is OK! your application will run on IIS Server.

these files are my last configuration of NInject in the App_Start folder

NinjectWebCommon.cs

public static class NinjectWebCommon
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            RegisterServices(kernel);

           // GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        // kernel.BindFilter<ApiAuthorizationFilter>(FilterScope.Controller, 0).WhenControllerHas<ApiAuthorizationFilter>();

        NInjectHelper.SetupKernel(kernel);
        kernel.Bind<IEncryptionSystemService>().To<EncryptionSystemService>();
        kernel.Bind<IUserService>().To<UserService>();
        kernel.Bind<IRoleService>().To<RoleService>();
        kernel.Bind<IStatusService>().To<StatusService>();
        kernel.Bind<ICardService>().To<CardService>();
        kernel.Bind<ICardTypeService>().To<CardTypeService>();


    }

}

NinjectResolver.cs

 public class NinjectResolver : NinjectScope, IDependencyResolver
{
    private IKernel _kernel;

    public NinjectResolver(IKernel kernel)
        : base(kernel)
    {
        _kernel = kernel;
    }

    public IDependencyScope BeginScope()
    {
        return new NinjectScope(_kernel.BeginBlock());
    }
}

NinjectScope.cs

  public class NinjectScope : IDependencyScope
{
    protected IResolutionRoot resolutionRoot;

    public NinjectScope(IResolutionRoot kernel)
    {
        resolutionRoot = kernel;
    }

    public object GetService(Type serviceType)
    {
        IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
        return resolutionRoot.Resolve(request).SingleOrDefault();
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
        return resolutionRoot.Resolve(request).ToList();
    }

    public void Dispose()
    {
        IDisposable disposable = (IDisposable)resolutionRoot;
        if (disposable != null) disposable.Dispose();
        resolutionRoot = null;
    }
}

IMPORTANT! For SQL Server Users As you can see on my ApiFilterConfiguration I have this line AuthorizationResult result = userService.LoginUser(model); this userService is a class that I have created with the method LoginUser(model) this method gets a model from the Request Headers and goes to SQL SERVER to verify the user exist.

If you installed SQL Server with default configuration as me, maybe you have another issue that throws Null reference after a consult, check this topic Login failed for user IIS APPPOOL\AppPool4.5 or APPPOOL\ASP.NET maybe you need to create a login for IIS on your SQLSERVER