I'm new to this forum and to have a question about await/async use in Xamarin (Also the first time I work with).
I am working for my internship on a project using Xamarin, PCL, MvvmCross.
In my PCL im do a postrequest to a WCF service to login in my application. In WP8 everything just works fine, but when I am running my application on Android the response is always null.
Below you can find my httpclient class. The method with the post is InternalPostAsync
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using Anton.Mobile.Shared.Infrastructure;
using System.Net;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
namespace Anton.Mobile.Shared.Data
{
public class AntonClient
{
#region static
/// <summary>
/// Base Uri of the Ria service (e.g. http://example.com/)
/// </summary>
private static readonly Uri _baseUri = new Uri(Config.BaseUri);
/// <summary>
/// Last cookie response header (Session, authentication, ...)
/// </summary>
private static string _cookieHeader = null;
/// <summary>
/// Lock object for read/write <para>_cookieHeader</para>
/// </summary>
private static object _lockObj = new object();
#endregion
#region protected
/// <summary>
/// Creates a client
/// </summary>
/// <param name="container">Cookie container to use</param>
/// <returns>HttpClient</returns>
protected virtual HttpClient CreateClient(CookieContainer container)
{
//set container on handler for tracking cookies between request-response
var handler = new HttpClientHandler()
{
CookieContainer = container,
UseCookies = true,
UseDefaultCredentials = false,
};
//Create client and set the base address
var cl = new HttpClient(handler)
{
BaseAddress = _baseUri
};
if (!string.IsNullOrEmpty(_cookieHeader))
{
cl.DefaultRequestHeaders.Add("Cookies", _cookieHeader);
}
return cl;
}
/// <summary>
/// Creates a JSON content request
/// </summary>
/// <param name="jsonContent">JSON value</param>
/// <returns>JSON content</returns>
protected virtual HttpContent CreateRequestContent(string jsonContent)
{
var content = new StringContent(jsonContent,Encoding.UTF8,"application/json");
//content.Headers.Add("ContentType", "application/json");
return content;
}
/// <summary>
/// Save cookies <para>_cookieHeader</para>
/// </summary>
/// <param name="container">cookie container</param>
protected void ParseCookies(HttpResponseMessage msg)
{
IEnumerable<string> values;
if (!msg.Headers.TryGetValues("Set-Cookie", out values) || !values.Any())
return;
//var cookies = container.GetCookieHeader(_baseUri);
var cs = new List<string>();
foreach (var v in values)
{
string[] vs = v.Split(new char[] { ';' });
string[] value = vs[0].Split(new char[] { '=' });
container.Add(new Uri("Http://initesting"), new Cookie(value[0], value[1]));
cs.Add(string.Format("{0}={1}", value[0], value[1]));
}
lock (_lockObj)
{
_cookieHeader = string.Join(";", cs.ToArray());
}
}
private static CookieContainer container = new CookieContainer();
/// <summary>
/// Create a new cookie container from <para>_cookieHeaders</para>
/// </summary>
/// <returns>Cookie container</returns>
protected CookieContainer CreateCookieContainer()
{
//lock (_lockObj)
//{
// if (!string.IsNullOrEmpty(_cookieHeader))
// {
// foreach (var header in _cookieHeader.Split(new char[] { ';' }))
// {
// container.SetCookies(_baseUri, header);
// }
// }
//}
return container;
}
/// <summary>
/// Executes a POST HTTP Request
/// </summary>
/// <param name="jsonContent">POST JSON content</param>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalPostAsync(string jsonContent, Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
var content = CreateRequestContent(jsonContent);
var response = await client.PostAsync(uri, content);
if (response.StatusCode != HttpStatusCode.OK)
{
return null; //todo
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
/// <summary>
/// Executes a GET HTTP Request
/// </summary>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalRequestAsync(Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
HttpResponseMessage response = await client.GetAsync(uri);
if (response.StatusCode != HttpStatusCode.OK)
{
return null;
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
#endregion protected
#region public
/// <summary>
/// Executes a POST HTTP Request for a given Request key
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <typeparam name="TResult">Result Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="key">Unique Request Key</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResult> PostAsync<TRequest, TResult>(TRequest request, RequestKey key)
where TRequest : class
where TResult : class
{
try
{
var uri = RequestMap.GetUri(key);
string jsonResult = await InternalPostAsync(request.SerializeJson(), uri);
return jsonResult.DeserializeJson<TResult>();
}
catch (Exception)
{
//todo
}
return default(TResult);
}
/// <summary>
/// Executes a POST HTTP Request for a given service uri
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="uri">Service URI</param>
/// <returns>Deserialized POST response content of type dynamic</returns>
public async Task<dynamic> PostAsync<TRequest>(TRequest request, string uri)
{
try
{
string jsonResult = await InternalPostAsync(request.SerializeJson(), new Uri(uri, UriKind.Absolute));
return jsonResult.DynamicJson();
}
catch (Exception)
{
//todo
}
return null;
}
/// <summary>
/// Executes a GET HTTP Request for a givin key and query string parameter info
/// </summary>
/// <typeparam name="TResponse">Response Type</typeparam>
/// <param name="key">Unique request key</param>
/// <param name="queryString">Querystring info</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResponse> RequestAsync<TResponse>(RequestKey key, IDictionary<string, string> queryString = null)
{
try
{
string jsonResult = await InternalRequestAsync(RequestMap.GetUri(key, queryString));
var dynamicResult = jsonResult.DynamicJson();
var item = (dynamicResult as JObject)[RequestMap.GetValue(key) + "Result"]["RootResults"].First; //todo: better solution for this
return item.ToObject<TResponse>();
}
catch (Exception)
{
//todo
}
return default(TResponse);
}
#endregion public
}
}
Regarding the website of Xamarin async/await is supported, but I did not find any similar problems. I hope you can help me.
I have solved my problem.
In Android, when you add the NuGet package with the HTTP Libraries, something went wrong when adding the references. You have to add the references mannualy to solve it. I also editted my Uri, now it works with the IP-address in the Uri.
Related
I have an odata/webapi app that's been updated to use OWIN auth. The code to issue tokens (/token url) works fine and returns a token, but any "normal" call to any controllers returns HTTP 400
{"error": "unsupported_grant_type"}
with no additional information.
Startup.cs:
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Web.Http;
[assembly: OwinStartup(typeof(VP4C_API.Startup))]
namespace VP4C_API
{
public class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthBearerAuthentication(oAuthBearerAuthenticationOptions);
Statics.Config = new HttpConfiguration();
WebApiConfig.Register(Statics.Config);
app.UseWebApi(Statics.Config);
}
/// <summary>
/// Called at when the AppPool starts the website
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
AuthorizationProvider which returns the token:
using System;
using System.Threading.Tasks;
using Microsoft.Owin.Security.OAuth;
using Microsoft.Owin.Security;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
namespace VP4C_API
{
internal class SimpleAuthorizationServerProvider : IOAuthAuthorizationServerProvider
{
Task IOAuthAuthorizationServerProvider.AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Take the ClientID and validate its one we want to talk to
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateTokenRequest"/> />
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
if (context.ClientId == "App1Test")
{
var identity = new ClaimsIdentity(new GenericIdentity(
context.ClientId, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("urn:oauth:scope", x))
);
context.Validated(identity);
}
else
{
context.Rejected();
context.SetError("Unknown client id", "You must list the client id in the known clients table");
}
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.GrantCustomExtension(OAuthGrantCustomExtensionContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateTokenRequest"/>
/// Check the username/password here
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName == "test" && context.Password == "test")
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
System.Security.Principal.GenericIdentity gi = new System.Security.Principal.GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType);
System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity(gi, context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
var ticket = new AuthenticationTicket(ci, null);
context.Validated(ticket);
}
else
{
context.SetError("GrantResourceOwner Error", "Username or Password is incorrect.");
context.Rejected();
}
return Task.FromResult(0);
}
/// <summary>
/// Called first to filter on the endpoint attributes (e.g. client application IP address)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.MatchEndpoint(OAuthMatchEndpointContext context)
{
context.MatchesTokenEndpoint();
return Task.FromResult(0);
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.GrantResourceOwnerCredentials"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.TokenEndpoint(OAuthTokenEndpointContext context)
{
AuthenticationProperties ap = new AuthenticationProperties();
ap.AllowRefresh = true;
ap.IssuedUtc = DateTime.UtcNow;
ap.IsPersistent = true;
context.Issue(context.Identity, ap);
return Task.FromResult(0);
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.TokenEndpoint"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.MatchEndpoint"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientID;
string clientPassword;
context.TryGetBasicCredentials(out clientID, out clientPassword);
context.Validated(clientID);
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateClientAuthentication"/>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.ValidateTokenRequest(OAuthValidateTokenRequestContext context)
{
context.Validated();
return Task.FromResult(0);
}
}
}
And a sample controller snippet for http://host/api/Verify/1
[HttpGet, AllowAnonymous, Route("{companyID}")]
public IHttpActionResult Verify(int companyID)
{
string cs = "not set";
int check = -1;
try
{
ErrorLogging.Logging.Information(null, null, Request.RequestUri, $"Verify Start company {companyID}");
Any suggestions on where to look?
Having the below class
/// <summary>
/// An unsuccessful HTTP result.
/// </summary>
public class UnsuccessfulActionResult : IActionResult, IHttpStatusCodeResult, IErrors
{
/// <inheritdoc />
/// <summary>
/// The HTTP status code.
/// </summary>
public HttpStatusCode StatusCode { get; }
/// <summary>
/// The corresponding error messages, if any.
/// </summary>
public IReadOnlyCollection<string> Errors { get; }
/// <summary>
/// The request Transaction ID.
/// </summary>
public string TransactionId { get; }
/// <summary>
/// Default ctor.
/// </summary>
/// <param name="transactionId">The request Transaction ID.</param>
/// <param name="errors">The corresponding error messages, if any.</param>
/// <param name="httpStatusCode"></param>
public UnsuccessfulActionResult(
string transactionId,
HttpStatusCode httpStatusCode,
IReadOnlyCollection<string> errors = null)
{
TransactionId = transactionId;
StatusCode = httpStatusCode;
Errors = errors ?? new List<string>();
}
/// <summary>
/// Convenient method to create an unsuccessful response model.
/// </summary>
/// <param name="statusCode"></param>
/// <returns></returns>
protected UnsuccessfulResponseModel CreateResponseModel(int statusCode)
{
return new UnsuccessfulResponseModel(TransactionId, statusCode, Errors);
}
/// <inheritdoc />
public virtual Task ExecuteResultAsync(ActionContext context)
{
var objectResult = new ObjectResult(CreateResponseModel((int)StatusCode))
{
StatusCode = (int)StatusCode
};
return objectResult.ExecuteResultAsync(context);
}
}
I need to test this class to make sure that ActionContext has the response provided in overridden ExecuteResultAsync.
Just started going deep into ASP.NET Core 2.0 and missing some parts, in Web API 2 I would just check the HttpResponseMessage returned in the equivalent method of IHttpActionResult.
Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken
)
So I have recently built at ExceptionFilter which handles all errors except Api Errors.
The ExceptionFilter looks like this:
public class ExceptionAttribute : IExceptionFilter
{
/// <summary>
/// Handles any exception
/// </summary>
/// <param name="filterContext">The current context</param>
public void OnException(ExceptionContext filterContext)
{
// If our exception has been handled, exit the function
if (filterContext.ExceptionHandled)
return;
// If our exception is not an ApiException
if (!(filterContext.Exception is ApiException))
{
// Set our base status code
var statusCode = (int)HttpStatusCode.InternalServerError;
// If our exception is an http exception
if (filterContext.Exception is HttpException)
{
// Cast our exception as an HttpException
var exception = (HttpException)filterContext.Exception;
// Get our real status code
statusCode = exception.GetHttpCode();
}
// Set our context result
var result = CreateActionResult(filterContext, statusCode);
// Set our handled property to true
filterContext.ExceptionHandled = true;
}
}
/// <summary>
/// Creats an action result from the status code
/// </summary>
/// <param name="filterContext">The current context</param>
/// <param name="statusCode">The status code of the error</param>
/// <returns></returns>
protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
{
// Create our context
var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
var statusCodeName = ((HttpStatusCode)statusCode).ToString();
// Create our route
var controller = (string)filterContext.RouteData.Values["controller"];
var action = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controller, action);
// Create our result
var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };
// Return our result
return result;
}
/// <summary>
/// Gets the first view name that matches the supplied names
/// </summary>
/// <param name="context">The current context</param>
/// <param name="viewNames">A list of view names</param>
/// <returns></returns>
protected string SelectFirstView(ControllerContext context, params string[] viewNames)
{
return viewNames.First(view => ViewExists(context, view));
}
/// <summary>
/// Checks to see if a view exists
/// </summary>
/// <param name="context">The current context</param>
/// <param name="name">The name of the view to check</param>
/// <returns></returns>
protected bool ViewExists(ControllerContext context, string name)
{
var result = ViewEngines.Engines.FindView(context, name, null);
return result.View != null;
}
}
As you can see, if the error is not an ApiException then I route the user to the error controller.
The ApiException is just an error that happens when I make an API call from within MVC.
When these errors happen I would like to return the error as JSON back to the client so that the JavaScript can handle it.
I thought not handling the error would do this, but instead it generates a server error (albeit with the JSON error in it) like so:
Server Error in '/' Application.
{"message":"validateMove validation failure:\r\nThe item is despatched and cannot be moved"}
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: SapphirePlus.Web.ApiException: {"message":"validateMove validation failure:\r\nThe item is despatched and cannot be moved"}
Source Error:
Line 181: if (response.StatusCode != HttpStatusCode.OK)
Line 182: throw new ApiException(result);
So my question is, can I get the Application_Error method to get errors that ARE ApiExceptions and return the error as JSON?
So my question is, can I get the Application_Error method to get
errors that ARE ApiExceptions and return the error as JSON?
Of course:
protected void Application_Error()
{
var apiException = Server.GetLastError() as ApiException;
if (apiException != null)
{
Response.Clear();
Server.ClearError();
Response.StatusCode = 400;
Context.Response.ContentType = "application/json";
Context.Response.Write("YOUR JSON HERE");
}
}
In the end I didn't need to use Global.asax at all, I was able to handle it all inside my ExceptionAttribute class like this:
public class ExceptionAttribute : IExceptionFilter
{
/// <summary>
/// Handles any exception
/// </summary>
/// <param name="filterContext">The current context</param>
public void OnException(ExceptionContext filterContext)
{
// If our exception has been handled, exit the function
if (filterContext.ExceptionHandled)
return;
// Set our base status code
var statusCode = (int)HttpStatusCode.BadRequest;
// If our exception is an http exception
if (filterContext.Exception is HttpException)
{
// Cast our exception as an HttpException
var exception = (HttpException)filterContext.Exception;
// Get our real status code
statusCode = exception.GetHttpCode();
}
// Set our context result
var result = CreateActionResult(filterContext, statusCode);
// Set our handled property to true
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
/// <summary>
/// Creats an action result from the status code
/// </summary>
/// <param name="filterContext">The current context</param>
/// <param name="statusCode">The status code of the error</param>
/// <returns></returns>
protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
{
// Create our context
var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
var statusCodeName = ((HttpStatusCode)statusCode).ToString();
// Create our route
var controller = (string)filterContext.RouteData.Values["controller"];
var action = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controller, action);
// Create our result
var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<IError>(this.Factorize(model)) };
// Return our result
return result;
}
/// <summary>
/// Factorizes the HandleErrorInfo
/// </summary>
/// <param name="error"></param>
/// <returns></returns>
protected virtual IError Factorize(HandleErrorInfo error)
{
// Get the error
var model = new Error
{
Message = "There was an unhandled exception."
};
// If we have an error
if (error != null)
{
// Get our exception
var exception = error.Exception;
// If we are dealing with an ApiException
if (exception is ApiException || exception is HttpException)
{
// Get our JSON
var json = JObject.Parse(exception.Message);
var message = json["exceptionMessage"] != null ? json["exceptionMessage"] : json["message"];
// If we have a message
if (message != null)
{
// Update our model message
model.Message = message.ToString();
}
}
else
{
// Update our message
model.Message = exception.Message;
}
}
// Return our response
return model;
}
/// <summary>
/// Gets the first view name that matches the supplied names
/// </summary>
/// <param name="context">The current context</param>
/// <param name="viewNames">A list of view names</param>
/// <returns></returns>
protected string SelectFirstView(ControllerContext context, params string[] viewNames)
{
return viewNames.First(view => ViewExists(context, view));
}
/// <summary>
/// Checks to see if a view exists
/// </summary>
/// <param name="context">The current context</param>
/// <param name="name">The name of the view to check</param>
/// <returns></returns>
protected bool ViewExists(ControllerContext context, string name)
{
var result = ViewEngines.Engines.FindView(context, name, null);
return result.View != null;
}
}
This handled any Mvc error and for my Api calls, I did this:
/// <summary>
/// Used to handle the api response
/// </summary>
/// <param name="response">The HttpResponseMessage</param>
/// <returns>Returns a string</returns>
private async Task<string> HandleResponse(HttpResponseMessage response)
{
// Read our response content
var result = await response.Content.ReadAsStringAsync();
// If there was an error, throw an HttpException
if (response.StatusCode != HttpStatusCode.OK)
throw new ApiException(result);
// Return our result if there are no errors
return result;
}
This allowed me to capture the ApiError and handle the response differently than with any other exception.
I need to do a PATCH request with the Windows.Web.Http.HttpClient class and there is no official documentation on how to do it. How can I do this?
I found how to do a "custom" PATCH request with the previous System.Net.Http.HttpClient class here, and then fiddled with until I made it work in the Windows.Web.Http.HttpClient class, like so:
public async Task<HttpResponseMessage> PatchAsync(HttpClient client, Uri requestUri, IHttpContent iContent) {
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri) {
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
// In case you want to set a timeout
//CancellationToken cancellationToken = new CancellationTokenSource(60).Token;
try {
response = await client.SendRequestAsync(request);
// If you want to use the timeout you set
//response = await client.SendRequestAsync(request).AsTask(cancellationToken);
} catch(TaskCanceledException e) {
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
Update: See SSX-SL33PY's answer below for an even better solution, that does the same thing.
You can write the very same method as extension method, so you can invoke it directly on the HttpClient object:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent iContent)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
Usage:
var responseMessage = await httpClient.PatchAsync(new Uri("testUri"), httpContent);
I'd like to extend on #alexander-pacha's answer and suggest adding following extension class somewhere in a common library. Wether this be a common library for a project / client / framework/... is something you'll have to make out on your own.
public static class HttpClientExtensions
{
/// <summary>
/// Send a PATCH request to the specified Uri as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, string requestUri, HttpContent content)
{
return client.PatchAsync(CreateUri(requestUri), content);
}
/// <summary>
/// Send a PATCH request to the specified Uri as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent content)
{
return client.PatchAsync(requestUri, content, CancellationToken.None);
}
/// <summary>
/// Send a PATCH request with a cancellation token as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, string requestUri, HttpContent content, CancellationToken cancellationToken)
{
return client.PatchAsync(CreateUri(requestUri), content, cancellationToken);
}
/// <summary>
/// Send a PATCH request with a cancellation token as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
return client.SendAsync(new HttpRequestMessage(new HttpMethod("PATCH"), requestUri)
{
Content = content
}, cancellationToken);
}
private static Uri CreateUri(string uri)
{
return string.IsNullOrEmpty(uri) ? null : new Uri(uri, UriKind.RelativeOrAbsolute);
}
}
This way you're not awaiting and holding up execution in some static extension class, but you handle that as if you were really doing a PostAsync or a PutAsync call. You also have the same overloads at your disposal and you're letting the HttpClient handle everything it was designed to handle.
For it to work you need to pass the content like that:
HttpContent httpContent = new StringContent("Your JSON-String", Encoding.UTF8, "application/json-patch+json");
Step 1: Create a Static class (I have created as Extention)
public static class Extention
{
public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient
client, string requestUri, T value)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter());
var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri)
{ Content = content };
return client.SendAsync(request);
}
}
Step 2 : Call this method in your api request
private static HttpClient client = new HttpClient();
var response = Extention.PatchAsJsonAsync<UserUpdateAPIModel>(client, "https://api.go1.com/v2/users/5886043", data);
Problem solved , here if it is common url , then you can do it with your practice
I have Google authentication working in my app through OAuthWebSecurity. Every time I login, Google re-prompts for permissions. How can I prevent Google from re-prompting every time?
I am currently using a custom OpenIdClient to get extra data from Google (source), but this also happens with the default client.
internal static class AuthConfig
{
public static void RegisterOpenAuth(ConfigurationWrapper configuration)
{
//OAuthWebSecurity.RegisterGoogleClient(); // using this has the exact same problem as my custom code
OAuthWebSecurity.RegisterClient(new GoogleCustomClient(), "Google", null);
}
}
/// <summary>
/// Represents Google OpenID client.
/// </summary>
public class GoogleCustomClient : OpenIdClient
{
#region Constructors and Destructors
public GoogleCustomClient()
: base("google", WellKnownProviders.Google)
{
}
#endregion
#region Methods
/// <summary>
/// Gets the extra data obtained from the response message when authentication is successful.
/// </summary>
/// <param name="response">
/// The response message.
/// </param>
/// <returns>A dictionary of profile data; or null if no data is available.</returns>
protected override Dictionary<string, string> GetExtraData(IAuthenticationResponse response)
{
var fetchResponse = response.GetExtension<FetchResponse>();
if (fetchResponse == null) return null;
var extraData = new Dictionary<string, string>
{
{"email", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email)},
{"country", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.Country)},
{"firstName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.First)},
{"lastName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.Last)}
};
return extraData;
}
/// <summary>
/// Called just before the authentication request is sent to service provider.
/// </summary>
/// <param name="request">
/// The request.
/// </param>
protected override void OnBeforeSendingAuthenticationRequest(IAuthenticationRequest request)
{
// Attribute Exchange extensions
var fetchRequest = new FetchRequest();
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Contact.HomeAddress.Country);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.Last);
request.AddExtension(fetchRequest);
}
#endregion
}