/// <summary> /// Deletes an existing SharePoint web hook /// </summary> /// <param name="webUrl">Url of the site holding resource with the webhook</param> /// <param name="resourceType">The type of Hookable SharePoint resource</param> /// <param name="resourceId">Id of the resource (e.g. list id)</param> /// <param name="subscriptionId">Id of the web hook subscription that we need to delete</param> /// <param name="accessToken">Access token to authenticate against SharePoint</param> /// <param name="context">ClientContext instance to use for authentication</param> /// <returns>true if succesful, exception in case something went wrong</returns> internal static async Task <bool> RemoveWebhookSubscriptionAsync(string webUrl, WebHookResourceType resourceType, string resourceId, string subscriptionId, string accessToken, ClientContext context) { using (var handler = new HttpClientHandler()) { if (String.IsNullOrEmpty(accessToken)) { context.Web.EnsureProperty(p => p.Url); handler.Credentials = context.Credentials; handler.CookieContainer.SetCookies(new Uri(context.Web.Url), (context.Credentials as SharePointOnlineCredentials).GetAuthenticationCookie(new Uri(context.Web.Url))); } using (var httpClient = new PnPHttpProvider(handler)) { string identifierUrl = GetResourceIdentifier(resourceType, webUrl, resourceId); if (string.IsNullOrEmpty(identifierUrl)) { throw new Exception("Identifier of the resource cannot be determined"); } string requestUrl = string.Format("{0}/{1}('{2}')", identifierUrl, SubscriptionsUrlPart, subscriptionId); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Delete, requestUrl); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.StatusCode != System.Net.HttpStatusCode.NoContent) { // oops...something went wrong, maybe the web hook does not exist? throw new Exception(await response.Content.ReadAsStringAsync()); } else { return(true); } } } }
/// <summary> /// Executes a REST Get request. /// </summary> /// <param name="web">The current web to execute the request against</param> /// <param name="endpoint">The full endpoint url, exluding the URL of the web, e.g. /_api/web/lists</param> /// <returns></returns> internal static async Task <string> ExecuteGetAsync(this Web web, string endpoint) { string returnObject = null; var accessToken = web.Context.GetAccessToken(); using (var handler = new HttpClientHandler()) { web.EnsureProperty(w => w.Url); // we're not in app-only or user + app context, so let's fall back to cookie based auth if (String.IsNullOrEmpty(accessToken)) { handler.SetAuthenticationCookies(web.Context as ClientContext); } using (var httpClient = new PnPHttpProvider(handler)) { var requestUrl = $"{web.Url}{endpoint}"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } else { if (web.Context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } } var requestDigest = await(web.Context as ClientContext).GetRequestDigestAsync().ConfigureAwait(false); request.Headers.Add("X-RequestDigest", requestDigest); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { returnObject = responseString; } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } return(await Task.Run(() => returnObject)); }
/// <summary> /// This helper method makes an HTTP request and eventually returns a result /// </summary> /// <param name="httpMethod">The HTTP method for the request</param> /// <param name="requestUrl">The URL of the request</param> /// <param name="responseHeaders">The response headers of the HTTP request (output argument)</param> /// <param name="accessToken">The OAuth 2.0 Access Token for the request, if authorization is required</param> /// <param name="accept">The content type of the accepted response</param> /// <param name="content">The content of the request</param> /// <param name="contentType">The content type of the request</param> /// <param name="referer">The URL Referer for the request</param> /// <param name="resultPredicate">The predicate to retrieve the result, if any</param> /// <param name="requestHeaders">A collection of any custom request headers</param> /// <param name="cookies">Any request cookies values</param> /// <param name="retryCount">Number of times to retry the request</param> /// <param name="delay">Milliseconds to wait before retrying the request. The delay will be increased (doubled) every retry</param> /// <param name="userAgent">UserAgent string value to insert for this request. You can define this value in your app's config file using key="SharePointPnPUserAgent" value="PnPRocks"</param> /// <param name="spContext">An optional SharePoint client context</param> /// <typeparam name="TResult">The type of the result, if any</typeparam> /// <returns>The value of the result, if any</returns> private static TResult MakeHttpRequest <TResult>( string httpMethod, string requestUrl, out HttpResponseHeaders responseHeaders, string accessToken = null, string accept = null, object content = null, string contentType = null, string referer = null, Func <HttpResponseMessage, TResult> resultPredicate = null, Dictionary <string, string> requestHeaders = null, Dictionary <string, string> cookies = null, int retryCount = 1, int delay = 500, string userAgent = null, ClientContext spContext = null) { HttpClient client = HttpHelper.httpClient; // Define whether to use the default HttpClient object // or a custom one with retry logic and/or with custom request cookies // or a SharePoint client context to rely on if (retryCount >= 1 || (cookies != null && cookies.Count > 0) || spContext != null) { // Let's create a custom HttpHandler var handler = new HttpClientHandler(); // Process any SPO authentication cookies, if we have an SPO context if (spContext != null) { SetAuthenticationCookies(handler, spContext); if (requestHeaders == null) { requestHeaders = new Dictionary <string, string>(); } if (!requestHeaders.ContainsKey("X-RequestDigest")) { requestHeaders.Add("X-RequestDigest", spContext.GetRequestDigest().GetAwaiter().GetResult()); } } // Process any other request cookies if (cookies != null) { foreach (var cookie in cookies) { handler.CookieContainer.Add(new System.Net.Cookie(cookie.Key, cookie.Value)); } } // And now create the customized HttpClient object client = new PnPHttpProvider(handler, true, retryCount, delay, userAgent); } // Prepare the variable to hold the result, if any TResult result = default(TResult); responseHeaders = null; Uri requestUri = new Uri(requestUrl); // If we have the token, then handle the HTTP request // Set the Authorization Bearer token if (!string.IsNullOrEmpty(accessToken)) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } if (!string.IsNullOrEmpty(referer)) { client.DefaultRequestHeaders.Referrer = new Uri(referer); } // If there is an accept argument, set the corresponding HTTP header if (!string.IsNullOrEmpty(accept)) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue(accept)); } // Process any additional custom request headers if (requestHeaders != null) { foreach (var requestHeader in requestHeaders) { client.DefaultRequestHeaders.Add(requestHeader.Key, requestHeader.Value); } } // Prepare the content of the request, if any HttpContent requestContent = null; System.IO.Stream streamContent = content as System.IO.Stream; if (streamContent != null) { requestContent = new StreamContent(streamContent); requestContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); } else if (content != null) { var jsonString = content is string ?content.ToString() : JsonConvert.SerializeObject(content, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new ODataBindJsonResolver(), }); requestContent = new StringContent(jsonString, Encoding.UTF8, contentType); } // Prepare the HTTP request message with the proper HTTP method HttpRequestMessage request = new HttpRequestMessage( new HttpMethod(httpMethod), requestUrl); // Set the request content, if any if (requestContent != null) { request.Content = requestContent; } // Fire the HTTP request HttpResponseMessage response = client.SendAsync(request).Result; if (response.IsSuccessStatusCode) { // If the response is Success and there is a // predicate to retrieve the result, invoke it if (resultPredicate != null) { result = resultPredicate(response); } // Get any response header and put it in the answer responseHeaders = response.Headers; } else { throw new ApplicationException( string.Format("Exception while invoking endpoint {0}.", requestUrl), #if !NETSTANDARD2_0 new HttpException( (int)response.StatusCode, response.Content.ReadAsStringAsync().Result)); #else new Exception(response.Content.ReadAsStringAsync().Result)); #endif } return(result); }
/// <summary> /// Updates the expiration datetime (and notification URL) of an existing SharePoint web hook /// </summary> /// <param name="webUrl">Url of the site holding resource with the webhook</param> /// <param name="resourceType">The type of Hookable SharePoint resource</param> /// <param name="resourceId">Id of the resource (e.g. list id)</param> /// <param name="subscriptionId">Id of the web hook subscription that we need to delete</param> /// <param name="webHookEndPoint">Url of the web hook service endpoint (the one that will be called during an event)</param> /// <param name="expirationDateTime">New web hook expiration date</param> /// <param name="accessToken">Access token to authenticate against SharePoint</param> /// <param name="context">ClientContext instance to use for authentication</param> /// <exception cref="ArgumentOutOfRangeException">Thrown when expiration date is out of valid range.</exception> /// <returns>true if succesful, exception in case something went wrong</returns> public static async Task <bool> UpdateWebhookSubscriptionAsync(string webUrl, WebHookResourceType resourceType, string resourceId, string subscriptionId, string webHookEndPoint, DateTime expirationDateTime, string accessToken, ClientContext context) { if (!ValidateExpirationDateTime(expirationDateTime)) { throw new ArgumentOutOfRangeException(nameof(expirationDateTime), "The specified expiration date is invalid. Should be greater than today and within 6 months"); } await new SynchronizationContextRemover(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(p => p.Url); if (String.IsNullOrEmpty(accessToken)) { handler.SetAuthenticationCookies(context); } using (var httpClient = new PnPHttpProvider(handler)) { string identifierUrl = GetResourceIdentifier(resourceType, webUrl, resourceId); if (string.IsNullOrEmpty(identifierUrl)) { throw new Exception("Identifier of the resource cannot be determined"); } string requestUrl = string.Format("{0}/{1}('{2}')", identifierUrl, SubscriptionsUrlPart, subscriptionId); HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUrl); request.Headers.Add("X-RequestDigest", await context.GetRequestDigestAsync()); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } WebhookSubscription webhookSubscription; if (string.IsNullOrEmpty(webHookEndPoint)) { webhookSubscription = new WebhookSubscription() { ExpirationDateTime = expirationDateTime }; } else { webhookSubscription = new WebhookSubscription() { NotificationUrl = webHookEndPoint, ExpirationDateTime = expirationDateTime }; } request.Content = new StringContent(JsonConvert.SerializeObject(webhookSubscription), Encoding.UTF8, "application/json"); HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.StatusCode != System.Net.HttpStatusCode.NoContent) { // oops...something went wrong, maybe the web hook does not exist? throw new Exception(await response.Content.ReadAsStringAsync()); } else { return(true); } } } }