Is it possible to use an ApiController to call an Action written in a Controller?

4.7k views Asked by At

I already have some Actions written in my Controllers but I'd need to use them as they were APIs.
So I wonder if it's possible to call a single Controller Action in an ApiController.

At the end I would like to have something like this:

public MyController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
    //or any more complex action
}


public MyApis : ApiController
{
    // some code that returns the same
    // MyController/Index/View result as an API URI     
}

The main reason for this choice is that I'm working on a multi tier solution in VisualStudio.
And I have Controllers in a dedicated project, while I want to have ApiControllers in a different one.

2

There are 2 answers

2
Drisan James On BEST ANSWER

Sure! So for your Action method pertaining to the view you would like to render you would write something basic like this.

Controllers without processes.

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
    //or some model initiation if needed
    public ActionResult Index(ViewModel model)
    {
        return View(model);
    }
}

From here you can then begin to create your API controller methods as you have for your ActionResult methods the only difference are the return types. You api controller should correspond to whatever model instance your are intending to run queries against. Personally I prefer to create a model pertaining to each database abstraction ie. ContactController, AccountController and so on.

    private readonly IRepository repo;

    //This is a generic repository I do not know if you are using this methodology.
    //but this is strictly for demo purposes.
    public ValuesController(IRepository<SomeModel> repo)
    {
        this.repo = repo;
    }
    public IEnumerable<SomeModel> Get()
    {
        var commands = repo.GetAll();
        return commands.ToList();
    }

    // GET api/values/5
    public IQueryable<SomeModel> Get(int id)
    {
        var commands = repo.GetAll().AsQueryable();
        return commands;
    }

    // POST api/values
    public HttpResponseMessage Post(SomeModel model)
    {
        repo.Add(model);

        var response = Request.CreateResponse<SomeModel>(HttpStatusCode.Created, model);

        string uri = Url.Link("DefaultApi", new { id = model.Id});

        response.Headers.Location = new Uri(uri);

        return response;
    }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }

As you can see this is architect-ed from the Empty Api controller class. Lastly, you can call your api controller from whatever view you would like by running the following Jquery code.

self.get = function () {
        $.ajax({
            url: '/api/Values',
            cache: false,
            type: 'GET',
            contentType: 'application/json; charset=utf-8',
            data: {},
            success: function (data) {
                //Success functions here.
            }
        });
    }

As for your post method...

self.create = function (formElement) {
        $.ajax({
            url: '/api/Values',
            cache: false,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            data: ko.toJSON(Command),
            success: function (data) {
              //Success function here.
            }
        }).fail(
                //Fail function here.
                 });

The javascript here came from a knockout.js method if you need a full code snippet to see how to wrap this all together with knockout let me know! But I hope this will get you moving in the right direction!

1
Drisan James On

If I am understanding this correctly you have a few options available.

The first one I would recommend is you create a separate folder that contains all of your api controllers. From here you can extract your process from your ActionResult method and make calls accordingly.

Or you can change your controller type from ActionResult to JsonResult.

public JsonResult DoStuff()
{
   // some code that returns the same
   return Json(new { Data = model } , JsonRequestBehavior = JsonRequestBehavior.AllowGet);
}

Hope this helps!