4

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")
2
  • Please post your routing configuration? Commented Mar 5, 2016 at 0:56
  • @lrb - I posted my RouteConfig.cs at the bottom of my post. Commented Mar 6, 2016 at 23:59

1 Answer 1

2

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);
        }
    }
Sign up to request clarification or add additional context in comments.

7 Comments

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 ?
@bob If it is a GET method then you should not decorate it with HttpPost. Just delete the HttpPost attribute.
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.
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.
old, but I also would drop Controller from the route as well (WhatIf/SummaryPortfolios)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.