วิธีการส่งผ่านพารามิเตอร์หลายตัวไปยังเมธอด get ใน ASP.NET Core


115

ฉันจะส่งผ่านพารามิเตอร์หลายตัวเพื่อรับเมธอดในตัวควบคุม MVC 6 ได้อย่างไร ตัวอย่างเช่นฉันต้องการที่จะมีดังต่อไปนี้

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

    }
}

ดังนั้นฉันสามารถสอบถามเช่น

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

คำตอบ:


104

คุณยังสามารถใช้สิ่งนี้:

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

หมายเหตุ : โปรดดู metalheart และmetalheartและMark Hughesสำหรับวิธีการอาจจะดีกว่า


23
จนกว่าคุณจะต้องรับทุกคนที่มีนามสกุลเดียวกัน :)
Phillip Copley

18
นั่นเป็นวิธีที่แย่มากในการออกแบบเส้นทาง API ... ไม่น่าสนใจเลย
Thomas Levesque

8
วิธีการข้างต้นดูยุ่งยากมากอย่าเข้าใจว่าทำไมจึงมีการโหวตเพิ่มขึ้นมากมาย
Bernoulli IT

1
@ThomasLevesque คุณหมายถึงอะไรที่มันไม่สงบ?
Bruno Santos

2
@BrunoSantos มันไม่เป็นไปตามหลักการของ REST URI ควรจะระบุทรัพยากรที่ไม่ซ้ำกัน นี่ไม่ใช่กรณีนี้ (อาจมีหลายคนที่มีชื่อและนามสกุลเหมือนกันและที่อยู่ไม่สามารถถือเป็นตัวระบุได้อย่างแน่นอน)
Thomas Levesque

64

ทำไมไม่ใช้คอนโทรลเลอร์เพียงตัวเดียว

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

อีกทางเลือกหนึ่งคือใช้การกำหนดเส้นทางแอตทริบิวต์ แต่คุณจะต้องมีรูปแบบ URL อื่น:

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

ใช่ตอนนี้ฉันแก้ปัญหาโดยใช้การดำเนินการเพียงครั้งเดียวในคุณลักษณะทั้งหมดที่ฉันต้องการให้สามารถค้นหาบุคคลได้ เหมือนการค้นหาทั่วไป. ฉันต้องการแม้ว่าจะมีวิธีที่จะมีการกระทำที่มากเกินไปในคอนโทรลเลอร์ แต่อาจไม่เป็นเช่นนั้น
mstrand

3
สิ่งนี้ใช้ไม่ได้กับ. net core 2.0 เนื่องจากไม่มีการสร้างเทมเพลต url ที่ถูกต้อง
ZZZ

45

ในการแยกวิเคราะห์พารามิเตอร์การค้นหาจาก URL คุณต้องใส่คำอธิบายประกอบพารามิเตอร์วิธีการควบคุมด้วย[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)
    {

    }
}

7
ทำไมคุณถึงต้องการสิ่งนี้? การผูกพารามิเตอร์จากสตริงการสืบค้นจะเกิดขึ้นตามค่าเริ่มต้น ...
metalheart

1
ฉันได้ลองทั้งสองอย่างแล้ว แต่ก็ทำงานหนักเกินไปในขณะที่พยายามทำล้มเหลวโดยมีหรือไม่มี [FromQuery]
mstrand

2
@mstrand ฉันได้อัปเดตแล้ว - ลองดู[HttpGet]คำอธิบายประกอบเพิ่มเติมชื่อวิธีการต่างๆและเส้นทางเฉพาะใน[Route]ตอนนี้เส้นทางควรมีความชัดเจนอย่างเต็มที่ซึ่งจะช่วยขจัดปัญหาที่อาจเกิดขึ้นได้เล็กน้อย
Mark Hughes

13

ฉันขอแนะนำให้ใช้วัตถุ dto แยกต่างหากเป็นอาร์กิวเมนต์:

[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 จะแมปฟิลด์กับวัตถุของคุณ

สิ่งนี้จะทำให้การส่งผ่านพารามิเตอร์ของคุณง่ายขึ้นมากและจะส่งผลให้โค้ดชัดเจนขึ้นมาก


9

AttributeRoutingผมคิดว่าวิธีที่ง่ายที่สุดก็คือการใช้งาน

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

ฉันสามารถใช้ประเภทการอ้างอิงที่ต้องการได้หรือไม่? นั่นคือint paramOne, string paramTwo
k4s

ใช้ [เส้นทาง ("api / YOURCONTROLLER / {paramOne} / {paramTwo?}")] หากคุณต้องการให้พารามิเตอร์ที่สองเป็นทางเลือก
Anytoe

7

ในการเรียกรับด้วยพารามิเตอร์หลายตัวใน 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

หากต้องการเพิ่มรายละเอียดเพิ่มเติมเกี่ยวกับการโอเวอร์โหลดที่คุณถามในความคิดเห็นของคุณหลังจากคำตอบอื่นนี่คือบทสรุป ความคิดเห็นในการApiControllerแสดงว่าการดำเนินการใดที่จะถูกเรียกในแต่ละGETแบบสอบถาม:

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

คุณต้องการเพียงเส้นทางเดียวสำหรับสิ่งนี้ในกรณีที่คุณสงสัย:

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

และคุณสามารถทดสอบทั้งหมดได้ด้วยมุมมอง MVC นี้หรืออะไรสักอย่าง ใช่ฉันรู้ว่าคุณไม่ควรผสม JavaScript กับมาร์กอัปและฉันไม่ได้ใช้ bootstrap เหมือนที่คุณทำตามปกติ แต่นี่มีไว้เพื่อการสาธิตเท่านั้น

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

3

วิธีการควรเป็นดังนี้:

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

โปรดทราบว่าวิธีที่สองส่งคืนอาร์เรย์ของอ็อบเจ็กต์และชื่อคอนโทรลเลอร์อยู่ใน plurar (บุคคลไม่ใช่บุคคล)

ดังนั้นหากคุณต้องการรับทรัพยากรโดย id จะเป็น:

api/persons/1

หากคุณต้องการใช้วัตถุตามเกณฑ์การค้นหาบางอย่างเช่นชื่อและอื่น ๆ คุณสามารถค้นหาดังนี้:

api/persons?firstName=Name&...

และก้าวต่อไปหากคุณต้องการรับคำสั่งของบุคคลนั้น (เช่น) ควรเป็นดังนี้:

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

และวิธีการในตัวควบคุมเดียวกัน:

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

1

ป้อนคำอธิบายภาพที่นี่

NB-I ลบ FromURI ยังคงสามารถส่งผ่านค่าจาก URL และรับผลลัพธ์ได้หากใครรู้จัก benfifts โดยใช้ fromuri แจ้งให้เราทราบ


ตามที่ระบุไว้ในเอกสารสำหรับการผูกพารามิเตอร์ [1] ประเภทแบบง่าย "(int, bool, double และอื่น ๆ ) บวก TimeSpan, DateTime, Guid, decimal และ string" จะถูกอ่านโดยอัตโนมัติจาก URI แอตทริบิวต์ [FromURI] เป็นสิ่งจำเป็นเมื่อพารามิเตอร์ไม่ได้อยู่ในประเภทใดประเภทหนึ่งเหล่านี้เพื่อบังคับให้อ่านค่าจาก URI แทนที่จะเป็นตำแหน่งเริ่มต้นคือ body เพื่อความสมบูรณ์แอตทริบิวต์ [FromBody] จึงตรงกันข้ามกับประเภทที่ซับซ้อนเป็นหลัก [1] docs.microsoft.com/en-us/aspnet/web-api/overview/… )
Seb

1

คุณสามารถทำสิ่งต่อไปนี้:

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

Request.Queryหากคุณจำเป็นต้องเข้าถึงแต่ละองค์ประกอบแยกกันก็หมายถึง


1

วิธีที่ง่ายที่สุด

ตัวควบคุม:

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

คำขอบุรุษไปรษณีย์:

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

จุดเรียนรู้: ผู้ควบคุมจะยอมรับรูปแบบที่แน่นอน


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");
            }
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.