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.
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.
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''
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]'.'
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)
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.
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>
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.
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:
Controller:
Patrial view which I'd render within a master template.
So, the error I'm getting just now is:
Hope someone is available to help me.
Hi Owain,
What happens if you change your Model definition at the top of the view to this:
or to this:
Cheers for the quick reply.
If I change it to List, I get the following error.
If I change it to IEnumberable, I get this error:
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
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:
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:(although the model/list is currently empty, which must be due to something in your controller)
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
https://github.com/Migaroez/our.ProjectAlpha
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.
I'm being told that IList
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.
I'm such an idiot, ill push tonight since its on my home computer...
No problems :) I wasn't sure if I had just missed something.
Your model is now the list, and the list doesn't have the
athlete
andaverage_heartrate
. Each item in the list should have these two properties.But like Nik wrote, you can try to hardcode these for now.
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
StravaModel
View
MasterPage calls the controller via:
Thanks for your help everyone!
is working on a reply...