A biblioteca RestClient fornece um conector simples para consumir serviços REST.
Para usá-lo em seu projeto, adicione o pacote Mafe.RestClient NuGet ao seu projeto.
O objetivo do RestClient é permitir que os desenvolvedores se conectem facilmente a qualquer servidor de uma maneira realmente fácil. Basta definir seu Data Transfer Object (Dto) e começar a brincar com o cliente!
Use o método Build() para criar um RestBuilder a partir do Rest:
var rest = Rest . Build ( ) ;
Para criar uma chamada get simples, faça assim:
var rest = Rest . Build ( ) ;
var result = rest . Url ( "[URL]" ) . Get ( ) ;
ou podemos usar o método GetAsync():
var rest = Rest . Build ( ) ;
var result = await rest . Url ( "[URL]" ) . GetAsync ( ) ;
Sempre que você encontrar a palavra "[URL]" neste documento, ela se refere à definição de URL base da webAPI.
Podemos definir um endpoint Root() e usá-lo para construir a solicitação.
public RestBuilder Root ( ) => rest . Url ( "https://mywebapi.mydomain.com" ) ;
e então, podemos usá-lo, assim:
public RestBuilder Root ( ) => Rest . Build ( ) . Url ( "https://mywebapi.mydomain.com" ) ;
var result = Root ( )
. Command ( "/my-action" )
. Payload ( "mystring" )
. Post ( ) ;
Para usar RestProperties para configurar seu ponto raiz restante. Para criar uma configuração simples, assim:
RestProperties properties = new RestProperties
{
EndPoint = new Uri ( "[URL]" ) , //if you use .Url("[URL]") you override it
BufferSize = 4096 ,
CertificateOption = ClientCertificateOption . Manual ,
Timeout = TimeSpan . FromMinutes ( 2 )
} ;
Use o método Build() com propriedades para criar um RestBuilder a partir do Rest:
var rest = Rest . Build ( properties ) ;
A validação de certificados X.509 é essencial para criar sessões SSL/TLS seguras e não vulneráveis a ataques man-in-the-middle.
A validação da cadeia de certificados inclui estas etapas:
Não é recomendado reinventar a roda implementando a validação da cadeia de certificados personalizada.
As bibliotecas TLS fornecem funções integradas de validação de certificado que devem ser usadas.
List < string > validCerts = new List < string > ( ) {
"CERT STRING"
} ;
var result = Rest . Build ( )
. CertificateValidation ( ( sender , certificate , chain , errors ) =>
{
// for development, trust all certificates
if ( development ) return true ;
// Compliant: trust only some certificates
return errors == SslPolicyErrors . None
&& validCerts . Contains ( certificate . GetCertHashString ( ) ) ;
} )
. Url ( "[URL]" )
. Get ( ) ;
Conforme definido pelo HTTP/1.1 [RFC2617], a aplicação deve enviar o access_token diretamente no cabeçalho da solicitação de autorização. Você pode fazer isso incluindo o valor access_token do token de acesso no corpo da solicitação HTTP como 'Autorização: Portador {access_token_value}'.
Se um usuário autenticado tiver um access_token ou refresh_token de token de acesso expirado, um erro '401 - Não autorizado (token de atualização inválido ou expirado)' será retornado.
var result = Rest . Build ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. Url ( "[URL]" )
. Get ( ) ;
Um token de portador válido (com propriedades access_token ou refresh_token ativas) mantém a autenticação do usuário ativa sem exigir que ele insira novamente suas credenciais com frequência. O access_token pode ser usado enquanto estiver ativo, ou seja, até uma hora após o login ou renovação. O refresh_token fica ativo por 336 horas (14 dias). Depois que o access_token expirar, um refresh_token ativo poderá ser usado para obter um novo par access_token/refresh_token, conforme mostrado no exemplo a seguir. Este ciclo pode continuar por até 90 dias, após os quais o usuário deverá fazer login novamente. Se o update_token expirar, os tokens não poderão ser renovados e o usuário deverá efetuar login novamente.
Para atualizar um token, use "RefreshTokenInvoke" automaticamente.
var url = "[URL]" ;
var result = Rest . Build ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. RefreshToken ( true )
. RefreshTokenInvoke ( async ( ) =>
{
var result = await rest
. Url ( url )
. Command ( "/refresh" )
. GetAsync < TokenObjectResponse > ( ) ;
doSomethings ( ) ; //store the token inside your env.
return result ;
} )
. Command ( "/detail" )
. Url ( url )
. Get ( ) ;
Um refresh_token deve ser revogado:
A classe NetworkCredential é uma classe base que fornece credenciais em esquemas de autenticação baseados em senha, como básico, resumo, NTLM e Kerberos. As classes que implementam a interface ICredentials, como a classe CredentialCache, retornam objetos NetworkCredential. Esta classe não oferece suporte a métodos de autenticação baseados em chave pública, como autenticação de cliente Secure Sockets Layer (SSL).
var result = Rest . Build ( )
. NetworkCredential ( "myUsername" , "myPassword" )
. Url ( "[URL]" )
. Get ( ) ;
var result = rest
. NetworkCredential ( ( ) => new System . Net . NetworkCredential ( "myUsername" , "myPassword" ) )
. Url ( "[URL]" )
. Get ( ) ;
A coleção Headers contém os cabeçalhos de protocolo associados à solicitação. O método Header((h)=>{}) permite adicionar a lista de chaves.
var result = Rest . Build ( )
. Header ( ( h ) => {
if ( ! h . Contains ( "auth-custom" ) )
h . Add ( "auth-custom" , "value" ) ;
} )
. Url ( "[URL]" )
. Get ( ) ;
Dois tipos de serialização são suportados pelo RestClient: Xml e Json, mas é possível implementar ISerializerContent para customizar a serialização. RestClient usa .Json() para serializar um objeto em JSON.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( )
. Get < ResponseObject > ( ) ;
É possível passar opções de serializador json para o método .Json(), assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerOptions {
WriteIndented = true
} )
. Get < ResponseObject > ( ) ;
O código do trecho acima considera o uso da biblioteca System.Text.Json. Se usarmos o Netwnsoft assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerSettings {
Formatting = Formatting . Indented
} )
. Get < ResponseObject > ( ) ;
RestClient usa .Xml() para serializar um objeto em xml.
var result = rest
. Url ( "[URL]" )
. Xml ( )
. Get < ResponseObject > ( ) ;
É possível passar as configurações para o método .Xml(), assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Xml ( new XmlReaderSettings { Indent = true } , new XmlWriterSettings { IgnoreWhitespace = true } )
. Get < ResponseObject > ( ) ;
Abaixo um exemplo de como você pode fazer uma serialização customizada implementando a interface ISerializerContent:
public class MyCustomSerializer : ISerializerContent
{
public string MediaTypeAsString => throw new NotImplementedException ( ) ;
public object DeserializeObject ( string value , Type typeOf , object options = null )
{
throw new NotImplementedException ( ) ;
}
public string SerializeObject ( object value , Type typeOf , object options = null )
{
throw new NotImplementedException ( ) ;
}
}
Agora podemos usar MyCustomSerializer assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. CustomSerializer ( new MyCustomSerializer { } )
. Get < ResponseObject > ( ) ;
BufferSize pode ser usado para definir o tamanho do buffer durante o fluxo de upload e download. O valor padrão é 80 KB
var result = Rest . Build ( )
. Url ( "[URL]" )
. BufferSize ( 4096 * 5 * 5 ) //100Kb
. Get ( ) ;
Ativa a compactação gzip durante a comunicação com um recurso especificado:
var result = Rest . Build ( )
. Url ( "[URL]" )
. EnableGZipCompression ( )
. Get ( ) ;
A biblioteca descompacta automaticamente a resposta.
GET é um dos métodos HTTP mais comuns e GET é usado para solicitar dados de um recurso especificado
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Get ( ) ;
var result = await rest
. Url ( "[URL]" )
. GetAsync ( ) ;
Algumas outras notas sobre solicitações GET:
Observe que a string de consulta (pares nome/valor) é enviada na URL de uma solicitação GET.
Em alguns casos, precisamos usar argumentos como string de consulta. Podemos usar o método Parameter(key, value) para resolvê-lo assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/path" )
. Parameter ( "id" , "10" )
. Parameter ( "type" , "myType" )
. Get ( ) ;
A URL gerada é: [URL]/path?id=10&type=myType
POST é usado para enviar dados a um servidor para criar/atualizar um recurso. Os dados enviados ao servidor com POST são armazenados na carga útil da solicitação HTTP:
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Post < ResponseObject > ( ) ;
var result = await rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. PostAsync < ResponseObject > ( ) ;
Post é outro método http comum, usado para:
PUT é usado para enviar dados a um servidor para criar/atualizar um recurso.
A diferença entre POST e PUT é que as solicitações PUT são idempotentes. Ou seja, chamar a mesma solicitação PUT várias vezes sempre produzirá o mesmo resultado. Por outro lado, chamar uma solicitação POST repetidamente tem efeitos colaterais de criar o mesmo recurso várias vezes.
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Put < ResponseObject > ( ) ;
var result = await rest
. Url ( "[URL]" )
. Payload ( new Object { } )
. PutAsync < ResponseObject > ( ) ;
O método DELETE exclui o recurso especificado.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Delete < ResponseObject > ( ) ;
var result = await Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. DeleteAsync < ResponseObject > ( ) ;
O método DOWNLOAD baixa o recurso especificado.
var result = Rest . Build ( )
. Download ( "[URL]" ) ;
var result = await rest
. DownloadAsync ( "[URL]" ) ;
É possível exibir o status do download com OnDownloadProgress.
var rest = Rest . Build ( ) ;
var result = await rest
. OnDownloadProgress ( ( d ) => Console . WriteLine ( $ " { d . CurrentBytes } / { d . TotalBytes } " ) )
. DownloadAsync ( "[URL]" ) ;
Propaga notificação de que as operações devem ser canceladas.
Um CancellationToken permite o cancelamento cooperativo entre threads, itens de trabalho do pool de threads ou objetos Task. Você cria um token de cancelamento instanciando um objeto CancellationTokenSource, que gerencia tokens de cancelamento recuperados de seu CancellationTokenSource.
O exemplo a seguir usa token de cancelamento para interromper a execução:
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource ( ) ;
CancellationToken token = source . Token ;
// Schedules a cancel operation on this System.Threading.CancellationTokenSource
// after the specified number of milliseconds
token . CancelAfter ( 3000 ) ;
var file1 = Rest . Build ( ) . DownloadAsync ( "[URL FILE1]" , token . Token ) ;
var file2 = Rest . Build ( ) . DownloadAsync ( "[URL FILE2]" , token . Token ) ;
var get1 = Rest . Build ( ) . Url ( "[URL GET1]" ) . GetAsync < MyObject > ( token . Token ) ;
Task . WaitAll ( file1 , file2 , get1 ) ;
Após o cancelamento solicitado, ele lança uma TaskCancellatedException. A exceção será encapsulada no objeto RestResult.
O método CUSTOM que personaliza o recurso especificado.
HttpMethod PATCH = new HttpMethod ( "PATCH" ) ;
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. CustomCall < ResponseObject > ( PATCH ) ;
var result = await rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. CustomCallAsync < ResponseObject > ( PATCH ) ;
RestClient usa o método Playload(obj) para definir um objeto mediante solicitação:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new RequestObject { } )
. Post < ResponseObject > ( ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new RequestObject { } )
. Put < ResponseObject > ( ) ;
Quando necessário, podemos usar a solicitação como um formulário codificado em URL. Para usá-lo precisamos habilitá-lo, assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. EnableFormUrlEncoded ( true )
e então podemos passar os parâmetros como um dicionário:
var params = new Dictionary < string , string > ( ) ;
params . Add ( "key1" , "value1" ) ;
params . Add ( "key2" , "value2" ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. EnableFormUrlEncoded ( true )
. FormUrlEncoded ( params )
. Post ( ) ;
É possível passar os parâmetros dentro do handler e habilitar o form-url-encoded:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. FormUrlEncoded ( true , ( p ) =>
{
p . Add ( "key1" , "value1" ) ;
p . Add ( "key2" , "value2" ) ;
} )
. Post ( ) ;
OnUploadProgress ocorre quando a solicitação está em execução e os dados estão sendo enviados. Podemos obter uma porcentagem dos dados que estão sendo carregados assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnUploadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during request
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnDownloadProgress ocorre quando a resposta está em execução e os dados estão chegando. Podemos obter uma porcentagem dos dados sendo baixados assim:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnDownloadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during response
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
O valor padrão é 100.000 milissegundos (100 segundos). Para definir um tempo limite infinito, defina o valor da propriedade como InfiniteTimeSpan. Uma consulta do Sistema de Nomes de Domínio (DNS) pode levar até 15 segundos para retornar ou atingir o tempo limite. Se a sua solicitação contiver um nome de host que requer resolução e você definir o Tempo Limite com um valor inferior a 15 segundos, poderá levar 15 segundos ou mais antes que uma Exceção seja lançada para indicar um tempo limite na sua solicitação.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( 3200 ) //milliseconds
. Get < ResponseObject > ( ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( TimeSpan . FromMinutes ( 10 ) )
. Get < ResponseObject > ( ) ;
OnStart é um evento que é acionado quando a solicitação é iniciada.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnStart ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentRequestAsString é um evento que é acionado quando a solicitação está pronta e ainda não foi enviada.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentRequestAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentResponseAsString é um evento que é acionado quando a resposta é recebida e ainda não foi desserializada.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentResponseAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreResult ocorre quando a solicitação está sendo concluída, mas ainda não foi concluída. Quando OnPreResult é gerado, podemos fazer algumas coisas, por exemplo, obter e usar o resultado da solicitação.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreCompleted ( ( r ) => {
DoSomethings ( r . Result ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnException ocorre quando a solicitação gera uma exceção.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnException ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnCompleted ocorre quando a solicitação é concluída.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnCompleted ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
RestClient permite criar uma camada de rede flexível e robusta e é muito fácil de usar. Abaixo você encontra uma demonstração de código completa e um exemplo de código completo.
public class NetworkService {
//building context
public RestBuilder Root ( )
=> Rest . Build ( )
. CertificateValidation ( ( sender , certificate , chain , errors ) =>
{
if ( development ) return true ;
return errors == SslPolicyErrors . None
&& validCerts . Contains ( certificate . GetCertHashString ( ) ) ;
} )
. Url ( "[URL]" ) ;
public RestBuilder RootAuthentication ( )
=> Root ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. RefreshToken ( )
. RefreshTokenInvoke ( async ( ) => await PostRefreshAsync ( new RefreshRequest { } ) ) ;
public RestBuilder UsersRoot ( )
=> Root ( ) . Command ( "/Users" ) ;
public RestBuilder DimensionsRoot ( )
=> Root ( ) . Command ( "/Dimensions" ) ;
public RestBuilder EventsRoot ( )
=> RootAuthentication ( ) . Command ( "/Events" ) ;
//requests
public async Task < RestResult < LoginResponse > > PostLoginAsync ( LoginRequest request )
=> await UsersRoot ( )
. Command ( "/Login" ) //[URL]/Users/Login
. Payload ( request )
. PostAsync < LoginResponse > ( ) ;
public async Task < RestResult < RuleResponse > > GetRulesAsync ( )
=> await UsersRoot ( )
. Command ( "/Rules" )
. GetAsync < RuleResponse > ( ) ;
public async Task < RestResult < RefreshResponse > > PostRefreshAsync ( RefreshRequest request )
=> await UsersRoot ( )
. Command ( "/Refresh" )
. Payload ( request )
. PostAsync < RefreshResponse > ( ) ;
public async Task < RestResult < CountryResponse > > PostCountriesAsync ( CountryRequest request )
=> await DimensionsRoot ( )
. Command ( "/Countries" )
. Payload ( request )
. PostAsync < CountryResponse > ( ) ;
public async Task < RestResult < EventDetailResponse > > PostEventDetailAsync ( EventDetailRequest request )
=> await EventsRoot ( )
. Command ( "/EventDetail" )
. Payload ( request )
. PostAsync < EventDetailResponse > ( ) ;
}