coalmee
coalmee

Reputation: 1536

Asp.Net WebApi Inheritance with a BaseController

I'm currently struggling to get inheritance in WebApi working like I'd like to. Maybe you can point me in the right direction?

My goal is to have a generic WebApi project, which can be reused in multiple other WebApi projects.

For example: I have a reusable UserController which can handle the login for multiple WebSites. Now I build a new WebSite and simple add the UserController to the newly created project, which simply works.

But now the requirement for the new WebSite changes and I need to overload the UserController methods to reflect these changes without modifying the generic controller.

What I want to achieve can be seen in the following code snippet (of course this won't compile because C# complains not suitable method to overload found.)

// This belongs to solution "GenericWebApi"

public class LoginRequest
{
    public string User { get; set; }
}

public class BaseApiController : ApiController
{
     [Route("login")]
    public virtual HttpResponseMessage Login(LoginRequest request) 
    {
        return new HttpResponseMessage();
    }
}


// This belongs to solution "CarWebsite"

public class CarWebsiteLoginRequest : LoginRequest
{
    public string Car { get; set; }
}

[RoutePrefix("api/users")]
public class CarWebsiteApiController : BaseApiController
{
    public override HttpResponseMessage Login(CarWebsiteLoginRequest request)
    {
        // Do some car specific login stuff
        return base.Login(request);
    }
}

One solution I found to be working was replacing the "LoginRequest" and "CarWebsiteLoginRequest" with "dynamic", but that feels simply wrong and the ModelBinder of course will not work as intended.

Any ideas how you would solve that?

Thanks in advance

Upvotes: 3

Views: 15834

Answers (2)

Brian from state farm
Brian from state farm

Reputation: 2896

If you pull out the variable and read it inside the method it is easier to overload

    /// <summary>
/// Notice name is not Controller.cs  This would expose this API if the name ended in controller
/// </summary>
public class GenericControllerApi : ApiController
{
    protected JavaScriptSerializer JsonReader = new JavaScriptSerializer();

    [HttpPost]
    public virtual HttpResponseMessage Login()
    {
        string JsonRequest = Request.Content.ReadAsStringAsync().Result;

        LoginRequest JSONData = null;
        try
        {
            JSONData = (LoginRequest)JsonReader.Deserialize(JsonRequest, typeof(LoginRequest));
        }
        catch { }

        if (JSONData != null)
        {
            if (CommonUserCalls(JSONData.User))
            {
                ShowUser UserReturn = new ShowUser();
                UserReturn.UserName = "BaseUser";
                return Request.CreateResponse(HttpStatusCode.OK, UserReturn);
            }

            return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid User");
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "ShowUser not provided");
        }
    }

    protected bool CommonUserCalls(string User)
    {
        if (User == "MyUser")
        {
            return true;
        }
        return false;
    }

    public class LoginRequest
    {
        public string User { get; set; }
    }

    protected class ShowUser
    {
        public string UserName { get; set; }
    }
}

public class CarController : GenericControllerApi
{
    [HttpPost]
    public override HttpResponseMessage Login()
    {
        string JsonRequest = Request.Content.ReadAsStringAsync().Result;

        CarWebsiteLoginRequest JSONData = null;
        try
        {
            JSONData = (CarWebsiteLoginRequest)JsonReader.Deserialize(JsonRequest, typeof(CarWebsiteLoginRequest));
        }
        catch { }

        if (JSONData != null)
        {
            if (CommonUserCalls(JSONData.User))
            {
                ShowUser UserReturn = new ShowUser();
                UserReturn.UserName = "CarUser";
                return Request.CreateResponse(HttpStatusCode.OK, UserReturn);
            }

            return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid User");
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "CarWebsiteLoginRequest not provided");
        }
    }

    public class CarWebsiteLoginRequest : LoginRequest
    {
        public string Car { get; set; }
    }
}

Assuming you are reading/sending JSON

Upvotes: 0

Sergey Kolodiy
Sergey Kolodiy

Reputation: 5899

You can make your BaseApiController generic:

public class BaseApiController<TLoginRequest> : ApiController
where TLoginRequest : LoginRequest
{
    [Route("login")]
    public virtual HttpResponseMessage Login(TLoginRequest request)
    {
        return new HttpResponseMessage();
    }
}

And make your CarWebsiteApiController use CarWebsiteLoginRequest as a generic parameter:

public class CarWebsiteApiController : BaseApiController<CarWebsiteLoginRequest>
{
    public override HttpResponseMessage Login(CarWebsiteLoginRequest request)
    {
        // Do some car specific login stuff
        return base.Login(request);
    }
}

Upvotes: 7

Related Questions