Get data from external API with a asp.net core API
I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
asp.net api asp.net-web-api
add a comment |
I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
asp.net api asp.net-web-api
add a comment |
I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
asp.net api asp.net-web-api
I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
asp.net api asp.net-web-api
asp.net api asp.net-web-api
edited Nov 15 '18 at 19:30
Jefferson Bruchado
asked Nov 15 '18 at 18:50
Jefferson BruchadoJefferson Bruchado
389314
389314
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
You could use an HttpClient
instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
{
var client = new RestClient("http://api.football-
data.org/v1/competitions/445/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content
property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath
syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository
in your sample:
public RankingRepository: IRankingRepository
{
public LeagueTableModel GetRankings(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
var model = _rankingRepository.GetRankings(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53326123%2fget-data-from-external-api-with-a-asp-net-core-api%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You could use an HttpClient
instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
{
var client = new RestClient("http://api.football-
data.org/v1/competitions/445/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content
property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath
syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository
in your sample:
public RankingRepository: IRankingRepository
{
public LeagueTableModel GetRankings(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
var model = _rankingRepository.GetRankings(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
add a comment |
You could use an HttpClient
instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
{
var client = new RestClient("http://api.football-
data.org/v1/competitions/445/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content
property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath
syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository
in your sample:
public RankingRepository: IRankingRepository
{
public LeagueTableModel GetRankings(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
var model = _rankingRepository.GetRankings(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
add a comment |
You could use an HttpClient
instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
{
var client = new RestClient("http://api.football-
data.org/v1/competitions/445/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content
property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath
syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository
in your sample:
public RankingRepository: IRankingRepository
{
public LeagueTableModel GetRankings(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
var model = _rankingRepository.GetRankings(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
You could use an HttpClient
instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
{
var client = new RestClient("http://api.football-
data.org/v1/competitions/445/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content
property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath
syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository
in your sample:
public RankingRepository: IRankingRepository
{
public LeagueTableModel GetRankings(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
var model = _rankingRepository.GetRankings(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!
edited Nov 15 '18 at 21:10
answered Nov 15 '18 at 19:46
Karel TamayoKarel Tamayo
2,79721623
2,79721623
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
add a comment |
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
1
1
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:
public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
I suggest to use async programming model based on async and await keywords to improve responsiveness. Something like this:
public async Task<IActionResult> GetById(long id) { IRestResponse response = await client.ExecuteAsync(request);
– Mohsin Mehmood
Nov 15 '18 at 21:57
1
1
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
Thank you very much for sharing your knowledge, it worked perfectly!
– Jefferson Bruchado
Nov 15 '18 at 22:20
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53326123%2fget-data-from-external-api-with-a-asp-net-core-api%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown