Übergeben mehrerer Parameter an eine get-Methode in ASP.NET Core


105

Wie kann ich mehrere Parameter an Get-Methoden in einem MVC 6-Controller übergeben? Zum Beispiel möchte ich in der Lage sein, so etwas wie das Folgende zu haben.

[Route("api/[controller]")]
public class PersonController : Controller
{
    public string Get(int id)
    {
    }

    public string Get(string firstName, string lastName)
    {

    }

    public string Get(string firstName, string lastName, string address)
    {

    }
}

So kann ich gerne abfragen.

api/person?id=1
api/person?firstName=john&lastName=doe
api/person?firstName=john&lastName=doe&address=streetA

Antworten:


89

Sie können dies auch verwenden:

// GET api/user/firstname/lastname/address
[HttpGet("{firstName}/{lastName}/{address}")]
public string GetQuery(string id, string firstName, string lastName, string address)
{
    return $"{firstName}:{lastName}:{address}";
}

Hinweis : Bitte beziehen Sie sich auf Metalheart's und metalheartund Mark Hughesfür einen möglicherweise besseren Ansatz.


21
Bis Sie alle mit dem gleichen Nachnamen bekommen müssen :)
Phillip Copley

14
Das ist ein wirklich schlechter Weg, um API-Routen zu entwerfen ... Überhaupt nicht RESTful.
Thomas Levesque

7
Der obige Ansatz sieht sehr umständlich aus, verstehen Sie nicht, warum es so viele positive Stimmen gibt.
Bernoulli IT

1
@ThomasLevesque Was hast du damit gemeint, dass es nicht RESTful ist?
Bruno Santos

2
@BrunoSantos folgt nicht den Prinzipien von REST. URIs sollen Ressourcen eindeutig identifizieren. Dies ist hier nicht der Fall (es kann mehrere Personen mit demselben Vor- und Nachnamen geben, und eine Adresse kann sicherlich nicht als Kennung angesehen werden)
Thomas Levesque

60

Warum nicht nur eine Controller-Aktion verwenden?

public string Get(int? id, string firstName, string lastName, string address)
{
   if (id.HasValue)
      GetById(id);
   else if (string.IsNullOrEmpty(address))
      GetByName(firstName, lastName);
   else
      GetByNameAddress(firstName, lastName, address);
}

Eine andere Option ist die Verwendung des Attribut-Routings. Dann benötigen Sie jedoch ein anderes URL-Format:

//api/person/byId?id=1
[HttpGet("byId")] 
public string Get(int id)
{
}

//api/person/byName?firstName=a&lastName=b
[HttpGet("byName")]
public string Get(string firstName, string lastName, string address)
{
}

Ja, ich löse es jetzt mit nur einer Aktion, die alle Attribute berücksichtigt, nach denen ich eine Person suchen möchte. Wie eine allgemeine Suche. Ich würde es jedoch vorziehen, wenn es eine Möglichkeit gibt, Aktionen in einem Controller zu überladen, aber das ist möglicherweise nicht der Fall.
mstrand

3
Dies funktioniert nicht mit .net Core 2.0, da tatsächlich keine gültige URL-Vorlage generiert wird.
ZZZ

44

Um die Suchparameter aus der URL zu analysieren, müssen Sie die Parameter der Controller-Methode mit folgenden Anmerkungen versehen [FromQuery]:

[Route("api/person")]
public class PersonController : Controller
{
    [HttpGet]
    public string GetById([FromQuery]int id)
    {

    }

    [HttpGet]
    public string GetByName([FromQuery]string firstName, [FromQuery]string lastName)
    {

    }

    [HttpGet]
    public string GetByNameAndAddress([FromQuery]string firstName, [FromQuery]string lastName, [FromQuery]string address)
    {

    }
}

6
warum brauchst du das Die Parameterbindung aus der Abfragezeichenfolge erfolgt standardmäßig ...
metalheart

Ich habe beides versucht, aber das Überladen, wie ich es versuche, schlägt mit oder ohne [FromQuery]
fehl

2
@mstrand Ich habe aktualisiert - probieren Sie es aus, sehen Sie sich die zusätzlichen [HttpGet]Anmerkungen, die verschiedenen Methodennamen und die spezifische Route an [Route]- die Routen sollten jetzt vollständig explizit sein, wodurch einige mögliche Probleme beseitigt werden.
Mark Hughes

9

Ich denke, der einfachste Weg ist, einfach zu benutzen AttributeRouting.

[Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }

Kann ich den bevorzugten Referenztyp verwenden? Das heißt,int paramOne, string paramTwo
k4s

Verwenden Sie [Route ("api / YOURCONTROLLER / {paramOne} / {paramTwo?}")], Wenn Ihr zweiter Parameter optional sein soll
Anytoe

8

Ich würde vorschlagen, ein separates dto-Objekt als Argument zu verwenden:

[Route("api/[controller]")]
public class PersonController : Controller
{
    public string Get([FromQuery] GetPersonQueryObject request)
    {
        // Your code goes here
    }
}

public class GetPersonQueryObject 
{
    public int? Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string Address { get; set; }
}

Dotnet ordnet die Felder Ihrem Objekt zu.

Dies erleichtert das Durchlaufen Ihrer Parameter erheblich und führt zu einem viel klareren Code.


5

Zum Aufrufen get mit mehreren Parametern im Web-API-Core

  [ApiController]
    [Route("[controller]")]
    public class testController : Controller
    {

      [HttpGet]
        [Route("testaction/{id:int}/{startdate}/{enddate}")]
        public IEnumerable<classname> test_action(int id, string startdate, string enddate)
        {

            return List_classobject;
        }

    }

In web browser
https://localhost:44338/test/testaction/3/2010-09-30/2012-05-01

3

Im Folgenden finden Sie eine Zusammenfassung, um weitere Details zu der Überlastung hinzuzufügen, nach der Sie in Ihrem Kommentar nach einer anderen Antwort gefragt haben. Die Kommentare in der ApiControllerShow zeigen, welche Aktion bei jeder GETAbfrage aufgerufen wird :

public class ValuesController : ApiController
{
    // EXPLANATION: See the view for the buttons which call these WebApi actions. For WebApi controllers, 
    //          there can only be one action for a given HTTP verb (GET, POST, etc) which has the same method signature, (even if the param names differ) so
    //          you can't have Get(string height) and Get(string width), but you can have Get(int height) and Get(string width).
    //          It isn't a particularly good idea to do that, but it is true. The key names in the query string must match the
    //          parameter names in the action, and the match is NOT case sensitive. This demo app allows you to test each of these
    //          rules, as follows:
    // 
    // When you send an HTTP GET request with no parameters (/api/values) then the Get() action will be called.
    // When you send an HTTP GET request with a height parameter (/api/values?height=5) then the Get(int height) action will be called.
    // When you send an HTTP GET request with a width parameter (/api/values?width=8) then the Get(string width) action will be called.
    // When you send an HTTP GET request with height and width parameters (/api/values?height=3&width=7) then the 
    //          Get(string height, string width) action will be called.
    // When you send an HTTP GET request with a depth parameter (/api/values?depth=2) then the Get() action will be called
    //          and the depth parameter will be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with height and depth parameters (/api/values?height=4&depth=5) then the Get(int height) 
    //          action will be called, and the depth parameter would need to be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with width and depth parameters (/api/values?width=3&depth=5) then the Get(string width) 
    //          action will be called, and the depth parameter would need to be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with height, width and depth parameters (/api/values?height=7&width=2&depth=9) then the 
    //          Get(string height, string width) action will be called, and the depth parameter would need to be obtained from 
    //          Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with a width parameter, but with the first letter of the parameter capitalized (/api/values?Width=8) 
    //          then the Get(string width) action will be called because the case does NOT matter.
    // NOTE: If you were to uncomment the Get(string height) action below, then you would get an error about there already being  
    //          a member named Get with the same parameter types. The same goes for Get(int id).
    //
    // ANOTHER NOTE: Using the nullable operator (e.g. string? paramName) you can make optional parameters. It would work better to
    //          demonstrate this in another ApiController, since using nullable params and having a lot of signatures is a recipe
    //          for confusion.

    // GET api/values
    public IEnumerable<string> Get()
    {
        return Request.GetQueryNameValuePairs().Select(pair => "Get() => " + pair.Key + ": " + pair.Value);
        //return new string[] { "value1", "value2" };
    }

    //// GET api/values/5
    //public IEnumerable<string> Get(int id)
    //{
    //    return new string[] { "Get(height) => height: " + id };
    //}

    // GET api/values?height=5
    public IEnumerable<string> Get(int height) // int id)
    {
        return new string[] { "Get(height) => height: " + height };
    }

    // GET api/values?height=3
    public IEnumerable<string> Get(string height)
    {
        return new string[] { "Get(height) => height: " + height };
    }

    //// GET api/values?width=3
    //public IEnumerable<string> Get(string width)
    //{
    //    return new string[] { "Get(width) => width: " + width };
    //}

    // GET api/values?height=4&width=3
    public IEnumerable<string> Get(string height, string width)
    {
        return new string[] { "Get(height, width) => height: " + height + ", width: " + width };
    }
}

Sie würden dafür nur eine einzige Route benötigen, falls Sie sich fragen:

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

und Sie könnten alles mit dieser MVC-Ansicht oder etwas Ähnlichem testen. Ja, ich weiß, dass Sie JavaScript nicht mit Markup mischen sollen, und ich verwende Bootstrap nicht wie normalerweise, aber dies dient nur zu Demozwecken.

<div class="jumbotron">
    <h1>Multiple parameters test</h1>
    <p class="lead">Click a link below, which will send an HTTP GET request with parameters to a WebAPI controller.</p>
</div>
<script language="javascript">
    function passNothing() {
        $.get("/api/values", function (data) { alert(data); });
    }

    function passHeight(height) {
        $.get("/api/values?height=" + height, function (data) { alert(data); });
    }

    function passWidth(width) {
        $.get("/api/values?width=" + width, function (data) { alert(data); });
    }

    function passHeightAndWidth(height, width) {
        $.get("/api/values?height=" + height + "&width=" + width, function (data) { alert(data); });
    }

    function passDepth(depth) {
        $.get("/api/values?depth=" + depth, function (data) { alert(data); });
    }

    function passHeightAndDepth(height, depth) {
        $.get("/api/values?height=" + height + "&depth=" + depth, function (data) { alert(data); });
    }

    function passWidthAndDepth(width, depth) {
        $.get("/api/values?width=" + width + "&depth=" + depth, function (data) { alert(data); });
    }

    function passHeightWidthAndDepth(height, width, depth) {
        $.get("/api/values?height=" + height + "&width=" + width + "&depth=" + depth, function (data) { alert(data); });
    }

    function passWidthWithPascalCase(width) {
        $.get("/api/values?Width=" + width, function (data) { alert(data); });
    }
</script>
<div class="row">
    <button class="btn" onclick="passNothing();">Pass Nothing</button>
    <button class="btn" onclick="passHeight(5);">Pass Height of 5</button>
    <button class="btn" onclick="passWidth(8);">Pass Width of 8</button>
    <button class="btn" onclick="passHeightAndWidth(3, 7);">Pass Height of 3 and Width of 7</button>
    <button class="btn" onclick="passDepth(2);">Pass Depth of 2</button>
    <button class="btn" onclick="passHeightAndDepth(4, 5);">Pass Height of 4 and Depth of 5</button>
    <button class="btn" onclick="passWidthAndDepth(3, 5);">Pass Width of 3 and Depth of 5</button>
    <button class="btn" onclick="passHeightWidthAndDepth(7, 2, 9);">Pass Height of 7, Width of 2 and Depth of 9</button>
    <button class="btn" onclick="passHeightWidthAndDepth(7, 2, 9);">Pass Height of 7, Width of 2 and Depth of 9</button>
    <button class="btn" onclick="passWidthWithPascalCase(8);">Pass Width of 8, but with Pascal case</button>
</div>

1

Geben Sie hier die Bildbeschreibung ein

NB-Ich habe FromURI entfernt. Trotzdem kann ich einen Wert von der URL übergeben und ein Ergebnis erhalten. Wenn jemand Benfifts mit fromuri kennt, lass es mich wissen


Wie in der Dokumentation zur Parameterbindung [1] für einfache Typen festgelegt, werden "(int, bool, double usw.) sowie TimeSpan, DateTime, Guid, decimal und string automatisch aus dem URI gelesen. Das Attribut [FromURI] ist erforderlich, wenn der Parameter in keinem dieser Typen das Lesen der Parameter aus dem URI anstelle ihres Standardorts, des Körpers, erzwingt. Der Vollständigkeit halber macht das Attribut [FromBody] bei komplexen Typen im Wesentlichen das Gegenteil. [1] docs.microsoft.com/en-us/aspnet/web-api/overview/… )
Seb Andraos

1

Sie können einfach Folgendes tun:

    [HttpGet]
    public async Task<IActionResult> GetAsync()
    {
        string queryString = Request.QueryString.ToString().ToLower();

        return Ok(await DoMagic.GetAuthorizationTokenAsync(new Uri($"https://someurl.com/token-endpoint{queryString}")));
    }

Wenn Sie auf jedes Element separat zugreifen müssen, lesen Sie einfach Request.Query.


1

Methoden sollten so sein:

[Route("api/[controller]")]
public class PersonsController : Controller
{
    [HttpGet("{id}")]
    public Person Get(int id)

    [HttpGet]
    public Person[] Get([FromQuery] string firstName, [FromQuery] string lastName, [FromQuery] string address)
}

Beachten Sie, dass die zweite Methode ein Array von Objekten zurückgibt und der Name des Controllers in Plurar steht (Personen nicht Person).

Wenn Sie also eine Ressource anhand ihrer ID erhalten möchten, ist dies:

api/persons/1

Wenn Sie Objekte anhand einiger Suchkriterien wie Vorname usw. übernehmen möchten, können Sie wie folgt suchen:

api/persons?firstName=Name&...

Wenn Sie beispielsweise die Bestellungen dieser Person annehmen möchten, sollte dies folgendermaßen aussehen:

api/persons/1/orders?skip=0&take=20

Und Methode im selben Controller:

    [HttpGet("{personId}/orders")]
    public Orders[] Get(int personId, int skip, int take, etc..)

0
    public HttpResponseMessage Get(int id,string numb)
    {

        using (MarketEntities entities = new MarketEntities())
        {
          var ent=  entities.Api_For_Test.FirstOrDefault(e => e.ID == id && e.IDNO.ToString()== numb);
            if (ent != null)
            {
                return Request.CreateResponse(HttpStatusCode.OK, ent);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Applicant with ID " + id.ToString() + " not found in the system");
            }
        }
    }

0

Einfachster Weg,

Regler:

[HttpGet("empId={empId}&startDate={startDate}&endDate={endDate}")]
 public IEnumerable<Validate> Get(int empId, string startDate, string endDate){}

Postbotenanfrage:

{router}/empId=1&startDate=2020-20-20&endDate=2020-20-20

Lernpunkt: Das genaue Muster anfordern wird vom Controller akzeptiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.