将Asp.Net MVC控制器转换为Web API的最佳方法

将Asp.Net MVC控制器转换为Web API的最佳方法

问题描述:

我有这个ASP.NET MVC 5项目,我正在使用MS Web Api将其转换为AngularJS.

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

现在在旧项目中,我有这些类型为Controller的c#控制器,但是在我的新项目中,我创建了一些新的类型为ApiController的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.

当我尝试将旧的控制器代码移植到Web Api控制器时,出现了一些前端$http请求错误.

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

这是我的Angular dataService工厂中的一个函数,它使http要求降至'api/Whatif/SummaryPortfolios':

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);
  });
}

但是$http错误部分返回此异常:

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()

这是我要调用的c#API控制器,但我需要弄清楚如何创建除直接Get()和Post()方法之外的方法:

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;
        }
	}
}

(MVC5项目中的)旧控制器当然看起来略有不同,因为_Summary方法的类型为ActionResult并返回Partial:

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));
        }

我的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 }
      );
    }
  }
}

旧项目还使用HTML表单提取URL,例如:

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>

,然后在JavaScript(非Angular)中构建ajax请求时拉出action属性以获取URL:

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

url: form.prop("action")

这是您的整个ApiController吗?您收到的错误消息是因为您的ApiController有几种相同类型的方法,并且无法确定要路由到哪个方法.要测试这一点,请执行以下操作:注释掉您要调用的控制器方法之外的所有方法.您不应该再收到该错误.

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.

这是一个简单的解决方法,只需告诉Web api如何映射您的路线即可.将属性'[Route("yourroute')]''添加到您的方法中,它应该可以工作.

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);
        }
    }