Best way to convert Asp.Net MVC controllers to Web API

10,511

Is this your entire ApiController? The error message you are receiving is because your ApiController has several methods that are of the same type and it can't tell which one to route to. To test this: comment out all of your controller's methods except the one you are calling. You shouldn't receive that error anymore.

This is an easy fix, just tell web api how to map your route. Add the attribute '[Route("yourroute')]' to your method and it should work.

    public class WhatifController : ApiController
    {
        [HttpPost, Route("Your Route Goes here 'SummaryPortfolios'")]
        public IHttpActionResult SummaryPortfolios(string filterValue = "", int? whatIfBatchNumber = null, bool includeBaseline = true)
        {
            // Get portfolios from Rz
            IEnumerable<Portfolio> portfolios = GetPortfolios(filterValue, whatIfBatchNumber, includeBaseline)
                .Where(x => x.PortfolioKeys.Any(k => k.Type == Settings.Whatif.SidebarPortfolioKey && k.DisplayValue == filterValue));

            // View Model
            List<WhatifSummaryViewModel> model = new List<WhatifSummaryViewModel> { };

            /// additional code here...

            return Ok(model);
        }
    }
Share:
10,511
bob.mazzo
Author by

bob.mazzo

Front-end Developer with full stack experience in c# and Sql Server, working in norther NJ. Current technologies include Angular, Html/JavaScript/CSS, Kendo UI, Angular-Material, Web Api. I'm married with two boys, play piano at church, and do some gigs on the side. I love soccer (Brazil, Man City) - and enjoying training with my kids, and playing with the guys at work. [email protected]

Updated on June 04, 2022

Comments

  • bob.mazzo
    bob.mazzo almost 2 years

    I have this ASP.NET MVC 5 project which I'm converting over to AngularJS with MS Web Api.

    Now in the old project I have these c# controllers of type Controller, however in my new project I've created some new Web Api controllers of type ApiController.

    Now I'd like to reuse the old controller code in my new project. Herein lies my confusion.

    As I attempt to port the old controller code over to my Web Api controller, I'm getting some front-end $http request errors.

    Here's a function from my Angular dataService factory which makes an http req down to 'api/Whatif/SummaryPortfolios':

    function getCurrPortfoliosLIst() {
      var deferred = $q.defer();
    
      var url = 'api/Whatif/SummaryPortfolios';
      var req={
        method: 'POST',
        url: url,
        headers: {
          'Content-Type': 'application/json',
        },
        data:{}
      };
      $http(req).then(function (resp){
        deferred.resolve(resp.data);
      }, function(err){
        console.log('Error from dataService: ' + resp);
      });
    }

    But the $http error section is returning this exception:

    data: Object
    ExceptionMessage: "Multiple actions were found that match the request: 
    ↵SummaryPortfolios on type MarginWorkbenchNG.Controllers.WhatifController
    ↵Post on type MarginWorkbenchNG.Controllers.WhatifController"
    ExceptionType: "System.InvalidOperationException"
    Message: "An error has occurred."
    StackTrace: "   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
    ↵   at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
    ↵   at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
    ↵   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()

    Here's the c# API controller I'm calling down to, but I need to figure out how to create methods other than straight Get() and Post() methods:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http;
    using Microsoft.AspNet.Identity;
    using NLog;
    using Microsoft.AspNet.Identity.Owin;
    using MarginWorkbenchNG.Models;
    using Rz.DAL;
    using Rz.DAL.Integration;
    using Rz.DAL.Models;
    using Rz.DAL.Models.Rz;
    
    namespace MarginWorkbenchNG.Controllers
    {
        public class WhatifController : ApiController
        {
    		public IEnumerable<string> Get()
    			{						 
    				return new string[] { "value1", "value2" };
    			}
    		[HttpPost]
            public List<WhatifSummaryViewModel> SummaryPortfolios(string filterValue = "", int? whatIfBatchNumber = null, bool includeBaseline = true)
            {
                // Get portfolios from Rz
                IEnumerable<Portfolio> portfolios = GetPortfolios(filterValue, whatIfBatchNumber, includeBaseline)
                    .Where(x => x.PortfolioKeys.Any(k => k.Type == Settings.Whatif.SidebarPortfolioKey && k.DisplayValue == filterValue));
    
                // View Model
                List<WhatifSummaryViewModel> model = new List<WhatifSummaryViewModel> { };
    
                /// additional code here...
    
                return model;
            }
    	}
    }

    The old controller (from the MVC5 project) looks slightly different of course because the _Summary method is of type ActionResult and returns a Partial:

    public class WhatifController : Controller
        {
          
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult _Summary(string filterValue = "", int? whatIfBatchNumber = null, bool includeBaseline = true)
            {
                // Get portfolios from Razor
                IEnumerable<Portfolio> portfolios = GetPortfolios(filterValue, whatIfBatchNumber, includeBaseline)
                    .Where(x => x.PortfolioKeys.Any(k => k.Type == Settings.Whatif.SidebarPortfolioKey && k.DisplayValue == filterValue));
    
                // View Model
                List<WhatifSummaryViewModel> model = new List<WhatifSummaryViewModel> { };
    
               // additional code removed for brevity...
    
                return PartialView(model.OrderBy(x => x.Title).ThenBy(x => x.SubTitle));
            }

    My RouteConfig.cs :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace MarginWorkbenchNG
    {
      public class RouteConfig
      {
        public static void RegisterRoutes(RouteCollection routes)
        {
          routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
          routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
          );
        }
      }
    }

    The old project also uses Html forms to pull the URL, for example:

     <form id="whatif-summary-form" action="@Url.Action("_Summary", "WhatIf")" method="POST"></form>
    

    and then pulls the action attrib to get the URL when building out the ajax request in JavaScript (non-Angular) :

    url: form.prop("action")
    
  • bob.mazzo
    bob.mazzo about 8 years
    in reading this post, asp.net/web-api/overview/web-api-routing-and-actions/… , I believe I decorate my SummaryPortfolios(0 method with [HttpPost, Route("WhatIfController/GetSummaryPortfolios")]. Does it look correct ?
  • Andrew M.
    Andrew M. about 8 years
    @bob If it is a GET method then you should not decorate it with HttpPost. Just delete the HttpPost attribute.
  • bob.mazzo
    bob.mazzo about 8 years
    I want everything to be POST for the most part. I also see that there's more flexibility in WebApi 2 in terms of naming your routes. This is quite useful. I'm gonna have a lot of methods to call, as I'm converting from Asp.Net MVC "Web Pages" to AngularJS/WebApi.
  • Andrew M.
    Andrew M. about 8 years
    Your route shouldn't be called 'GetSummaryPortfolios' if it is a POST call. The route makes it sound like a GET call. Rather it should just be 'SummaryPortfolios'. Follow the best practices vinaysahni.com/…
  • bob.mazzo
    bob.mazzo about 8 years
    good point. i was going back and forth in my mind about that naming convention. thx for your answer. the routing thing is working out.
  • bob.mazzo
    bob.mazzo about 8 years
    Truly the answer is to start with a new Web Api project, and not concern myself with "converting" from MVC. The Web Api project allows me to setup my REST end points, and make clearn $http post requests from JavaScript.
  • Robert
    Robert over 6 years
    old, but I also would drop Controller from the route as well (WhatIf/SummaryPortfolios)