ASP.NET Web API'sında birden çok GET yöntemine sahip tek denetleyici


167

Web API'sinde benzer bir yapı sınıfı vardı:

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) { ... }
}

Bireysel yöntemleri haritalayabildiğimizden, doğru talebi doğru yerde almak çok basitti. Sadece tek bir GETyöntemi olan ama aynı zamanda bir Objectparametresi olan benzer sınıf için , başarılı bir şekilde kullandım IActionValueBinder. Ancak, yukarıda açıklanan durumda aşağıdaki hatayı alıyorum:

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

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

ExecuteAsyncMetodu geçersiz kılarak ApiControllerama şimdiye kadar hiçbir şans ile bu soruna yaklaşmaya çalışıyorum . Bu konuda herhangi bir tavsiye var mı?

Düzenleme: Şimdi yönlendirme için farklı bir yaklaşımı olan ASP.NET Web API bu kodu taşımak çalışıyorum bahsetmeyi unuttum. Soru, kodu ASP.NET Web API üzerinde nasıl çalıştırabilirim?


1
Hala RouteParameter.Optional olarak {parent} var mı?
Antony Scott

Evet yaptım. Belki IActionValueBinder yanlış şekilde kullanıyorum çünkü int id (demoda olduğu gibi) gibi tipler için iyi çalışıyor.
paulius_l

Üzgünüm, daha net olmalıydım. İsteğe bağlı olarak sahip olmanın, gördüğünüz hata mesajını açıklayan Öğe yolunun yanı sıra alt öğeler yoluyla eşleştiği anlamına geleceğini düşünürdüm.
Antony Scott

Aşağıdaki yaklaşımlar (birden fazla yolla) uygun REST kurallarına aykırı ise şu anda tartışma yaşıyoruz? Bence bu iyi. İş arkadaşım hoş olmadığını düşünüyor. Bu konuda herhangi bir yorumunuz var mı?
Remy

REST hakkında okumaya başladığımda genellikle ona karşıydım. Bunun doğru bir yaklaşım olup olmadığından hala emin değilim ama bazen daha uygun veya kullanıcı dostu, bu yüzden kuralları hafifçe bükmek o kadar da kötü olmayabilir. Belirli bir sorunu çözmek için çalıştığı sürece. Bu soruyu yayınladığımdan bu yana 6 ay geçti ve o zamandan beri bu yaklaşımı kullanmaktan pişman olmadık.
paulius_l

Yanıtlar:


249

Ekstra GET yöntemlerini desteklemenin ve normal REST yöntemlerini de desteklemenin en iyi yolu budur. WebApiConfig konsolunuza aşağıdaki yolları ekleyin:

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

Bu çözümü aşağıdaki test sınıfıyla doğruladım. Aşağıdaki denetleyicimdeki her yöntemi başarıyla vurabildim:

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

Aşağıdaki istekleri desteklediğini doğruladım:

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

Not ekstra GET eylemleri ile başlayan yoksa yöntemine HttpGet öznitelik eklemek isteyebilir 'Get That'.


4
Bu harika bir cevap ve başka bir soru ile ilgili bana çok yardımcı oldu. Teşekkürler!!
Alfero Chingono

4
Bunu denedim - işe yaramıyor gibi görünüyor. Rotaların tümü GetBlah (long id) yöntemiyle rastgele eşleştirilir. :(
BrainSlugs83

1
@ BrainSlugs83: Siparişe bağlıdır. Ve eklemek isteyeceksiniz ("withId" yöntemlerine), aconstraints: new{id=@"\d+"}
Eric Falsken

4
bir yöntem daha eklemeye ne dersiniz - Get (int id, string name)? ... başarısız
Anil Purswani

1
routes.MapHttpRoute("DefaultApiPut", "Api/{controller}", new {action = "Put"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Put)});Benim Putyöntem için böyle bir ekstra yol eklemek zorunda kaldı aksi takdirde bana 404 veriyordu.
Syed Ali Taqi

57

Şuradan git:

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

Buna:

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

Böylece, HTTP isteğinizi göndermek istediğiniz eylemi (yöntemi) belirtebilirsiniz.

için gönderme "http: // localhost: 8383 / API / Command / PostCreateUser" çağırır:

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

ve "http: // localhost: 8383 / api / Command / PostMakeBooking" e gönderme :

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

Ben kendi kendine barındırılan bir WEB API hizmet uygulamasında denedim ve bir cazibe gibi çalışır :)


8
Yararlı cevap için teşekkürler. Yöntem adlarınızı Get, Post, vb. İle başlatırsanız, isteklerinizin kullanılan HTTP fiiline göre bu yöntemlerle eşleşeceğini eklemek isterim. Ama aynı zamanda yöntemler herhangi bir isim ve sonra onları dekore edebilirsiniz [HttpGet], [HttpPost]vb yönteme fiil haritaya bağlıyor.
indot_brad

lütfen benim bakınız soru
Moeez

@DikaArtaKarunia Sorun değil, cevabımın 6 yıl sonra hala geçerli olduğuna sevindim: D
uggeh

31

Ben elle kod yoluyla eklemek daha kullanmak için nitelikleri bulmak. İşte basit bir örnek.

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

}

Buna webapiconfig dosyasında da ihtiyacınız var

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

Bazı İyi Bağlantılar http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api Bu, yönlendirmeyi daha iyi açıklar. http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api


3
Ben de eklemek config.MapHttpAttributeRoutes();gerekiyordu WebApiConfig.cs, ve GlobalConfiguration.Configuration.EnsureInitialized();benim WebApiApplication.Application_Start()yöntem sonunda rota öznitelikleri işe almak.
Ergwun

@Ergwun Bu yorum bana çok yardımcı oldu. Sadece buna eklemek, config.MapHttpAttributeRoutes();(rota haritalama önce görünmesi gerekir örneğin önce config.Routes.MappHttpRoute(....
Philip Stratford

11

Global.asax.cs dosyasında aşağıdaki gibi başka yollar tanımlamanız gerekir:

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

5
Evet bu doğru ama aslında bu rotaların bir örneğini görmek güzel olurdu. Bu cevabı toplum için daha değerli kılacaktır. (ve benden +1 alırsınız :)
Aran Mulholland

Burada bir örnek okuyabilirsiniz - stackoverflow.com/questions/11407267/…
Tom Kerkhove

2
Gerçek bir çözüm daha iyi olurdu.
Pek Çok Goblin

6

Daha yeni Web Api 2 ile birden fazla alma yöntemine sahip olmak daha kolay hale geldi.

Geçirilen parametre ise GETolduğu gibi yöntemlerle kendi türlerini ayırt etmek sistemini yönlendirme özellik için farklı yeterlidir ints ve Guidsen beklenen türünü belirtebilirsiniz s [Route...]özniteliği

Örneğin -

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

Bu yaklaşım hakkında daha fazla ayrıntı için buraya bakın http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

Başka bir seçenek, GETyöntemlere farklı yollar vermektir .

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

Daha fazla ayrıntı için buraya bakın - http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/


5

ASP.NET Core 2.0'da denetleyiciye Route özniteliği ekleyebilirsiniz :

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

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

4

Birden çok Get yöntemine izin vermek için Web Api 2 öznitelik yönlendirmesini kullanmaya çalışıyordum ve önceki yanıtlardan faydalı önerileri dahil ettim, ancak Denetleyicide yalnızca "özel" yöntemi (örnek) süsledim:

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

... ayrıca Denetleyicinin üstüne bir [RoutePrefix] yerleştirmeden:

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

Gönderilen URI ile eşleşen bir Rota bulunmadığını belirten hatalar alıyordum. Ben hem [Route] yöntemi dekorasyon hem de [RoutePrefix] Denetleyici bir bütün olarak dekorasyon vardı, o çalıştı.


3

U cevabı bulup bulmadığından emin değilim, ama bunu yaptım ve işe yarıyor

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

Şimdi global.asx içinde

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

3

WebInvokeAttribute'a geçmeyi ve Yöntemi "GET" olarak ayarlamayı denediniz mi?

Ben benzer bir sorun vardı ve açıkça tüm yöntemleri olmasa da, hangi Yöntem (GET / PUT / POST / DELETE) beklenen anlatmaya geçti inanıyorum.

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 gerektiğini hallederim ama çoklu kullanıma sahip bazı sorunlar aynı dönüş türü çok az birden Get Get sahip gördük.

[Düzenle: bunların hiçbiri WCF WebAPI'nin gün batımı ve MVC yığınında ASP.Net WebAPI'ye geçiş ile geçerli değildir]


1
Maalesef, WCF Web API'sı kullanımdan kaldırıldığı için kodu ASP.NET Web API'ye taşıdığımı belirtmeyi unuttum. Gönderiyi düzenledim. Teşekkür ederim.
paulius_l

2
**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) { ... }
    }

Stack Overflow'a hoş geldiniz! Lütfen yanıtınızı , kodunuz için bir açıklama ve bunun yanı sıra diğer on dört yanıttan nasıl farklı olduğuna dair bir açıklama içerecek şekilde düzenleyin . Bu soru neredeyse sekiz yaşında ve zaten kabul edilmiş ve birkaç iyi açıklanmış cevabı var. Sizinkine bir açıklama yapılmazsa, büyük olasılıkla indirgenecek veya kaldırılacaktır. Bu açıklamaya sahip olmak, yanıtınızın bu sorudaki yerini haklı çıkarmaya yardımcı olacaktır.
Das_Geek

1
Şahsen (SOs önerilerinin ne olduğunu biliyorum) bir soru için bu açık / temel ben şahsen çok saf bir kod cevabı olurdu . Çok fazla açıklama okumak istemiyorum Faydalı fonksiyonel yazılımları hızlı hale getirmek istiyorum . +1
MemeDeveloper

2

Tembel / acele alternatifi (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;}

Onları çağırıyorum:

localhost: 5000 / API / controllername / method1-42

"Hello42"

localhost: 5000 / API / controllername / method2-99

"World99"


0

Yukarıdaki örneklerin hiçbiri kişisel ihtiyaçlarım için işe yaramadı. Aşağıdakileri yaptığım şey.

 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;             

    }

Yukarıdakileri rotanızda kullanmak için şunu kullanın:

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

Olan şey, yöntemdeki kısıtlayıcı sahte türdür, böylece bu yol sadece varsayılan GET, POST, PUT ve DELETE yöntemleriyle eşleşir. Buradaki "doğru" dizideki öğelerin eşleşmesini kontrol etmek istediğimizi söylüyor. Yanlış olsaydı, str'de bulunanları hariç tut diyordunuz, sonra bu varsayılan yöntemin üzerindeki yolları kullanabilirsiniz:

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

Yukarıda, aslında aşağıdaki URL => http://www.domain.com/Account/Status/Activeveya bunun gibi bir şey arıyor .

Yukarıdakilerin ötesinde çok delireceğimden emin değilim. Günün sonunda kaynak başına olmalıdır. Ancak, çeşitli nedenlerden dolayı dost URL'leri haritalamaya ihtiyacım var. Web Api'nin bir tür hüküm olacak gibi geliştiğinden oldukça eminim. Eğer zaman kalırsa daha kalıcı bir çözüm ve görev yapacağım.


Bunun new System.Web.Http.Routing.HttpMethodConstraint(HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete) yerine kullanabilirsiniz .
abatishchev

0

Yukarıdaki yönlendirme çözümlerinden hiçbiri işe yaramadı - sözdiziminin bir kısmı değişmiş gibi görünüyor ve hala MVC'de yeniyim - bir tutamda beni bir araya getirecek gerçekten korkunç (ve basit) kesmek bir araya getirdim şimdilik - not, bu "public MyObject GetMyObjects (long id)" yönteminin yerini alır - "id" türünü bir dizeye değiştiririz ve dönüş türünü nesneye değiştiririz.

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

0

Aynı dosya içinde birden çok Eylem varsa, aynı argümanı iletin, örneğin tüm Eylem'e Id. Bunun nedeni, eylemin yalnızca Kimliği tanımlayabilmesidir, Bu nedenle, argümana herhangi bir isim vermek yerine yalnızca Id'yi böyle beyan edin.


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

Url tarayıcıdaki her bir işlevi nasıl görüntülemek ister?
Si8

0

Basit Alternatif

Sadece bir sorgu dizesi kullanın.

Yönlendirme

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

kontrolör

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

    public SomeViewModel GetById(int objectId)
    {
    }
}

İstekler

GET /Test
GET /Test?objectId=1

Not

Sorgu dizesi parametresinin "id" olmaması veya parametrenin yapılandırılmış rotadaki biçimi olmaması gerektiğini unutmayın.


-1

WebApiConfig dosyasını değiştirin ve sonunda başka bir Routes.MapHttpRoute ekleyin:

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

Sonra böyle bir denetleyici oluşturun:

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

Ben böyle çözdüm. Umarım birine yardım eder.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.