簡體   English   中英

單個controller ASP.NET Web API中有多個GET方法

[英]Single controller with multiple GET methods in ASP.NET Web API

在 Web API 中,我有一個類似結構的 class:

public class SomeController : ApiController
{
    [WebGet(UriTemplate = "{itemSource}/Items")]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebGet(UriTemplate = "{itemSource}/Items/{parent}")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

由於我們可以使用 map 個單獨的方法,因此在正確的位置獲得正確的請求非常簡單。 對於類似的 class 它只有一個GET方法但也有一個Object參數,我成功地使用IActionValueBinder 但是,在上述情況下,我收到以下錯誤:

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

SomeValue GetChildItems(CustomParam parameter, SomeObject parent) on type SomeType

我試圖通過覆蓋ApiControllerExecuteAsync方法來解決這個問題,但到目前為止沒有運氣。 關於這個問題有什么建議嗎?

編輯:我忘了提及,現在我正在嘗試將此代碼移動到 ASP.NET Web API 上,它具有不同的路由方法。 問題是,如何讓代碼在 ASP.NET Web API 上運行?

這是我發現支持額外 GET 方法和支持普通 REST 方法的最佳方式。 將以下路由添加到您的 WebApiConfig:

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

我用下面的測試類驗證了這個解決方案。 我能夠成功地在我的控制器中點擊下面的每個方法:

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

我確認它支持以下請求:

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

請注意,如果您的額外 GET 操作不以“Get”開頭,您可能需要向該方法添加 HttpGet 屬性。

從這里開始:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

對此:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
            new { id = RouteParameter.Optional });

因此,您現在可以指定要將 HTTP 請求發送到的操作(方法)。

發布到“http://localhost:8383/api/Command/PostCreateUser”調用:

public bool PostCreateUser(CreateUserCommand command)
{
    //* ... *//
    return true;
}

並發布到“http://localhost:8383/api/Command/PostMakeBooking”調用:

public bool PostMakeBooking(MakeBookingCommand command)
{
    //* ... *//
    return true;
}

我在一個自托管的 WEB API 服務應用程序中嘗試了這個,它的工作原理非常棒:)

我發現屬性比通過代碼手動添加它們更易於使用。 這是一個簡單的例子。

[RoutePrefix("api/example")]
public class ExampleController : ApiController
{
    [HttpGet]
    [Route("get1/{param1}")] //   /api/example/get1/1?param2=4
    public IHttpActionResult Get(int param1, int param2)
    {
        Object example = null;
        return Ok(example);
    }

}

你的 webapiconfig 中也需要這個

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

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

一些不錯的鏈接http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api這個更好地解釋了路由。 http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

您需要在 global.asax.cs 中定義更多路由,如下所示:

routes.MapHttpRoute(
    name: "Api with action",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

使用較新的 Web Api 2,擁有多個 get 方法變得更加容易。

如果傳遞給GET方法的參數差異足以讓屬性路由系統區分它們的類型,就像int s 和Guid s 的情況一樣,您可以在[Route...]屬性中指定預期類型

例如 -

[RoutePrefix("api/values")]
public class ValuesController : ApiController
{

    // GET api/values/7
    [Route("{id:int}")]
    public string Get(int id)
    {
       return $"You entered an int - {id}";
    }

    // GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D
    [Route("{id:Guid}")]
    public string Get(Guid id)
    {
       return $"You entered a GUID - {id}";
    }
} 

有關此方法的更多詳細信息,請參見此處http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

另一種選擇是為GET方法提供不同的路由。

    [RoutePrefix("api/values")]
    public class ValuesController : ApiController
    {
        public string Get()
        {
            return "simple get";
        }

        [Route("geta")]
        public string GetA()
        {
            return "A";
        }

        [Route("getb")]
        public string GetB()
        {
            return "B";
        }
   }

有關更多詳細信息,請參見此處 - http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/

在 ASP.NET Core 2.0 中,您可以將Route屬性添加到控制器:

[Route("api/[controller]/[action]")]
public class SomeController : Controller
{
    public SomeValue GetItems(CustomParam parameter) { ... }

    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

在 VS 2019 中,這很容易實現:

[Route("api/[controller]/[action]")] //above the controller class

在代碼中:

[HttpGet]
[ActionName("GetSample1")]
public Ilist<Sample1> GetSample1()
{
    return getSample1();
}
[HttpGet]
[ActionName("GetSample2")]
public Ilist<Sample2> GetSample2()
{
    return getSample2();
}
[HttpGet]
[ActionName("GetSample3")]
public Ilist<Sample3> GetSample3()
{
    return getSample3();
}
[HttpGet]
[ActionName("GetSample4")]
public Ilist<Sample4> GetSample4()
{
    return getSample4();
}

您可以像上面提到的那樣有多個獲取。

我試圖使用 Web Api 2 屬性路由來允許多個 Get 方法,並且我已經合並了以前答案中的有用建議,但是在控制器中我只裝飾了“特殊”方法(示例):

[Route( "special/{id}" )]
public IHttpActionResult GetSomethingSpecial( string id ) {

...也沒有在控制器頂部放置 [RoutePrefix]:

[RoutePrefix("api/values")]
public class ValuesController : ApiController

我收到錯誤,指出找不到與提交的 URI 匹配的路由。 一旦我讓 [Route] 裝飾方法以及 [RoutePrefix] 將控制器作為一個整體裝飾,它就起作用了。

**Add Route function to direct the routine what you want**
    public class SomeController : ApiController
    {
        [HttpGet()]
        [Route("GetItems")]
        public SomeValue GetItems(CustomParam parameter) { ... }

        [HttpGet()]
        [Route("GetChildItems")]
        public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
    }

懶惰/匆忙的選擇(Dotnet Core 2.2):

[HttpGet("method1-{item}")]
public string Method1(var item) { 
return "hello" + item;}

[HttpGet("method2-{item}")]
public string Method2(var item) { 
return "world" + item;}

打電話給他們:

本地主機:5000/api/控制器名稱/方法1-42

“你好42”

本地主機:5000/api/控制器名/method2-99

“world99”

默認情況下 [Route("api/[controller]") 將由 .Net Core/Asp.Net Web API 生成。您需要稍微修改一下,只需添加 [Action] 像 [Route("api/[controller]/[行動]”)]。 我提到了一個虛擬解決方案:

// Default generated controller
//
[Route("api/[controller]")
public class myApiController : Controller
{
    [HttpGet]
    public string GetInfo()
    {
        return "Information";
    }
}

//
//A little change would do the magic
//

[Route("api/[controller]/[action]")]
public class ServicesController : Controller
{
    [HttpGet]
    [ActionName("Get01")]
    public string Get01()
    {
        return "GET 1";
    }

    [HttpGet]
    [ActionName("Get02")]
    public string Get02()
    {
        return "Get 2";
    }
    
    [HttpPost]
    [ActionName("Post01")]
    public HttpResponseMessage Post01(MyCustomModel01 model)
    {
        if (!ModelState.IsValid)
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        
        //.. DO Something ..
        return Request.CreateResonse(HttpStatusCode.OK, "Optional Message");
    }
    
    [HttpPost]
    [ActionName("Post02")]
    public HttpResponseMessage Post02(MyCustomModel02 model)
    {
        if (!ModelState.IsValid)
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        
        //.. DO Something ..
        return Request.CreateResonse(HttpStatusCode.OK, "Optional Message");
    }


}

您是否嘗試過切換到 WebInvokeAttribute 並將方法設置為“GET”?

我相信我有一個類似的問題,並切換到明確告訴我的大多數方法(如果不是全部)預期使用哪種方法(GET/PUT/POST/DELETE)。

public class SomeController : ApiController
{
    [WebInvoke(UriTemplate = "{itemSource}/Items"), Method="GET"]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebInvoke(UriTemplate = "{itemSource}/Items/{parent}", Method = "GET")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

WebGet應該處理它,但我已經看到它有一些問題,多個 Get 少了多個相同返回類型的多個 Get。

[編輯:隨着 WCF WebAPI 的日落和在 MVC 堆棧上遷移到 ASP.Net WebAPI,這些都無效]

我不確定你是否找到了答案,但我做到了,並且有效

public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

// GET /api/values/5
public string Get(int id)
{
    return "value";
}

// GET /api/values/5
[HttpGet]
public string GetByFamily()
{
    return "Family value";
}

現在在 global.asx

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapHttpRoute(
    name: "DefaultApi2",
    routeTemplate: "api/{controller}/{action}",
    defaults: new { id = RouteParameter.Optional }
);

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

以上示例均不適合我的個人需求。 下面是我最終做的。

 public class ContainsConstraint : IHttpRouteConstraint
{       
    public string[] array { get; set; }
    public bool match { get; set; }

    /// <summary>
    /// Check if param contains any of values listed in array.
    /// </summary>
    /// <param name="param">The param to test.</param>
    /// <param name="array">The items to compare against.</param>
    /// <param name="match">Whether we are matching or NOT matching.</param>
    public ContainsConstraint(string[] array, bool match)
    {

        this.array = array;
        this.match = match;
    }

    public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        if (values == null) // shouldn't ever hit this.                   
            return true;

        if (!values.ContainsKey(parameterName)) // make sure the parameter is there.
            return true;

        if (string.IsNullOrEmpty(values[parameterName].ToString())) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus"
            values[parameterName] = request.Method.ToString();

        bool contains = array.Contains(values[parameterName]); // this is an extension but all we are doing here is check if string array contains value you can create exten like this or use LINQ or whatever u like.

        if (contains == match) // checking if we want it to match or we don't want it to match
            return true;
        return false;             

    }

要在您的路線中使用上述內容,請使用:

config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { action = RouteParameter.Optional, id = RouteParameter.Optional}, new { action = new ContainsConstraint( new string[] { "GET", "PUT", "DELETE", "POST" }, true) });

發生的是方法中的約束類型的偽造,因此該路由將僅匹配默認的 GET、POST、PUT 和 DELETE 方法。 那里的“true”表示我們要檢查數組中的項目是否匹配。 如果它是假的,您會說排除 strYou 中的那些然后可以使用此默認方法之上的路由,例如:

config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });

在上面它本質上是在尋找以下 URL => http://www.domain.com/Account/Status/Active或類似的東西。

除了上述之外,我不確定我是否會變得太瘋狂。 歸根結底,它應該是每個資源。 但我確實認為出於各種原因需要映射友好的 url。 我很確定隨着 Web Api 的發展,將會有某種規定。 如果有時間我會建立一個更永久的解決方案並發布。

無法使上述任何路由解決方案起作用——有些語法似乎已經改變,我還是 MVC 的新手——雖然我把這個非常糟糕(而且簡單)的 hack 放在了一起,但它會讓我受益匪淺現在——注意,這取代了“public MyObject GetMyObjects(long id)”方法——我們將“id”的類型更改為字符串,並將返回類型更改為對象。

// GET api/MyObjects/5
// GET api/MyObjects/function
public object GetMyObjects(string id)
{
    id = (id ?? "").Trim();

    // Check to see if "id" is equal to a "command" we support
    // and return alternate data.

    if (string.Equals(id, "count", StringComparison.OrdinalIgnoreCase))
    {
        return db.MyObjects.LongCount();
    }

    // We now return you back to your regularly scheduled
    // web service handler (more or less)

    var myObject = db.MyObjects.Find(long.Parse(id));
    if (myObject == null)
    {
        throw new HttpResponseException
        (
            Request.CreateResponse(HttpStatusCode.NotFound)
        );
    }

    return myObject;
}

如果您在同一個文件中有多個操作,則將相同的參數(例如 Id)傳遞給所有操作。 這是因為動作只能識別Id,所以不要給參數任何名稱,只像這樣聲明Id。


[httpget]
[ActionName("firstAction")] firstAction(string Id)
{.....
.....
}
[httpget]
[ActionName("secondAction")] secondAction(Int Id)
{.....
.....
}
//Now go to webroute.config file under App-start folder and add following
routes.MapHttpRoute(
name: "firstAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

routes.MapHttpRoute(
name: "secondAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

簡單替代

只需使用查詢字符串。

路由

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

控制器

public class TestController : ApiController
{
    public IEnumerable<SomeViewModel> Get()
    {
    }

    public SomeViewModel GetById(int objectId)
    {
    }
}

要求

GET /Test
GET /Test?objectId=1

筆記

請記住,查詢字符串參數不應是“id”或配置路由中的任何參數。

[Route]屬性中指定基本路徑,然后添加到[HttpGet]的基本路徑對我[HttpGet] 你可以試試:

    [Route("api/TestApi")]      //this will be the base path
    public class TestController : ApiController
    {
        [HttpGet]  //example call: 'api/TestApi'
        public string Get()
        {
            return string.Empty;
        }
    
        [HttpGet("{id}")]  //example call: 'api/TestApi/4'
        public string GetById(int id) //method name won't matter
        {
            return string.Empty;
        }
    
        //....

我花了一段時間才弄清楚,因為我不想多次使用[Route]

單個 asp.net web api controller 中的多個方法的概念使得在代碼中擁有多個方法變得更容易。

我能夠按照上述解決方案中的步驟實施並得出最終代碼

在 WebApiConfig.cs 中,按此順序設置以下 Route 配置

 public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.Routes.MapHttpRoute(
                name: "DefaultApiAction",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

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

            config.MapHttpAttributeRoutes();


        }
    }

然后在您的 controller 中,使用 [ActionName] 引用 GET 的 [HttpGet] 或 POST 的 [HttpPost] 參見下面的示例代碼

namespace WebRESTApi.Controllers
{
    //[RoutePrefix("api/Test")]
    public class TestController : ApiController
    {

 

        [HttpGet]
        [ActionName("AllEmailWithDisplayname")]
        public string AllEmailWithDisplayname()
        {
          
            return "values";
        }


 

        [HttpPost]
        [ActionName("Authenticate")]
        // POST: api/Authenticate
        public object Authenticate([FromBody()] object Loginvalues)
        {
 
                return true;
        
        }


        [HttpPost]
        [ActionName("ShowCredential")]
        // POST:  api/Showcredential
        public object Showcredential([FromBody()] object Loginvalues)
        {

            
            return "Username: " 


        }



    }
}

然后您可以使用格式通過客戶端或 postman 使用不同的方法

http://url/api/controller/actionname

修改WebApiConfig並在最后添加另一個 Routes.MapHttpRoute,如下所示:

config.Routes.MapHttpRoute(
                name: "ServiceApi",
                routeTemplate: "api/Service/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

然后創建一個這樣的控制器:

public class ServiceController : ApiController
{
        [HttpGet]
        public string Get(int id)
        {
            return "object of id id";
        }
        [HttpGet]
        public IQueryable<DropDownModel> DropDowEmpresa()
        {
            return db.Empresa.Where(x => x.Activo == true).Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public IQueryable<DropDownModel> DropDowTipoContacto()
        {
            return db.TipoContacto.Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public string FindProductsByName()
        {
            return "FindProductsByName";
        }
}

我就是這樣解決的。 我希望它會幫助某人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM