1

I want to get the Web Api work on SPO. I did the example code of https://blogs.msmvps.com/windsor/2017/03/12/walkthrough-building-a-custom-web-api-for-use-with-sharepoint-online/ and it does works fine.

Now I want to add a get-Request to SPO, which works on-premise as well. I tried reading a file with clientcontext and Webclient, which I would to prefer. But I always get an 401 error "the remote server returned an error (401) unauthorized"

I do not understand the 401 error, because the example code works fine. I do not see any differences.

        public async Task<IEnumerable<string>> GetUserInfo(string sharePointUrl)
    {
        var results = new List<string>();

        try
        {
            var userToken = this.Request.Headers.Authorization.Parameter;
            var newToken = await GetSharePointAccessToken(sharePointUrl, userToken);

            using (var context = new ClientContext(sharePointUrl))
            {
                context.ExecutingWebRequest +=
                    (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add(
                    "Authorization", "Bearer " + newToken);

                var web = context.Web;
                var user = web.CurrentUser;
                context.Load(user);
                context.ExecuteQuery();

                results.Add(user.Title);
                results.Add(user.LoginName);
                results.Add(user.Email);
            }
        }
        catch (Exception ex)
        {
            results.Add(ex.ToString());
        }

        return results;
    }

Here is my code snippet:

        public async Task<IEnumerable<LessVariables>> GetLess(string url, string id)
    {
       [...]

        var list = new List<LessVariables>();

        try
        {
            var userToken = this.Request.Headers.Authorization.Parameter;
            var newToken = await GetSharePointAccessToken(url, userToken);
            var siteUrl = url;
            var file = siteUrl + "/Style Library/xxx/less/" + id + ".less";
            var files = "/Style Library/xxx/less/" + id + ".less";

            using (var context = new ClientContext(url))
            {
                context.ExecutingWebRequest +=
                    (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add(
                    "Authorization", "Bearer " + newToken);
                context.AuthenticationMode = ClientAuthenticationMode.Default;
                context.ExecuteQuery();
                var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(context, files);

// After this line I get the 401 error


                using (WebClient client = new WebClient())
                {
                    client.Headers[HttpRequestHeader.Authorization] = "Bearer " + newToken;
                    //client.UseDefaultCredentials = true;
                    string s = client.DownloadString(file);

// After try to DownloadString I got the 401 error.

                    var regex = new Regex(@"(?<name>[@].*:)\s(?<value>.*)(;)");
                    var matches = regex.Matches(s);
                    var parameters = new List<KeyValuePair<string, string>>();

                    foreach (var match in matches.Cast<Match>())
                    {
                        list.Add(new LessVariables
                        {
                            key = match.Groups["name"].Value,
                            value = match.Groups["value"].Value
                        });
                    }
                }
            }
            return list;
        }
        catch (Exception ex)
        { [...]

        }
    }
Nick Delefski
  • 80
  • 3
  • 11
  • To test the code on SPO, I used for test purposes `client.Credentials = new SharePointOnlineCredentials(username, passWord);` The code is fine and everything works, but I do not want to use credentials. The API has to be authenticated by a token. – Nick Delefski Nov 17 '17 at 12:28

1 Answers1

2

The reason you are experiencing this behavior while reading file is that the request http://<site url>//<folder name>/<file name> causes redirect but WebClient strip out authorization headers (such as Bearer token) when following a redirect. The similar behavior has been explained in this post.

Since you are targeting SharePoint Online, you could retrieve a file content using a different approach that does not cause a redirect, for example using SharePoint REST API, in particular the following File resource endpoint:

Url: http://<site url>/_api/web/getfilebyserverrelativeurl('/<folder name>/<file name>')/$value
Method: Get

Example

using (var client = new WebClient())
{
    client.Headers[HttpRequestHeader.Authorization] = "Bearer " + accessToken;     
    var content = client.DownloadString("https://contoso.sharepoint.com/_api/web/getfilebyserverrelativeurl('/Shared%20Documents/Guide.docx')/$value"); 
    //...     
}
Vadim Gremyachev
  • 57,952
  • 20
  • 129
  • 193
  • 1
    Thanks alot! I did not know that I did a redirect by using the full uri. If I understand you correctly, I have to use the SharePoint REST API when using SPO? – Nick Delefski Nov 18 '17 at 13:10
  • It is not mandatory to use REST API, another APIs (CSOM, RPC) are still available. But given the requirements to authenticate via OAuth bearer token and the mentioned limitations I think the REST is a way to go to download a file here – Vadim Gremyachev Nov 18 '17 at 13:27
  • Okay thanks for your answer. Last but not least - I execute the command with the permissions of the user, who calls the Web API (-> behalf of user / impersonation?)? – Nick Delefski Nov 18 '17 at 16:31
  • 1
    Web API is called on using an app+user context _on behalf_ of the currently logged in user, please refer [this article](https://blogs.msdn.microsoft.com/kaevans/2014/04/15/calling-o365-apis-from-your-web-api-on-behalf-of-a-user/) for a more details – Vadim Gremyachev Nov 19 '17 at 10:10