Dlaczego musimy określić FromBody i FromUri?

157

Dlaczego atrybuty FromBodyi są FromUripotrzebne w interfejsie ASP.NET Web API?

Jakie są różnice między używaniem atrybutów a ich nieużywaniem?

Rajneesh
źródło
11
Podpowiedź, kiedy użycie adnotacji [FromBody] może być przydatne: na przykład przesyłanie statycznych danych uwierzytelniających, takich jak nazwa użytkownika / hasło, jako parametrów zakodowanych w adresie URL, jest kiepską praktyką. Mimo że szyfrowanie SSL może uniemożliwić osobie trzeciej uzyskanie dostępu do odczytu parametrów w adresie URL, nadal pozostaje to kiepska praktyka, ponieważ te poświadczenia mogą być przechowywane w dziennikach przeglądarki i równe, co zdecydowanie nie jest pożądane. W takim przypadku można by użyć adnotacji [FromBody], aby wymusić przechowywanie parametru w treści wiadomości HTTP, wprowadzając wysoki
Chris

Odpowiedzi:

193

Gdy interfejs API sieci Web ASP.NET wywołuje metodę na kontrolerze, musi ustawić wartości parametrów, czyli proces nazywany powiązaniem parametrów .

Domyślnie interfejs API sieci Web używa następujących reguł do powiązania parametrów:

  • Jeśli parametr jest „prostym” typem , interfejs API sieci Web próbuje pobrać wartość z identyfikatora URI . Typy proste obejmują typy pierwotne .NET (int, bool, double itd.), A także TimeSpan, DateTime, Guid, decimal i string, a także dowolny typ z konwerterem typów, który można konwertować z ciągu.

  • W przypadku typów złożonych interfejs API sieci Web próbuje odczytać wartość z treści wiadomości przy użyciu programu formatującego typu nośnika.

Jeśli więc chcesz zastąpić powyższe domyślne zachowanie i wymusić odczytanie typu złożonego z identyfikatora URI przez interfejs API sieci Web, dodaj [FromUri]atrybut do parametru. Aby zmusić interfejs API sieci Web do odczytu prostego typu z treści żądania, dodaj [FromBody]atrybut do parametru.

Tak więc, aby odpowiedzieć na Twoje pytanie, potrzebą atrybutu [FromBody]i [FromUri]w interfejsie API sieci Web jest po prostu zastąpienie, jeśli to konieczne, domyślnego zachowania opisanego powyżej. Zauważ, że możesz użyć obu atrybutów dla metody kontrolera, ale tylko dla różnych parametrów, jak pokazano tutaj .

W internecie jest o wiele więcej informacji, jeśli wpiszesz w google „wiązanie parametrów interfejsu API sieci Web”.

djikay
źródło
2
@ user3510527: Nie musisz używać tych atrybutów, jeśli nie chcesz, o ile przestrzegasz domyślnego zachowania. Jeśli chcesz zmienić domyślne zachowanie, musisz ich użyć.
djikay,
1
jeśli robi swoje domyślne zachowanie, to dlaczego musimy przesadzić i jakie korzyści odniesiemy, jeśli wspomnimy o tym atrybucie?
Rajneesh
1
@ user3510527 Nie musisz nadpisywać. Możesz po prostu użyć domyślnego zachowania. Jednym z przykładów sytuacji, w której ktoś może chcieć przesłonić, jest podanie prostej liczby całkowitej w treści żądania, ponieważ domyślnie będzie oczekiwać, że znajdzie ją w identyfikatorze URI. Zasadniczo możesz po prostu pozostawić domyślne zachowanie, jeśli chcesz, lub możesz je zastąpić, to tylko opcja, którą masz. Nie rozumiem, na czym polega zamieszanie.
djikay,
Ja po prostu chcę wiedzieć wewnętrznego procesu pracy, jeśli używamy atrybutu formy, więc bezpośrednio będzie uzyskać wartość i nie sprawdzić wszelkie URI lub formbody ...
Rajneesh
7
Zastanawiam się, czy ktoś mógłby uczynić atrybut o nazwie JustGetIt, który służy temu samemu celowi dodawania wiele atrybutów jak [FromBody, FromQuery]etc
Muffin Man
93

Domyślne zachowanie to:

  1. Jeżeli parametr jest prymitywny typ ( int, bool, double, ...), próbuje Web API, aby uzyskać wartość z URI żądania HTTP.

  2. W przypadku typów złożonych (na przykład własny obiekt Person:) interfejs API sieci Web próbuje odczytać wartość z treści żądania HTTP.

Więc jeśli masz:

  • typ pierwotny w identyfikatorze URI lub
  • złożony typ ciała

... wtedy nie musisz dodawać żadnych atrybutów (ani [FromBody]ani [FromUri]).

Ale jeśli masz prymitywny typ w organizmie , to trzeba dodać [FromBody]przed swoim prymitywnym parametru typu w metodzie kontrolera WebAPI. (Ponieważ domyślnie interfejs WebAPI szuka typów pierwotnych w identyfikatorze URI żądania HTTP).

Lub, jeśli masz złożony typ w identyfikatorze URI , musisz dodać [FromUri]. (Ponieważ domyślnie interfejs WebAPI domyślnie szuka w treści żądania HTTP typów złożonych).

Typy pierwotne:

public class UsersController : ApiController
{
    // api/users
    public HttpResponseMessage Post([FromBody]int id)
    {

    }
    // api/users/id
    public HttpResponseMessage Post(int id)
    {

    }       
}

Złożone typy:

public class UsersController : ApiController
{       
    // api/users
    public HttpResponseMessage Post(User user)
    {

    }

    // api/users/user
    public HttpResponseMessage Post([FromUri]User user)
    {

    }       
}

Działa to tak długo, jak długo wysyłasz tylko jeden parametr w żądaniu HTTP. Wysyłając wiele , musisz stworzyć niestandardowy model, który ma wszystkie parametry, takie jak:

public class MyModel
{
    public string MyProperty { get; set; }
    public string MyProperty2 { get; set; }
}

[Route("search")]
[HttpPost]
public async Task<dynamic> Search([FromBody] MyModel model)
{
    // model.MyProperty;
    // model.MyProperty2;
}

Z dokumentacji firmy Microsoft dotyczącej wiązania parametrów w ASP.NET Web API :

Gdy parametr ma [FromBody], interfejs API sieci Web używa nagłówka Content-Type do wybrania programu formatującego. W tym przykładzie typ zawartości to „application / json”, a treść żądania to nieprzetworzony ciąg JSON (nie obiekt JSON). Z treści wiadomości można odczytać co najwyżej jeden parametr.

To powinno działać:

public HttpResponseMessage Post([FromBody] string name) { ... }

To nie zadziała:

// Caution: This won't work!    
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }

Powodem tej reguły jest to, że treść żądania może być przechowywana w niebuforowanym strumieniu, który można odczytać tylko raz.

Tadej
źródło
5
Szczególnie pomocna była informacja „Z treści wiadomości można odczytać co najwyżej jeden parametr”
Ryan
15

Tylko dodatek do powyższych odpowiedzi ...

[FromUri] może również służyć do wiązania typów złożonych z parametrów uri zamiast przekazywania parametrów z kwerendy

Dla Ex ...

public class GeoPoint
{
    public double Latitude { get; set; } 
    public double Longitude { get; set; }
}

[RoutePrefix("api/Values")]
public ValuesController : ApiController
{
    [Route("{Latitude}/{Longitude}")]
    public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}

Można go nazwać:

http://localhost/api/values/47.678558/-122.130989
Utkarsh Patel
źródło
12

Gdy parametr ma [FromBody], interfejs API sieci Web używa nagłówka Content-Type do wybrania programu formatującego. W tym przykładzie typ zawartości to „application / json”, a treść żądania to nieprzetworzony ciąg JSON (nie obiekt JSON).

Z treści wiadomości można odczytać co najwyżej jeden parametr. Więc to nie zadziała:

 // Caution: Will not work!    
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }

Powodem tej reguły jest to, że treść żądania może być przechowywana w niebuforowanym strumieniu, który można odczytać tylko raz

Więcej informacji można znaleźć na stronie internetowej: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

user5166729
źródło