2017-05-18 164 views
2

當我嘗試使用$ .getJSON調用Web API控制器方法時,我遇到了令人沮喪的問題:它始終以顯示的以下消息結束控制檯:「無法加載資源:服務器與405狀態響應(不允許的方法)」Web API上的405錯誤(方法不允許)獲取

這裏是我的控制器:

using MyProject.Domain; 
using MyProject.WebApp.Session; 
using System; 
using System.Linq; 
using System.Web.Mvc; 

namespace MyProject.WebApp.ApiControllers.Favorites 
{ 
    public class FavoriteArticlesController : BaseController<FavoriteArticle, Guid> 
    { 
     [HttpGet] 
     public object SetFavorite(Guid articleId, bool isFavorite) 
     { 
      try 
      { 
       if (isFavorite) 
       { 
        var favorite = new FavoriteArticle 
        { 
         UserId = UserInfo.GetUserId(), 
         ArticleId = articleId 
        }; 
        _repo.Upsert(favorite); 
       } 
       else 
       { 
        var favorite = _repo.GetAll() 
         .First(fa => fa.ArticleId.CompareTo(articleId) == 0); 
        _repo.Delete(favorite.Id); 
       } 
       _repo.Commit(); 
       return new { Success = true, Error = (string)null }; 
      } 
      catch (Exception ex) 
      { 
       return new { Success = false, Error = ex.Message }; 
      } 
     } 
    } 
} 

在與其相關的任何情況下,BaseController自然派生來自ApiController。下面是如果需要的代碼:

using MyProject.Data.Repository; 
using MyProject.Data.Services; 
using MyProject.Domain; 
using System.Web.Http; 

namespace MyProject.WebApp.ApiControllers 
{ 
    public class BaseController<TEntity, TKey> : ApiController 
     where TEntity : class, IEntity<TKey>, new() 
    { 
     protected UnitOfWork _unitOfWork; 
     protected Repository<TEntity, TKey> _repo; 

     protected BaseController() 
     { 
      _unitOfWork = new UnitOfWork(); 
      _repo = _unitOfWork.GetRepository<TEntity, TKey>(); 
     } 
    } 
} 

而這裏的是撥打電話的功能之一:

$.fn.bindFavoriteArticle = function() { 
    this.click(function() { 
     var link = $(this); 
     $.getJSON('/api/FavoriteArticles/SetFavorite', { ajax: true, articleId: link.attr('data-target-id'), isFavorite: true }, function (response) { 
      if (response.Success === true) { 
       link.children('i').removeClass('fa-heart-o') 
        .addClass('fa-heart'); 
       link.attr('data-toggle', 'unfavoriteArticle') 
        .unbind('click') 
        .bindUnfavoriteArticle(); 
      } else { 
       // TODO : use bootstrap alert messages 
       alert(response.Error); 
      } 
     }); 
    }); 
}; 

我看到在這裏和那裏的路由配置可能是問題的根源,所以這裏的RouteConfig.cs的內容:

using System.Web.Mvc; 
using System.Web.Routing; 

namespace MyProject.WebApp 
{ 
    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 } 
      ); 

      routes.MapRoute(
       name: "ApiDefault", 
       url: "api/{controller}/{action}/{id}", 
       defaults: new { controller = "SubscriptionsController", action = "GetSelectList", id = UrlParameter.Optional } 
      ); 
     } 
    } 
} 

想知道發生了什麼?我覺得我有很多關於Web API如何工作的想法...

+0

嘗試將link.attr('data-target-id')添加到'/ api/FavoriteArticles/SetFavorite' –

回答

1

正如其他答案之一所述,代碼配置了錯誤的路線。對於Web API配置WebApiConfig

public static class WebApiConfig { 
    public static void Register(HttpConfiguration config) { 
     // Web API routes 

     //Enable Attribute routing is they are being used. 
     config.MapHttpAttributeRoutes(); 

     //Convention based routes. 

     //Matches GET /api/FavoriteArticles/SetFavorite 
     config.Routes.MapHttpRoute(
      name: "DefaultActionApi", 
      routeTemplate: "api/{controller}/{action}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 

     //Matches GET /api/FavoriteArticles 
     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 
    } 
} 

確保它是MVC路線之前配置。

protected void Application_Start() { 
    // Pass a delegate to the Configure method. 
    GlobalConfiguration.Configure(WebApiConfig.Register); 
    //...other configurations 
} 

一些關於重構控制器的建議,讓它更安心一些。嘗試從操作中返回IHttpActionResult。簡化框架,並讓您更好地控制返回響應的方式。

[RoutePrefix("api/favoritearticles"] 
public class FavoriteArticlesController : BaseController<FavoriteArticle, Guid> { 


    [HttpPost] 
    [Route("{articleId:guid}")] //Matches POST api/favoritearticles/{articleId:guid} 
    public IHttpActionResult SetFavorite(Guid articleId) {    
     var favorite = new FavoriteArticle 
     { 
      UserId = UserInfo.GetUserId(), 
      ArticleId = articleId 
     }; 
     _repo.Upsert(favorite); 
     _repo.Commit(); 
     return Ok(new { Success = true, Error = (string)null }); 
    } 

    [HttpDelete] 
    [Route("{articleId:guid}")] //Matches DELETE api/favoritearticles/{articleId:guid} 
    public IHttpActionResult RemoveFavorite(Guid articleId) {    
     var favorite = _repo.GetAll() 
      .First(fa => fa.ArticleId == articleId); 

     if(favorite == null) return NotFound(); 

     _repo.Delete(favorite.Id); 
     _repo.Commit(); 
     return Ok(new { Success = true, Error = (string)null }); 
    } 
} 

控制器應該儘可能稀薄甚至以上所以應當瘦身經由服務注入到控制器甚至更多。

錯誤處理是一個橫切關注點,也應該通過框架的可擴展性點進行提取和處理。

+0

非常感謝,這確實是我的問題,您的建議非常有幫助。 – ZipionLive

0

我相信默認情況下,JSON請求會阻止GET方法來避免JSON劫持。或者試試你的方法轉換爲POST(和更新Ajax調用)或改變當前的函數返回到

return Json(new { Success = true, Error = (string)null }, JsonRequestBehavior.AllowGet); 
1

您所配置的RouteConfig而非WebApiConfig內部API路線。

你需要一個WebApiConfig.cs文件您App_Start文件夾中有這樣的:

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 
    } 
} 

...所以你可以刪除routes.MapRoute()api/{controller}/{action}/{id}RouteConfig.cs內。

然後在你的Global.asax你怎麼稱呼它是這樣的:

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); // only if you have areas... 

    GlobalConfiguration.Configure(WebApiConfig.Register); 
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
    RouteConfig.RegisterRoutes(RouteTable.Routes); 
    BundleConfig.RegisterBundles(BundleTable.Bundles); 
} 

有了這個,這兩個鏈接應該能正常運行的控制器樣品:

/api/FavoriteArticles?articleId=a7d944f7-66be-4200-9b89-26eda5173dca&isFavorite=true 
/api/FavoriteArticles/SetFavorite?articleId=a7d944f7-66be-4200-9b89-26eda5173dca&isFavorite=true 

您通過jQuery調用它的方式是正確的,沒有必要改變它。

+0

第二個鏈接不會工作,這裏沒有路由模板,它採用動作名稱 – Nkosi

+0

@Nkosi實際上它工作正常,我只是測試自己。 – Alisson

+1

這是最奇怪的。好。我想到了。它匹配'SetFavorite'到默認模板中的{{id}'佔位符並匹配查詢字符串。這使其成爲誤報。如果你要給動作一個id參數,你會發現它是正確的。 – Nkosi