Como devo passar vários parâmetros para um GET da API da Web do ASP.Net?

136

Eu estou usando a API da Web .Net MVC4 para (espero) implementar uma API RESTful. Preciso passar alguns parâmetros para o sistema e fazê-lo executar alguma ação e, em seguida, retornar uma lista de objetos como resultado. Especificamente, estou passando em duas datas e retornando registros que caem entre elas. Também acompanho os registros retornados para que as chamadas subseqüentes não sejam reprocessadas no sistema.

Eu considerei algumas abordagens:

  1. Serializando os parâmetros em uma única cadeia JSON e separando-a na API. http://forums.asp.net/t/1807316.aspx/1

  2. Passe os parâmetros na string de consulta.
    Qual é a melhor maneira de passar vários parâmetros de consulta para uma API repousante?

  3. Definindo os parâmetros na rota: api / controller / date1 / date2

  4. Usando um POST que inerentemente me permite passar um objeto com parâmetros.

  5. Pesquisando ODATA desde a API da Web (atualmente) suporta. Ainda não fiz muito com isso, então não estou muito familiarizado com isso.

Parece que as práticas REST adequadas indicam quando os dados são extraídos, você deve usar um GET. No entanto, o GET também deve ser nulipotente (não produz efeitos colaterais), e me pergunto se minha implementação específica viola isso, já que marquei registros no sistema da API, portanto, estou produzindo efeitos colaterais.

Também me levou à questão de suportar parâmetros variáveis. Se a lista de parâmetros de entrada mudar, seria tedioso redefinir sua rota para a Opção 3 se isso acontecer muito. E o que pode acontecer se os parâmetros forem definidos em tempo de execução ...

De qualquer forma, para minha implementação específica, qual opção (se houver) parece melhor?

sig606
fonte

Respostas:

10

O que significa essa marcação de registro? Se isso for usado apenas para fins de log, eu usaria GET e desabilitaria todo o cache, pois você deseja registrar todas as consultas desses recursos. Se a marcação de registro tiver outro objetivo, o POST é o caminho a percorrer. O usuário deve saber que suas ações afetam o sistema e o método POST é um aviso.

LukLed
fonte
Marcando, quero dizer simplesmente rastrear quais registros são processados ​​e retornados para que as chamadas subsequentes não os repitam. No meu caso, estou apenas inserindo outra tabela para rastrear quais são processados.
sig606
No momento, eu o implementei como POST, principalmente pelo motivo que você disse - as ações acontecem e o consumidor está ciente delas. Além disso, ele parece fácil e mais flexível com resp para passar dados diferentes no.
sig606
@ sig606: POST é o caminho a percorrer para mim, mas seu protocolo não parece seguro. E se algo acontecer e os registros forem recuperados no lado do cliente, mas não processados ​​devido a um bug? Você não os retornará mais e o cliente ficará com dados perdidos.
LukLed
No momento, minha API só retorna registros depois de processados. Portanto, o consumidor passa a API duas datas. Os registros entre essas duas datas são processados ​​e marcados. Em seguida, os dados são retornados ao chamador. Suponho que, se algo acontecer durante o processamento ou após o processamento antes de chegar ao cliente, eu tenho um problema.
sig606
141

Eu acho que a maneira mais fácil é simplesmente usar AttributeRouting.

É óbvio dentro do seu controlador, por que você quer isso no seu global WebApiConfigde arquivo?

Exemplo:

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

Os {}nomes precisam corresponder aos seus parâmetros.

Simples assim, agora você tem um separado GETque lida com vários parâmetros neste exemplo.

Mark Pieszak - Trilon.io
fonte
12
Isso é ótimo. A maioria das pessoas recomenda configurar a rota no WebApiConfigarquivo, mas isso é realmente melhor.
rhyek
4
De fato, nós (a maioria das pessoas) recomendamos ter uma área de gerenciamento centralizado para sua configuração. No caso de APIs da Web (Microsoft ou outras), os padrões centralizados para o REST são fundamentais. O roteamento de atributos é atraente, mas torna as exceções únicas muito tentadoras.
David Betz
3
Concordo, preciso atualizar minha resposta realmente. Existe uma maneira muito melhor de fazer vários parâmetros com GETs. Publiquei isso quando eu era mais novo no WebAPI, agora não uso AttributeRouting (a menos que não queira criar um novo Controller) e transmito todos os Parâmetros no QueryString, eles são mapeados automaticamente. Atualizando quando eu tiver uma chance para que as pessoas não usam este método mais antigo
Mark Pieszak - Trilon.io
Existe uma maneira de definir um Routepara parâmetros nomeados (por exemplo, parâmetros de consulta)?
Shimmy Weitzhandler
1
Se o nome do método de ação for necessário, isso poderá ser modificado para acomodar isso. [Route ("api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}")] public string Get (int paramOne, int paramTwo) {retorne "algo"; }
Dash
49

Basta adicionar uma nova rota às WebApiConfigentradas.

Por exemplo, para ligar para:

public IEnumerable<SampleObject> Get(int pageNumber, int pageSize) { ..

adicionar:

config.Routes.MapHttpRoute(
    name: "GetPagedData",
    routeTemplate: "api/{controller}/{pageNumber}/{pageSize}"
);

Em seguida, adicione os parâmetros à chamada HTTP:

GET //<service address>/Api/Data/2/10 
Graham Wright
fonte
10
Esta parece ser a única resposta que lista todas as partes. Eu gostaria que alguém melhor descrevesse como usar o api/controller?start=date1&end=date2estilo URI.
Hot Licks
@Hot Licks A resposta de Andrew Veriga funciona bem com argumentos de string de consulta. Essencialmente, você vincula os nomes das strings de consulta às propriedades da classe e os transmite ao seu método. Seu método utilizará um argumento de classe única marcado com o atributo [FromUri] e terá seus argumentos de string de consulta como suas propriedades.
David Peterson
Coisas boas. Obrigado!
Hugo Nava Kopp
oi @ HotLicks e GrahamWright, você acha que pode responder a esta pergunta? Obrigado, stackoverflow.com/questions/57565318/…
45

Eu apenas tive que implementar uma API RESTfull onde eu preciso passar parâmetros. Eu fiz isso passando os parâmetros na string de consulta no mesmo estilo descrito no primeiro exemplo de Mark "api / controller? Start = date1 & end = date2"

No controlador, usei uma dica do URL dividido em c #?

// uri: /api/courses
public IEnumerable<Course> Get()
{
    NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
    var system = nvc["System"];
    // BL comes here
    return _courses;
}

No meu caso, eu estava chamando o WebApi via Ajax da seguinte forma:

$.ajax({
        url: '/api/DbMetaData',
        type: 'GET',
        data: { system : 'My System',
                searchString: '123' },
        dataType: 'json',
        success: function (data) {
                  $.each(data, function (index, v) {
                  alert(index + ': ' + v.name);
                  });
         },
         statusCode: {
                  404: function () {
                       alert('Failed');
                       }
        }
   });

Eu espero que isso ajude...

Nigel Findlater
fonte
2
Eu acho que você não está usando WebAPI porque ParameterBinding irá mapear o querystring para os parâmetros do método da API automagicamente ...
emp
1
Sim, uma maneira melhor seria usar e atribuir como [Route ("api / DbMetaData / {system} / {searchString}")] e adicionar parâmetros ao Get (sistema de strings, string searchString) e depois chamar com " ... api / DbMetaData / mysystem / mysearchstring "
Nigel Findlater
Eu usei o exemplo dele no meu C # MVC WebApi e funcionou bem. +1 por exemplo
Si8 07/07
38

Encontrei solução excelente em http://habrahabr.ru/post/164945/

public class ResourceQuery
{
   public string Param1 { get; set; }
   public int OptionalParam2 { get; set; }
}

public class SampleResourceController : ApiController
{
    public SampleResourceModel Get([FromUri] ResourceQuery query)
    {
        // action
    }
}
Andrew Veriga
fonte
5
A pista aqui é o [FromUri]
tranceporter
2
Embora o artigo esteja em russo, o @tranceporter está certo. O "FromUri" parece uma ótima maneira de obter os parâmetros do URL. Outro artigo que podem ser úteis: asp.net/web-api/overview/formats-and-model-binding/...
Greg
É isso que venho fazendo há algum tempo e funcionou muito bem! Eu também recomendaria esta solução.
David Peterson
Se você chamar outro método auxiliar (não o Get), ainda poderá usar [FromUri]? Parece que não consigo fazer isso funcionar.
jocull
8

O uso de GET ou POST é explicado claramente por @LukLed . Em relação às maneiras pelas quais você pode passar os parâmetros, sugiro seguir a segunda abordagem (também não sei muito sobre o ODATA ).

1.Serializando os parâmetros em uma única string JSON e separando-a na API. http://forums.asp.net/t/1807316.aspx/1

Isso não é fácil de usar e de SEO

2.Passe os parâmetros na string de consulta. Qual é a melhor maneira de passar vários parâmetros de consulta para uma API repousante?

Esta é a abordagem preferível usual.

3.Definição dos parâmetros na rota: api / controller / date1 / date2

Definitivamente, essa não é uma boa abordagem. Isso faz parecer que alguém date2é um sub-recurso date1e esse não é o caso. Os parâmetros de consulta date1e date2são e vem no mesmo nível.

Em um caso simples, eu sugeriria um URI como esse,

api/controller?start=date1&end=date2

Mas eu pessoalmente gosto do padrão URI abaixo, mas, neste caso, precisamos escrever um código personalizado para mapear os parâmetros.

api/controller/date1,date2
VJAI
fonte
Na verdade, essas foram minhas explicações originais. Acho que o LukLed iluminou minhas tags e link de URL.
sig606
Quanto ao SEO, nesse caso, não se aplicaria. Este código será "servidor para servidor", então eu não me importaria se o mundo exterior o descobrisse. Na verdade, tenho que garantir que as medidas de segurança adequadas sejam tomadas para evitar o acesso aleatório. Eu tive que fazer a serialização JSON para outra parte do sistema (parece ser um erro ao tentar postar grandes listas de obj, então eu tive que serializar para string), para que não fosse muito difícil nesse caso .
6060 sigl6
1
Espero que você já tenha respostas, por que está fazendo perguntas?
VJAI
2
Desculpe por esta resposta tardia, Mark. Eu já havia tentado algumas soluções, mas não tinha certeza de qual era a melhor e estava tentando seguir as abordagens padrão do setor, então publiquei aqui na SO.
sig606
1
@ Mark Algo parecido com o próximo: stackoverflow.com/questions/9681658/… ?
RredCat
3
 [Route("api/controller/{one}/{two}")]
    public string Get(int One, int Two)
    {
        return "both params of the root link({one},{two}) and Get function parameters (one, two)  should be same ";
    }

Os parâmetros do link raiz ({one}, {two}) e os parâmetros da função Get (um, dois) devem ser iguais

Ashwath Hegde
fonte
2

Eu sei que isso é muito antigo, mas eu queria a mesma coisa recentemente e aqui está o que eu encontrei ...

    public HttpResponseMessage Get([FromUri] string var, [FromUri] string test) {
        var retStr = new HttpResponseMessage(HttpStatusCode.OK);
        if (var.ToLower() == "getnew" && test.ToLower() == "test") {
            retStr.Content = new StringContent("Found Test", System.Text.Encoding.UTF8, "text/plain");
        } else {
            retStr.Content = new StringContent("Couldn't Find that test", System.Text.Encoding.UTF8, "text/plain");
        }

        return retStr;
    }

Então agora em seu endereço / URI / ...

http (s): // myURL / api / myController /? var = getnew & test = test

Resultado: "Teste Encontrado"


http (s): // myURL / api / myController /? var = getnew & test = qualquer coisa

Resultado: "Não foi possível encontrar esse teste"

Rick Riggs
fonte
Pessoalmente, gosto desse estilo em C #, porque posso alterar a assinatura do método original e sobrecarregar exatamente o que estou tentando realizar, sem alterar as configurações de roteamento. Espero que ajude outras pessoas acostumadas a essa abordagem (talvez antiquada) de fazer uma solicitação GET.
Rick Riggs
1
Eu tive que criar uma API de eventos consumida por um aplicativo de calendário de terceiros, que usa essa abordagem. Estou feliz por ter encontrado esta resposta!
clayRay
0
    public HttpResponseMessage Get(int id,string numb)
    {
        //this will differ according to your entity name
        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");
            }
        }
    }
Jesse Mwangi
fonte