I am making a web app (.NET Core 3.1) and currently have an API created (using an API controller) that I want to consume in my web app (and display it's data on the View). The API is a few simple GET
requests as following (and is working great when accessing the proper endpoints):
//GET: api/Fish
[HttpGet]
public IActionResult Get()
{
IEnumerable<Fish> fish = _dataRepository.GetAll();
return Ok(fish);
}
//GET: api/Fish/5
[HttpGet]
[Route("{id}")]
public IActionResult Get(long id)
{
Fish fish = _dataRepository.Get(id);
if (fish == null)
{
return NotFound("The fish could not be found");
}
return Ok(fish);
}
Now, on my homepage I want to allow the user to type in an endpoint on my form, and I will return the JSON from my API. This is my form:
<form class="form-inline" asp-action="Index" asp-controller="Home">
<div class="form-group">
<label asp-for="Endpoint">https://url.com/api/</label>
<input type="text" class="form-control" placeholder="Try entering fish/4" asp-for="Endpoint"/>
<button type="submit" class="btn btn-primary">GET</button>
</div>
</form>
<div>
@Model.ReturnedJson
</div>
Here is the view model:
public class GetDataViewModel
{
public string Endpoint { get; set; }
public string ReturnedJson { get; set; }
}
And my homepage controller to get the entered endpoint, and retrieve the JSON from the API:
public async Task<IActionResult> Index(GetDataViewModel model)
{
var url = "https://localhost:44398/api/" + model.Endpoint;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
var table = Newtonsoft.Json.JsonConvert.DeserializeObject(data);
model.ReturnedJson = data;
}
}
return View();
}
What I'm wondering, is this:
Is HttpClient my best bet to handle this? I was hoping there was a way I could just call my API methods, something like ApiController().Get
but I don't think that is possible (at least in my research). Also, is it possible to return the RAW (preferably prettified) JSON directly on the homepage, or will I need to show it on a different view? I'm not 100% sure what my method should be returning in place of return View()
to show that JSON data.
Edit:
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(GetDataViewModel model)
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44398/api/" + model.Endpoint);
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
model.ReturnedJson = JsonConvert.SerializeObject(data, Formatting.Indented);
}
return View(model);
}
Those are both of my Index methods in the controller. I seem to be getting a Sustem.NullReferenceException when the web app starts up. I assume it's because my model isn't passed in on load (first Index method), but if I add it there, I get an error because it's then the same declaration as the second Index method. The following is my view:
@model TheFishAPI.Models.ViewModels.GetDataViewModel
@{
ViewData["Title"] = "Home Page";
}
<form class="form-inline" asp-action="Index" asp-controller="Home">
<div class="form-group">
<label asp-for="Endpoint">https://url/api/</label>
<input type="text" class="form-control" placeholder="Try entering fish/4" asp-for="Endpoint"/>
<button type="submit" class="btn btn-primary">GET</button>
</div>
</form>
<div>
@if (!string.IsNullOrEmpty(Model.ReturnedJson))
{
Html.Raw(Model.ReturnedJson);
}
</div>
You have a few options:
I personally would do this one. If your api doesn’t have any Cors issue, I would call it directly using JavaScript or jquery Ajax, they also have libraries that support rest calls. This would bypass the need to go via your controllers plus it can be done in ajax manner. If you go this route you can even do this to pretty print your json:
JSON.stringify({ uno: 1, dos: 2 }, null, '\t');
If all you need is to expose your api endpoints, have you looked at swagger ui? May be that’s not what you need but looking at the code you shared, seems like that’s what you are creating.
https://code-maze.com/different-ways-consume-restful-api-csharp/amp/
Pretty printing:
In c# you can do something like JsonConvert.SerializeObject(object, Formatting.Indented);
to pretty print, but ensure you add it using Raw tag helper or just indent it on the view like so:
@Html.Raw(JsonConvert.SerializeObject(ModelProperty, Formatting.Indented))
Last for your return View, you can return the same model in which you populated the json just make sure you indent it in action or directly in view to pretty print it.
public async Task<IActionResult> Index(GetDataViewModel model)
{
var url = "https://localhost:44398/api/" + model.Endpoint;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
// deserialize first since json is your response type
var table=Newtonsoft.Json.JsonConvert.DeserializeObject(data);
// re serialize with formatting:
model.ReturnedJson = JsonConvert.SerializeObject(table, Formatting.Indented);
}
}
return View(model);
}
In your view do this:
<pre>
@Html.Raw(Model.ReturnedJson)
</pre>