Jak odczytywać wartości z kwerendy za pomocą ASP.NET Core?

151

Buduję jeden interfejs API RESTful przy użyciu ASP.NET Core MVC i chcę użyć parametrów kwerendy, aby określić filtrowanie i stronicowanie zasobu, który zwraca kolekcję.

W takim przypadku muszę odczytać wartości przekazane w kwerendzie do filtrowania i wybrać wyniki do zwrócenia.

Dowiedziałem się już, że wewnątrz Getakcji kontrolera dostęp HttpContext.Request.Queryzwraca jeden IQueryCollection.

Problem w tym, że nie wiem, w jaki sposób jest używany do pobierania wartości. Prawdę mówiąc, pomyślałem, że należy na przykład użyć

string page = HttpContext.Request.Query["page"]

Problem polega na tym, HttpContext.Request.Query["page"]że nie zwraca ciągu, ale plik StringValues.

W każdym razie, w jaki sposób można użyć, IQueryCollectionaby faktycznie odczytać wartości zapytania?

user1620696
źródło
1
Napisałem post o tym samym, który można znaleźć tutaj: neelbhatt40.wordpress.com/2017/07/06/…
Neel

Odpowiedzi:

147

Możesz użyć [FromQuery]do powiązania określonego modelu z querystringiem:

https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding

na przykład

[HttpGet()]
public IActionResult Get([FromQuery(Name = "page")] string page)
{...}
Mike_G
źródło
7
Myślę, że [FromQuery]atrybut może również zostać pominięty, ponieważ wiązanie .net będzie domyślnie sprawdzać wszystkie dane wejściowe formularza i parametry zapytania o adres URL, z wyjątkiem tego, że masz jakiś powód, aby ograniczyć jego źródło.
S.Serpooshan
14
(Nazwa = "strona") jest niepotrzebna - zostanie powiązana ze zmienną, jeśli zostanie nazwana tak samo
a3uge
Jest to ważne, jeśli nazwa parametru querystring ma strukturę. Na przykład „object.propname”
Jose Capistrano
106

Możesz użyć metody ToString, IQueryCollectionktóra zwróci żądaną wartość, jeśli pagezostanie określony pojedynczy parametr:

string page = HttpContext.Request.Query["page"].ToString();

jeśli istnieje wiele wartości, takich jak ?page=1&page=2to, wynik wywołania ToString będzie1,2

Ale jak @ mike-g zasugerował w swojej odpowiedzi, lepiej byłoby użyć wiązania modelu, a nie bezpośredniego dostępu do HttpContext.Request.Queryobiektu.

Darin Dimitrov
źródło
7
ToString nie jest konieczne. Kompilator wyrzuci go niejawnie, jeśli przypiszesz wartość zapytania do łańcucha ...
Stefan Steiger
31

Rdzeń ASP.NET automatycznie wiążą form values, route valuesi query stringspo imieniu. Oznacza to, że możesz po prostu zrobić to:

[HttpGet()]
public IActionResult Get(int page)
{ ... }

MVC spróbuje powiązać dane żądania z parametrami akcji według nazwy ... poniżej znajduje się lista źródeł danych w kolejności, w jakiej przegląda je powiązanie modelu

  1. Form values: Są to wartości formularzy, które są przesyłane w żądaniu HTTP przy użyciu metody POST. (w tym żądania POST jQuery).

  2. Route values: Zestaw wartości tras dostarczonych przez Routing

  3. Query strings: Część ciągu zapytania identyfikatora URI.

Źródło: powiązanie modelu w ASP.NET Core


FYI, możesz również połączyć podejście automatyczne i jawne:

[HttpGet()]
public IActionResult Get(int page
     , [FromQuery(Name = "page-size")] int pageSize)
{ ... }
spottedmahn
źródło
25

Oto przykładowy kod, którego użyłem (z widokiem .NET Core):

@{
    Microsoft.Extensions.Primitives.StringValues queryVal;

    if (Context.Request.Query.TryGetValue("yourKey", out queryVal) &&
        queryVal.FirstOrDefault() == "yourValue")
    {
    }
}
Pavel
źródło
5
Głosuj za dołączeniem PEŁNEJ nazwy obiektu (lub poprawnej instrukcji using). Doprowadza mnie do szału, gdy ludzie umieszczają tylko nazwę obiektu bez pełnego kwestionowania lub przynajmniej instrukcji using. Dzięki.
granadaCoder
23

Możesz po prostu utworzyć taki obiekt:

public class SomeQuery
{
    public string SomeParameter { get; set; }
    public int? SomeParameter2 { get; set; }
}

A potem w kontrolerze po prostu zrób coś takiego:

[HttpGet]
public IActionResult FindSomething([FromQuery] SomeQuery query)
{
    // Your implementation goes here..
}

Co więcej, możesz stworzyć model API z:

[HttpGet]
public IActionResult GetSomething([FromRoute] int someId, [FromQuery] SomeQuery query)

do:

[HttpGet]
public IActionResult GetSomething(ApiModel model)

public class ApiModel
{
    [FromRoute]
    public int SomeId { get; set; }
    [FromQuery]
    public string SomeParameter { get; set; }
    [FromQuery]
    public int? SomeParameter2 { get; set; }
}
Mariusz
źródło
Do jakiego adresu URL to się odnosi? Jestem w tym nowy, więc nie mogę „cofnąć inżynierii” do adresu URL. Czy byłoby to coś w rodzaju example.com/somequery/… ?
Christian,
1
@Christian jeśli nie zmienisz żadnej konwencji, będzie to example.com/[controller]/[akcja]/{someid:int}?someparameter=1&someparameter2=2
LxL
16

StringValuesjest tablicą ciągów . Możesz uzyskać wartość ciągu, podając indeks, np HttpContext.Request.Query["page"][0].

Constantinos G.
źródło
2
Dziękuję Ci; była to jedyna odpowiedź, która faktycznie odpowiedziała na pytanie. (W moim przypadku nie mogę użyć wiązania, ponieważ mam bardziej złożoną logikę, taką jak „najpierw wypróbuj ciąg zapytania, jeśli go brakuje, spróbuj sesji i tak dalej”.)
Marcel Popescu
8

IQueryCollectionma na sobie znak TryGetValue(), który zwraca wartość z podanym kluczem. Tak więc, jeśli masz parametr zapytania o nazwie someInt, możesz go użyć w następujący sposób:

var queryString = httpContext.Request.Query;
StringValues someInt;
queryString.TryGetValue("someInt", out someInt);
var daRealInt = int.Parse(someInt);

Zauważ, że jeśli nie masz wielu parametrów o tej samej nazwie, StringValuestyp nie stanowi problemu.

steamrolla
źródło
Aby dodać do tej odpowiedzi, jeśli wywołasz StringValues.ToString (), możesz rzutować ją bezpośrednio na łańcuch, jeśli tego potrzebujesz.
eltiare
Przyszli czytelnicy: w pełni kwalifikowana nazwa "Microsoft.AspNetCore.Http.IQueryCollection queryString = this.Request.Query;" Mój był w „Assembly Microsoft.AspNetCore.Http.Features, Version = 3.1.0.0” i „Microsoft.Extensions.Primitives.StringValues” (mój był w „Assembly Microsoft.Extensions.Primitives, Version = 3.1.2.0,”)
granadaCoder
3

w .net core, jeśli chcesz uzyskać dostęp do querystringu, użyj go jak

@Context.Request.Query["yourKey"]

jeśli jesteśmy w miejscu, w którym @Context nie jest dostępny, możemy go wstrzyknąć

@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
@if (HttpContextAccessor.HttpContext.Request.Query.Keys.Contains("yourKey"))
{
      <text>do something </text>
}

także dla plików cookie

HttpContextAccessor.HttpContext.Request.Cookies["DeniedActions"]
M.Ali El-Sayed
źródło
2
Nie potrzeba całego tego kodu. Po prostu użyj @ Context.Request.Query ["yourKey"]
Shadi Namrouti
Tak, @ShadiNamrouti, masz rację na widoku, gdzie \ @Context jest dostępny, możemy go użyć jak \ @ Context.Request.Query ["yourKey"], ale jeśli jesteśmy w kontrolerze, musimy wstrzyknąć HttpContextAccessor.HttpContext.
M.Ali El-Sayed
3

Może to pomaga. Aby uzyskać parametr ciągu zapytania w widoku

Widok:

@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
@{ Context.Request.Query["uid"]}

Startup.cs ConfigureServices:

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Sinan Dogan
źródło
2

Mam lepsze rozwiązanie tego problemu,

  • request jest członkiem abstrakcyjnej klasy ControllerBase
  • GetSearchParams () jest metodą rozszerzającą utworzoną w klasie pomocniczej poniżej.

var searchparams = await Request.GetSearchParams();

Utworzyłem klasę statyczną z kilkoma metodami rozszerzającymi

public static class HttpRequestExtension
{
  public static async Task<SearchParams> GetSearchParams(this HttpRequest request)
        {
            var parameters = await request.TupledParameters();

            try
            {
                for (var i = 0; i < parameters.Count; i++)
                {
                    if (parameters[i].Item1 == "_count" && parameters[i].Item2 == "0")
                    {
                        parameters[i] = new Tuple<string, string>("_summary", "count");
                    }
                }
                var searchCommand = SearchParams.FromUriParamList(parameters);
                return searchCommand;
            }
            catch (FormatException formatException)
            {
                throw new FhirException(formatException.Message, OperationOutcome.IssueType.Invalid, OperationOutcome.IssueSeverity.Fatal, HttpStatusCode.BadRequest);
            }
        }



public static async Task<List<Tuple<string, string>>> TupledParameters(this HttpRequest request)
{
        var list = new List<Tuple<string, string>>();


        var query = request.Query;
        foreach (var pair in query)
        {
            list.Add(new Tuple<string, string>(pair.Key, pair.Value));
        }

        if (!request.HasFormContentType)
        {
            return list;
        }
        var getContent = await request.ReadFormAsync();

        if (getContent == null)
        {
            return list;
        }
        foreach (var key in getContent.Keys)
        {
            if (!getContent.TryGetValue(key, out StringValues values))
            {
                continue;
            }
            foreach (var value in values)
            {
                list.Add(new Tuple<string, string>(key, value));
            }
        }
        return list;
    }
}

w ten sposób możesz łatwo uzyskać dostęp do wszystkich parametrów wyszukiwania. Mam nadzieję, że pomoże to wielu programistom :)

Shanjee
źródło
Czy czegoś mi brakuje - gdzie zdefiniowano FhirException, w jakiej przestrzeni nazw znajduje się Task <SearchParams>?
Doug Thompson - DouggyFresh
1

Niektóre komentarze również o tym wspominają, ale asp net core wykonuje całą tę pracę za Ciebie.

Jeśli masz ciąg zapytania, który pasuje do nazwy, będzie on dostępny w kontrolerze.

https: // myapi / some-endpoint / 123? someQueryString = YayThisWorks

[HttpPost]
[Route("some-endpoint/{someValue}")]
public IActionResult SomeEndpointMethod(int someValue, string someQueryString)
    {
        Debug.WriteLine(someValue);
        Debug.WriteLine(someQueryString);
        return Ok();
    }

Ouputs:

123

YayThisWorks

Jordania
źródło