La biblioteca RestClient proporciona un conector simple para consumir servicios REST.
Para usarlo en su proyecto, agregue el paquete Mafe.RestClient NuGet a su proyecto.
El objetivo de RestClient es permitir a los desarrolladores conectarse fácilmente a cualquier servidor de una manera realmente sencilla. ¡Simplemente define tu Objeto de Transferencia de Datos (Dto) y comienza a jugar con el cliente!
Utilice el método Build() para crear un RestBuilder a partir de Rest:
var rest = Rest . Build ( ) ;
Para crear una llamada de obtención simple, simplemente haga lo siguiente:
var rest = Rest . Build ( ) ;
var result = rest . Url ( "[URL]" ) . Get ( ) ;
o podemos usar el método GetAsync():
var rest = Rest . Build ( ) ;
var result = await rest . Url ( "[URL]" ) . GetAsync ( ) ;
Siempre que encuentre la palabra "[URL]" en este documento, se refiere a la definición de URL base de webAPI.
Podemos definir un punto final Root() y usarlo para generar la solicitud.
public RestBuilder Root ( ) => rest . Url ( "https://mywebapi.mydomain.com" ) ;
y luego, podemos usarlo, así:
public RestBuilder Root ( ) => Rest . Build ( ) . Url ( "https://mywebapi.mydomain.com" ) ;
var result = Root ( )
. Command ( "/my-action" )
. Payload ( "mystring" )
. Post ( ) ;
Usar RestProperties para configurar su punto raíz de descanso. Para crear una configuración simple, así:
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 )
} ;
Utilice el método Build() con propiedades para crear un RestBuilder a partir de Rest:
var rest = Rest . Build ( properties ) ;
La validación de los certificados X.509 es esencial para crear sesiones SSL/TLS seguras que no sean vulnerables a ataques de intermediario.
La validación de la cadena de certificados incluye estos pasos:
No se recomienda reinventar la rueda implementando una validación de cadena de certificados personalizada.
Las bibliotecas TLS proporcionan funciones de validación de certificados integradas que deben utilizarse.
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 ( ) ;
Según lo definido por HTTP/1.1 [RFC2617], la aplicación debe enviar el token de acceso directamente en el encabezado de solicitud de autorización. Puede hacerlo incluyendo el valor access_token del token de portador en el cuerpo de la solicitud HTTP como 'Autorización: Portador {access_token_value}'.
Si un usuario autenticado tiene un token de acceso o un token de actualización caducado, se devuelve el error '401 - No autorizado (token de actualización no válido o caducado)'.
var result = Rest . Build ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. Url ( "[URL]" )
. Get ( ) ;
Un token de portador válido (con propiedades access_token o refresco_token activas) mantiene viva la autenticación del usuario sin necesidad de volver a ingresar sus credenciales con frecuencia. El access_token se puede utilizar mientras esté activo, es decir, hasta una hora después del inicio de sesión o la renovación. El refresco_token está activo durante 336 horas (14 días). Una vez que expire el token de acceso, se puede usar un token de actualización activo para obtener un nuevo par token de acceso/token de actualización, como se muestra en el siguiente ejemplo. Este ciclo puede continuar por hasta 90 días, después de los cuales el usuario debe iniciar sesión nuevamente. Si el refresco_token caduca, los tokens no se pueden renovar y el usuario debe iniciar sesión nuevamente.
Para actualizar un token, use "RefreshTokenInvoke" automáticamente.
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 ( ) ;
Se debe revocar un refresco_token:
La clase NetworkCredential es una clase base que proporciona credenciales en esquemas de autenticación basados en contraseñas, como básico, resumen, NTLM y Kerberos. Las clases que implementan la interfaz ICredentials, como la clase CredentialCache, devuelven objetos NetworkCredential. Esta clase no admite métodos de autenticación basados en claves públicas, como la autenticación 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 ( ) ;
La colección Headers contiene los encabezados de protocolo asociados con la solicitud. El método Header((h)=>{}) le permite agregar la lista de claves.
var result = Rest . Build ( )
. Header ( ( h ) => {
if ( ! h . Contains ( "auth-custom" ) )
h . Add ( "auth-custom" , "value" ) ;
} )
. Url ( "[URL]" )
. Get ( ) ;
RestClient admite dos tipos de serialización: Xml y Json, pero es posible implementar ISerializerContent para personalizar la serialización. RestClient usa .Json() para serializar un objeto en json.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( )
. Get < ResponseObject > ( ) ;
Es posible pasar opciones de serializador json al método .Json(), así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerOptions {
WriteIndented = true
} )
. Get < ResponseObject > ( ) ;
El fragmento de código anterior considera el uso de la biblioteca System.Text.Json. Si usamos Netwnsoft así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerSettings {
Formatting = Formatting . Indented
} )
. Get < ResponseObject > ( ) ;
RestClient usa .Xml() para serializar un objeto en xml.
var result = rest
. Url ( "[URL]" )
. Xml ( )
. Get < ResponseObject > ( ) ;
Es posible pasar la configuración al método .Xml(), así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Xml ( new XmlReaderSettings { Indent = true } , new XmlWriterSettings { IgnoreWhitespace = true } )
. Get < ResponseObject > ( ) ;
A continuación se muestra un ejemplo de cómo puede realizar una serialización personalizada implementando la interfaz 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 ( ) ;
}
}
Ahora podemos usar MyCustomSerializer así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. CustomSerializer ( new MyCustomSerializer { } )
. Get < ResponseObject > ( ) ;
BufferSize se puede utilizar para establecer el tamaño del búfer durante la transmisión de carga y descarga. El valor predeterminado es 80Kb
var result = Rest . Build ( )
. Url ( "[URL]" )
. BufferSize ( 4096 * 5 * 5 ) //100Kb
. Get ( ) ;
Habilita la compresión gzip durante la comunicación con un recurso específico:
var result = Rest . Build ( )
. Url ( "[URL]" )
. EnableGZipCompression ( )
. Get ( ) ;
La biblioteca descomprime automáticamente la respuesta.
GET es uno de los métodos HTTP más comunes y GET se utiliza para solicitar datos de un recurso específico.
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Get ( ) ;
var result = await rest
. Url ( "[URL]" )
. GetAsync ( ) ;
Algunas otras notas sobre solicitudes GET:
Tenga en cuenta que la cadena de consulta (pares nombre/valor) se envía en la URL de una solicitud GET.
En algunos casos necesitamos utilizar argumentos como cadena de consulta. Podemos usar el método Parámetro(clave, valor) para resolverlo así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/path" )
. Parameter ( "id" , "10" )
. Parameter ( "type" , "myType" )
. Get ( ) ;
La URL generada es: [URL]/path?id=10&type=myType
POST se utiliza para enviar datos a un servidor para crear/actualizar un recurso. Los datos enviados al servidor con POST se almacenan en la carga útil de la solicitud 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 > ( ) ;
La publicación es otro método http común, que se utiliza para:
PUT se utiliza para enviar datos a un servidor para crear/actualizar un recurso.
La diferencia entre POST y PUT es que las solicitudes PUT son idempotentes. Es decir, llamar a la misma solicitud PUT varias veces siempre producirá el mismo resultado. Por el contrario, llamar repetidamente a una solicitud POST tiene efectos secundarios al crear el mismo recurso varias veces.
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 > ( ) ;
El método DELETE elimina el 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 > ( ) ;
El método DOWNLOAD descarga el recurso especificado.
var result = Rest . Build ( )
. Download ( "[URL]" ) ;
var result = await rest
. DownloadAsync ( "[URL]" ) ;
Es posible mostrar el estado de la descarga con OnDownloadProgress.
var rest = Rest . Build ( ) ;
var result = await rest
. OnDownloadProgress ( ( d ) => Console . WriteLine ( $ " { d . CurrentBytes } / { d . TotalBytes } " ) )
. DownloadAsync ( "[URL]" ) ;
Propaga notificación de que las operaciones deben cancelarse.
Un CancellationToken permite la cancelación cooperativa entre subprocesos, elementos de trabajo del grupo de subprocesos u objetos de tarea. Para crear un token de cancelación, crea una instancia de un objeto CancellationTokenSource, que administra los tokens de cancelación recuperados de su CancellationTokenSource.
El siguiente ejemplo utiliza un token de cancelación para detener la ejecución:
// 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 ) ;
Después de la cancelación solicitada, arroja una TaskCancellatedException. La excepción se encapsulará en el objeto RestResult.
El método CUSTOM que personaliza el 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 el método Playload(obj) para configurar un objeto a pedido:
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 > ( ) ;
Cuando sea necesario, podemos utilizar la solicitud como un formulario codificado en URL. Para usarlo necesitamos habilitarlo, así:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. EnableFormUrlEncoded ( true )
y luego podemos pasar los parámetros como un diccionario:
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 ( ) ;
Es posible pasar los parámetros dentro del controlador y habilitar el formulario codificado en URL:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. FormUrlEncoded ( true , ( p ) =>
{
p . Add ( "key1" , "value1" ) ;
p . Add ( "key2" , "value2" ) ;
} )
. Post ( ) ;
OnUploadProgress ocurre cuando la solicitud se está ejecutando y los datos se envían. Podemos obtener un porcentaje de los datos que se cargan de esta manera:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnUploadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during request
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnDownloadProgress ocurre cuando la respuesta se está ejecutando y los datos ingresan. Podemos obtener un porcentaje de los datos que se descargan de esta manera:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnDownloadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during response
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
El valor predeterminado es 100.000 milisegundos (100 segundos). Para establecer un tiempo de espera infinito, establezca el valor de la propiedad en InfiniteTimeSpan. Una consulta del Sistema de nombres de dominio (DNS) puede tardar hasta 15 segundos en regresar o expirar. Si su solicitud contiene un nombre de host que requiere resolución y establece el Tiempo de espera en un valor inferior a 15 segundos, pueden pasar 15 segundos o más antes de que se genere una excepción para indicar un tiempo de espera en su solicitud.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( 3200 ) //milliseconds
. Get < ResponseObject > ( ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( TimeSpan . FromMinutes ( 10 ) )
. Get < ResponseObject > ( ) ;
OnStart es un evento que se activa cuando comienza la solicitud.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnStart ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentRequestAsString es un evento que se activa cuando la solicitud está lista y aún no se ha enviado.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentRequestAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentResponseAsString es un evento que se activa cuando se recibe la respuesta y aún no está deserializada.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentResponseAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreResult ocurre cuando la solicitud se está completando pero aún no se ha completado. Cuando se genera OnPreResult, podemos hacer algunas cosas, por ejemplo, obtener y usar el resultado de la solicitud.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreCompleted ( ( r ) => {
DoSomethings ( r . Result ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnException ocurre cuando la solicitud genera una excepción.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnException ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnCompleted ocurre cuando se completa la solicitud.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnCompleted ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
RestClient permite crear una capa de red flexible y robusta y es muy fácil de usar. A continuación encontrará una demostración de código completa y un ejemplo 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 > ( ) ;
}