4

I am trying a simple display of github repositories. The url "https://api.github.com/search/repositories?q=pluralsight" works in my browser return json and works in fiddler but the following in my .NET Web App is getting a 403 Forbidden error. Can anyone help me understand a fix? My controller is as follows:

public class HomeController : Controller
    {
    public ActionResult Index()
        {
        Tweets model = null;
        var client = new HttpClient();
        var task = client.GetAsync("https://api.github.com/search/repositories?q=pluralsight")
            .ContinueWith((taskwithresponse) =>
            {
                var response = taskwithresponse.Result;
                response.EnsureSuccessStatusCode();
                var readtask = response.Content.ReadAsAsync<Tweets>();
                readtask.Wait();
                model = readtask.Result;

            });
        task.Wait();
        return View(model.results);
        }
    }

I have a class defined as follows (ignore that it is called Tweets) originally was trying to access twitter api.

namespace HttpClientMVCDemo.Controllers
{
public class Tweets
    {
    public Tweet[] results;
    }
public class Tweet
    {
    [JsonProperty("name")]
    public string UserName { get; set; }
    [JsonProperty("id")]
    public string id { get; set; }
    }
}

Code view auto generated based on Amit's classes below:

@model IEnumerable<HttpClientMVCDemo.Controllers.Gits>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
    <th>
        @Html.DisplayNameFor(model => model.total_count)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.incomplete_results)
    </th>
    <th></th>
</tr>

@foreach (var item in Model) {
<tr>
    <td>
        @Html.DisplayFor(modelItem => item.total_count)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.incomplete_results)
    </td>
    <td>
        @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
        @Html.ActionLink("Details", "Details", new       { /*id=item.PrimaryKey*/ }) |
        @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
    </td>
</tr>
}

jaykum
  • 138
  • 3
  • 10

3 Answers3

4

Well here are some modification in your model class

First Json string contain array of items not results

and you forget to provide get and set propertyin model class.

So This is new modified model class.

public class Tweets
    {
        public int total_count { get; set; }
        public bool incomplete_results { get; set; }
        public List<Item> items { get; set; }
    }

    public class Item
    {
        public int id { get; set; }
        public string name { get; set; }
        public string full_name { get; set; }
    }

And to get data from that url, You need to add User-Agent header in request. And also add this in your web.cofig file

<system.net>
    <settings>
      <httpWebRequest useUnsafeHeaderParsing="true" />
    </settings>
  </system.net>

So here is complete code.

Tweets model = null;           
            var client = new HttpClient();
            client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "http://developer.github.com/v3/#user-agent-required");
            var task = client.GetAsync("https://api.github.com/search/repositories?q=pluralsight")
                .ContinueWith((taskwithresponse) =>
                {
                    var response = taskwithresponse.Result.Content.ReadAsStringAsync();
                    response.Wait();
                    model = JsonConvert.DeserializeObject<Tweets>(response.Result);
                });
            task.Wait();
            return View(model.items);

And in your view should accept this type of model

@model IEnumerable<HttpClientMVCDemo.Controllers.Item>
Amit Kumar
  • 5,888
  • 11
  • 47
  • 85
  • Amit, I tried your solution. I had already taken care of the system.net addition. I remodeled my class and added the code you list. Now I am getting a different error "The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[HttpClientMVCDemo.Controllers.Item]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[HttpClientMVCDemo.Controllers.Gits]'." – jaykum Feb 28 '16 at 23:33
  • I am having trouble fitting it into comments (too long). – jaykum Feb 29 '16 at 10:21
  • @jaykum: replace this line `@model IEnumerable` with `@model IEnumerable` – Amit Kumar Feb 29 '16 at 10:28
  • Okay that worked, having some issues with model in the for each loop, I will try to fix it and if I can't I will post again later (have to go to work). I appreciate your help. – jaykum Feb 29 '16 at 10:38
  • Amit, sorry, work took me away. I really appreciate your help. I had to rebuild the View since I changed it from Gits to Item and now it works. – jaykum Mar 02 '16 at 10:19
2

Your browser automatically adds several accept headers to the request. You might have to add the headers in your request to avoid the 403.

 httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/json");

A similar question is here. The easiest way is to use Fiddler to check your request.

Furthermore you should not call Wait on your async call. Better declare the action as async and call await client.GetAsync();Otherwise you might get a deadlock. See here.

Community
  • 1
  • 1
slfan
  • 8,950
  • 115
  • 65
  • 78
2

HTTP 403: Forbidden is thrown because of administrative rules from github site.

Accessing api(https://api.github.com/) from github site expects 'User-Agent' header.

This can be resolved using below code:

HttpClient Client = new HttpClient();    
Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "http://developer.github.com/v3/#user-agent-required");
var res = Client.GetAsync("https://api.github.com/search/repositories?q=pluralsight").Result.Content.ReadAsStringAsync().Result;

Refernce: https://docs.github.com/en/free-pro-team@latest/rest/overview/resources-in-the-rest-api#user-agent-required

S.Pradeep
  • 487
  • 1
  • 5
  • 16