Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Owain Williams 240 posts 760 karma points
    1 week ago
    Owain Williams
    0

    Hand holding required - Consume API which requires authentication

    Hi, I've been trying to do this for some time now and keep on getting started then stop. I'm looking for a bit of hand holding to help me consume an external API which requires authentication to consume the data. The API I want to play with just now is https://strava.github.io/api/

    I'd like to pull data in to Umbraco via MVC and then display information to the user. I've looked around at a number of different websites but, unless I've missed it, I haven't found a step by step guide on how to do this with Umbraco. My plan is to create this step by step guide so others don't have the headache I've had trying to do this.

    I'd imagine that if I can get it to work with the strava api then making it work with other sources shouldnt be too difficult.

    Hope someone can help, thanks,

    Owain.

  • Owain Williams 240 posts 760 karma points
    6 days ago
    Owain Williams
    0

    Ok, so moving forward I've now got my model, controller and view and a feed: https://www.strava.com/api/v3/athlete/activities?accesstoken=dbf8e1247d8cbe41785c2cf52f71123452317c50&perpage=200

    Model:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace ProjectAlpha.App_Code.Models
    {
    
        public class StravaModel
        {
            public int id { get; set; }
            public int resource_state { get; set; }
            public string external_id { get; set; }
            public int? upload_id { get; set; }
            public Athlete athlete { get; set; }
            public string name { get; set; }
            public float distance { get; set; }
            public int moving_time { get; set; }
            public int elapsed_time { get; set; }
            public float total_elevation_gain { get; set; }
            public string type { get; set; }
            public DateTime start_date { get; set; }
            public DateTime start_date_local { get; set; }
            public string timezone { get; set; }
            public float utc_offset { get; set; }
            public float[] start_latlng { get; set; }
            public float[] end_latlng { get; set; }
            public string location_city { get; set; }
            public string location_state { get; set; }
            public string location_country { get; set; }
            public float? start_latitude { get; set; }
            public float? start_longitude { get; set; }
            public int achievement_count { get; set; }
            public int kudos_count { get; set; }
            public int comment_count { get; set; }
            public int athlete_count { get; set; }
            public int photo_count { get; set; }
            public Map map { get; set; }
            public bool trainer { get; set; }
            public bool commute { get; set; }
            public bool manual { get; set; }
            public bool _private { get; set; }
            public bool flagged { get; set; }
            public string gear_id { get; set; }
            public bool? from_accepted_tag { get; set; }
            public float average_speed { get; set; }
            public float max_speed { get; set; }
            public float average_watts { get; set; }
            public float kilojoules { get; set; }
            public bool device_watts { get; set; }
            public bool has_heartrate { get; set; }
            public float elev_high { get; set; }
            public float elev_low { get; set; }
            public int pr_count { get; set; }
            public int total_photo_count { get; set; }
            public bool has_kudoed { get; set; }
            public int? workout_type { get; set; }
            public float average_cadence { get; set; }
            public float average_heartrate { get; set; }
            public float max_heartrate { get; set; }
    
         }
    
        public class Athlete
        {
            public int id { get; set; }
            public int resource_state { get; set; }
        }
    
        public class Map
        {
            public string id { get; set; }
            public string summary_polyline { get; set; }
            public int resource_state { get; set; }
        }
    
    }
    

    Controller:

    using ProjectAlpha.App_Code.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Web.Mvc;
    using Umbraco.Web.Mvc;
    using Umbraco.Core.Models;
    using Umbraco.Web;
    
    namespace ProjectAlpha.App_Code.Controllers
    {
        public class StravaController : SurfaceController
        {
            public ActionResult Index()
            {
    
             IEnumerable<StravaModel> activities = null;
    
                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri("https://www.strava.com/api/v3/athlete/");
                    //HTTP Get
                    var responseTask = client.GetAsync("activites");
                    responseTask.Wait();
    
                    var result = responseTask.Result;
                    if (result.IsSuccessStatusCode)
                    {
                        var readTask = result.Content.ReadAsAsync<IList<StravaModel>>();
                        readTask.Wait();
    
                        activities = readTask.Result;
                    }
                    else //web api sent error response 
                    {
                        //log response status here..
    
                        activities = Enumerable.Empty<StravaModel>();
    
                        ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
                    }
                }
    
                return View(activities);
            }
    
        }
    }
    

    Patrial view which I'd render within a master template.

    @model ProjectAlpha.App_Code.Models.StravaModel
    @{
        Layout = null;
        ViewBag.Title = "Index";
    }
    
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.athlete)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.average_heartrate)
            </th>
            <th></th>
        </tr>
    
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.athlete)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.average_heartrate)
                </td>
    
            </tr>
        }
    
    </table>
    

    So, the error I'm getting just now is:

    foreach statement cannot operate on variables of type 'ProjectAlpha.AppCode.Models.StravaModel' because 'ProjectAlpha.AppCode.Models.StravaModel' does not contain a public definition for 'GetEnumerator''

    Hope someone is available to help me.

  • Nik 526 posts 2112 karma points
    6 days ago
    Nik
    1

    Hi Owain,

    What happens if you change your Model definition at the top of the view to this:

    @model List<ProjectAlpha.App_Code.Models.StravaModel>
    

    or to this:

    @model IEnumerable<ProjectAlpha.App_Code.Models.StravaModel>
    
  • Owain Williams 240 posts 760 karma points
    6 days ago
    Owain Williams
    0

    Cheers for the quick reply.

    If I change it to List, I get the following error.

    'System.Collections.Generic.List<ProjectAlpha.App_Code.Models.StravaModel>' does not contain a definition for 'athlete' and no extension method 'athlete' accepting a first argument of type 'System.Collections.Generic.List<ProjectAlpha.App_Code.Models.StravaModel>' could be found (are you missing a using directive or an assembly reference?)'
    

    If I change it to IEnumberable, I get this error:

    System.InvalidOperationException: 'The model item passed into the dictionary is of type 'Umbraco.Web.Models.RenderModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[ProjectAlpha.App_Code.Models.StravaModel]'.'
    
  • Nik 526 posts 2112 karma points
    6 days ago
    Nik
    1

    Hi Owen,

    The issue is that your API code (in the controller) results in an IEnumerable collection of the StravaModel class.

    In the view, are you doing a mix of trying to treat it as an individual and as a collection which isn't possible. Conflicting the behaviour.

    Try hard coding the @Html.DisplayNameFor fields that are outside of your loop and see if that works.

    Thanks,

    Nik

  • Anders Bjerner 321 posts 1838 karma points mvp c-trib
    6 days ago
    Anders Bjerner
    1

    I think you will need to create a partial view, as you can't change the model of your master view like this.

    Your master view could look something like:

    @inherits UmbracoTemplatePage
    @{
        Layout = null;
        ViewBag.Title = "Index";
    }
    
    @Html.Action("Index", "Strava")
    

    The last line refers to the Index action/method in your surface controller. By convention, MVC will then look for a partial view at ~/Views/Strava/Index.cshtml, which could look like:

    @inherits UmbracoViewPage<IList<WebApplication9.StravaModel>>
    
    <p>
            @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        @*
            As the model is now a collection, it doesn't have the "athlete" and "average_heartrate" properties
            <tr>
            <th>
                @Html.DisplayNameFor(model => model.athlete)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.average_heartrate)
            </th>
            <th></th>
        </tr>*@
    
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.athlete)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.average_heartrate)
                </td>
    
            </tr>
        }
    
    </table>
    

    (although the model/list is currently empty, which must be due to something in your controller)

  • Sven Geusens 117 posts 642 karma points
    6 days ago
    Sven Geusens
    1

    Hey Owain, i made a working example (except for api) based on your code with comments where needed. And some possible insights.

    What I learned today: There are extension methods on HttpContent that let you do ReadAsAsync. Thanks for that, gotta test them out.

    https://github.com/Migaroez/our.ProjectAlpha

  • Owain Williams 240 posts 760 karma points
    6 days ago
    Owain Williams
    0

    Hi, the repo looks empty? Did you forget to commit?

    Thanks for the help everyone. I think I'm almost there but still getting an error. It's on my View now.

    @inherits UmbracoViewPage<IList<ProjectAlpha.App_Code.Models.StravaModel>>
    
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.athlete)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.average_heartrate)
            </th>
            <th></th>
        </tr>
    

    I'm being told that IList does not contain a definition for 'athlete' and no extension method 'athlete' accepting a first argument of type IList could be found.

    So I have a master page with @Html.Action("Index", "Strava"), I've a model called StravaModel, which I used the special paste to make it from the API feed. I've the controller which I think may be the weak link as I've not really got my head around how I query the strava api. I don't need to authenticate with it because it's pulling the data from a public account.

  • Sven Geusens 117 posts 642 karma points
    6 days ago
    Sven Geusens
    1

    I'm such an idiot, ill push tonight since its on my home computer...

  • Owain Williams 240 posts 760 karma points
    6 days ago
    Owain Williams
    0

    No problems :) I wasn't sure if I had just missed something.

  • Anders Bjerner 321 posts 1838 karma points mvp c-trib
    6 days ago
    Anders Bjerner
    1

    Your model is now the list, and the list doesn't have the athlete and average_heartrate. Each item in the list should have these two properties.

    But like Nik wrote, you can try to hardcode these for now.

  • Owain Williams 240 posts 760 karma points
    6 days ago
    Owain Williams
    100

    Thanks! I didn't spot the commented out part int he code. I've now got it working!

    For anyone else interested, here is my code:

    StravaController

    using ProjectAlpha.App_Code.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Web.Mvc;
    using Umbraco.Web.Mvc;
    using Umbraco.Core.Models;
    using Umbraco.Web;
    using System.Threading.Tasks;
    using System.Net.Http.Headers;
    using Newtonsoft.Json;
    
    namespace ProjectAlpha.App_Code.Controllers
    {
        public class StravaController : SurfaceController
        {
            string BaseUrl = "https://www.strava.com/";
            public ActionResult Index()
            {
    
                IEnumerable<StravaModel> activities = null;
    
                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri("https://www.strava.com/");
                    //HTTP Get
                    var responseTask = client.GetAsync("api/v3/athlete/activities?access_token=dbf8e1247d8cbe41785c2cf52f71123452317c50&per_page=200");
                    responseTask.Wait();
    
                    var result = responseTask.Result;
                    if (result.IsSuccessStatusCode)
                    {
                        var readTask = result.Content.ReadAsAsync<IList<StravaModel>>();
                        readTask.Wait();
    
                        activities = readTask.Result;
                    }
                    else //web api sent error response 
                    {
                        //log response status here..
    
                        activities = Enumerable.Empty<StravaModel>();
    
                        ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
                    }
                }
    
                return View(activities);
            }
        }
    }
    

    StravaModel

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace ProjectAlpha.App_Code.Models
    {
    
        public class StravaModel
        {
            public int id { get; set; }
            public int resource_state { get; set; }
            public string external_id { get; set; }
            public int? upload_id { get; set; }
            public Athlete athlete { get; set; }
            public string name { get; set; }
            public float distance { get; set; }
            public int moving_time { get; set; }
            public int elapsed_time { get; set; }
            public float total_elevation_gain { get; set; }
            public string type { get; set; }
            public DateTime start_date { get; set; }
            public DateTime start_date_local { get; set; }
            public string timezone { get; set; }
            public float utc_offset { get; set; }
            public float[] start_latlng { get; set; }
            public float[] end_latlng { get; set; }
            public string location_city { get; set; }
            public string location_state { get; set; }
            public string location_country { get; set; }
            public float? start_latitude { get; set; }
            public float? start_longitude { get; set; }
            public int achievement_count { get; set; }
            public int kudos_count { get; set; }
            public int comment_count { get; set; }
            public int athlete_count { get; set; }
            public int photo_count { get; set; }
            public Map map { get; set; }
            public bool trainer { get; set; }
            public bool commute { get; set; }
            public bool manual { get; set; }
            public bool _private { get; set; }
            public bool flagged { get; set; }
            public string gear_id { get; set; }
            public bool? from_accepted_tag { get; set; }
            public float average_speed { get; set; }
            public float max_speed { get; set; }
            public float average_watts { get; set; }
            public float kilojoules { get; set; }
            public bool device_watts { get; set; }
            public bool has_heartrate { get; set; }
            public float elev_high { get; set; }
            public float elev_low { get; set; }
            public int pr_count { get; set; }
            public int total_photo_count { get; set; }
            public bool has_kudoed { get; set; }
            public int? workout_type { get; set; }
            public float average_cadence { get; set; }
            public float average_heartrate { get; set; }
            public float max_heartrate { get; set; }
    
         }
    
        public class Athlete
        {
            public int id { get; set; }
            public int resource_state { get; set; }
        }
    
        public class Map
        {
            public string id { get; set; }
            public string summary_polyline { get; set; }
            public int resource_state { get; set; }
        }
    
    }
    

    View

    @inherits UmbracoViewPage<IList<ProjectAlpha.App_Code.Models.StravaModel>>
    
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
    
    
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.kudos_count)
                </td>
                <td>
                    @Html.ActionLink("Edit", "Edit", new { id = item.id }) |
                    @Html.ActionLink("Details", "Details", new { id = item.id }) |
                    @Html.ActionLink("Delete", "Delete", new { id = item.id })
                </td>
            </tr>
        }
    
    </table>
    

    MasterPage calls the controller via:

     @Html.Action("Index", "Strava")
    

    Thanks for your help everyone!

Please Sign in or register to post replies

Write your reply to:

Draft