I'm working on an ASP.NET Coe Web API project.
I have a post method (route) and I need it to call another get method:
[HttpGet(Name = "Profile")]
public async Task<ActionResult<ProfileResponseDto>> GetProfile(string username)
If I call :
return RedirectToRoute("Profile", new { username = profile.UserName });
I will end up with Get resource.
But what I want is to take the result of that Get method and continue in my post method.
In Python its something like
amount = requests.get("http://"+catalogIpAddress+":"+port+"/count/" + str(id))
HttpResponse.RedirectToRoute Method Redirects a request to a new URL by using route parameter values, a route name, or both this means that you cannot continue with the last URL
HttpResponse.RedirectToRoute Method
but you can return value from another action result like
var result = await controller.GetTodosAsync();
so you can use like this :
[HttpGet(Name = "Profile")]
public async Task<string> GetProfile(string username)
{
return await Task.Run(()=> "hello") ;
}
[HttpPost(Name = "Profile")]
public async Task<string> PostProfile(string username)
{
string result = await this.GetProfile(username);
result = result + " from post";
return await Task.Run(() => result);
}
Related
I have this in my repository:
public async Task<IEnumerable<CatalogModel>> GetCatalogByName(string _UserId, string _CatalogName)
{
var data = await dbcontext.Catalog.Where(x => x.UserId == _UserId).ToListAsync();
return mapper.Map<IEnumerable<CatalogModel>>(data);
}
And currently, this in my Controller:
[HttpGet]
public IActionResult GetCatalogsByName([FromQuery] string UserId, string CatalogName)
{
var task = repository.Catalog.GetCatalogByName(UserId, CatalogName);
return Ok(task);
}
So right now I am returning Ok(task) all the time. I would like to check if there is data returned from the repository or not so I can also return NotFound(task). I could not seem to figure out how to do that.
You will need to wait for the GetCatalogByName to complete before examining the result.
A simple await will do
[HttpGet]
public IActionResult GetCatalogsByName([FromQuery] string UserId, string CatalogName)
{
var task = await repository.Catalog.GetCatalogByName(UserId, CatalogName);
// check task data before return
return Ok(task);
}
But I strongly recommend you to read more about async/await programming here
I studied over the Internet regarding Task Async method but cannot seem to find an approach to assign my return value in Task Async to another object. The first method is to prepare HTTP Request header and Uri.
public static async Task MainAsync()
{
string token = await AuthHelper.AcquireToken(tenantId, clientId, clientSecret);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.BaseAddress = new Uri("https://foo.net");
await GetValue(client);
}
}
The second method is to use GetAsync to call to an API to get the JSON and the two last lines I extract only value from the "Value" field in the JSON body.
public static async Task<String> GetValue(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
JObject json = JObject.Parse(responsContent);
string value = json["value"].ToString();
return value;
}
}
Now I would like to use this value to assign to another object, but not sure how to do so. I managed to return the valid value. Is it possible to retrieve the value from another method or even different class?
[Updated] The main function is:
static void Main(string[] args)
{
try
{
MainAsync().Wait();
}
catch (Exception e)
{
Console.WriteLine(e.GetBaseException().Message);
}
}
Update
To be more clear. The HTTP response message is a JSON format and I can return the value from Value property in this JSON. Now how I can to reuse the value from an external method or class
I'm not sure exactly what you are trying to achieve. And there would be thorough debates about your architecture, you can do something like this..
Update
Because your MainAsync is static it can be called form anywhere.
You just need to modify it a bit to return your result as follows :
public static async Task<string> MainAsync()
{
...
return await GetValue(client);
...
And somewhere else
public class MyAwesomeClass
{
public async Task DoMagic()
{
var newValueOfSomething = await MainAsync();
// hilarity ensues
}
}
You can Make it more generic and useful, something like below :
Your initial method can be changes to :
public async Task<T> GetContentAsync<T>(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
return Deserialize<T>(json);
}
}
private T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, SerializationSettings);
}
You can now call method like :
var person = await GetContentAsync<Person>(/*http client*/)
Previously i handled all my http requests in a single class but i would like to move the http login functionality to a different class but now i cant access the http client response.IsSuccessStatusCode
this is my original code whic works
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl+"/v2auth/default/login");
var response2 = await http.PostAsync(url, credentials);
if (response.IsSuccessStatusCode)
{
//do after login stuff
}
Now i would like to move the login logic to a different class that is in a different folder(auth->dbhelpers)
class LoginHttp
{
public static async Task<object> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
So am now trying to access the returned response via
var responsefromhttplogin = auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin .IsSuccessStatusCode) //this fails
{
//do after login stuff
}
How can i get the retrned response be of type HttpClient again?
Am getting an error of
Task<Objct> does not contain defination for IsSuccessStatusCode
Make your loginAsync method return Task instead. Currently you are returning an object, then you will have access to the IsSuccessStatusCode
class LoginHttp{
public static async Task<HttpResponseMessage> loginAsync(String username, String password)
{
var values = new Dictionary<string, string>
{
{ "username",username },
{ "password", password }
};
var credentials = new FormUrlEncodedContent(values);
var http = new HttpClient();
var url = String.Format(shared.AppDetails.domainurl + "/v2auth/default/login");
var response = await http.PostAsync(url, credentials);
return response;
}
}
You also need to use await in your calling method, otherwise you get a Task back
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Try to change your code to await async method like this:
var responsefromhttplogin = await auth.dbhelpers.AuthHttp.loginAsync(login_username.Text, login_password.Password);
if (responsefromhttplogin.IsSuccessStatusCode)
{
//do after login stuff
}
Now after you add await this responsefromhttplogin.IsSuccessStatusCode should be accessible.
And also change your method to return Task<HttpResponseMessage> instead of Task<object>
You are returning Task<object> from loginAsync(), to access the result of the task you can use Task.Result.
I would consider to narrow down the type that you are returning from object to HttpResponseMessage.
Judging from your code, you are returning a Task<object>
You should either cast the return response; as return (HttpResponseMessage) response;
Or change the return type to be Task<HttpResponseMessage>
Relevant documentation
I'm creating an Instagram API client on ASP MVC using HttpClient, I'm trying to make a get request but it fails without throwing exception or responding and doesn't respond to my timeout. Here is my code:
public class InstagramService
{
private HttpClient Client = new HttpClient {
BaseAddress = new Uri("https://api.instagram.com/v1/"),
Timeout = TimeSpan.FromMilliseconds(500)
};
public async Task<InstagramUser> GetInstagramUser(long? userId = null)
{
InstagramUser User = null;
string Parameter = (userId == null) ? "self" : userId.ToString();
try {
var response = await Client.GetAsync("users/" + Parameter + "/" + GetAccessToken());
if (response.IsSuccessStatusCode)
{
User = await response.Content.ReadAsAsync<InstagramUser>();
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}
return User;
}
private string GetAccessToken()
{
return "?access_token=" + DB.config_det_sys.Single(i => i.codigo == "ACCESS_TOKEN_INSTAGRAM" && i.estado == true).Valor;
}
}
EDIT
Here I add how I call my service on the Home Controller, I will still test changing the controller to async Task
public class HomeController : Controller
{
private InstagramService IGService = new InstagramService();
public ActionResult About()
{
var apiCall = IGService.GetInstagramUser();
var model = apiCall.Result;
return View(model);
}
}
I tested on Postman trying to make the API call and it indeed worked, so where I'm failing to catch errors?
Your problem is here:
var model = apiCall.Result;
As I describe on my blog, you shouldn't block on asynchronous code. It can cause a deadlock.
Instead of Result, use await:
var model = await apiCall;
Adding to Stephen's answer, update the controller's action to be async all the way.
public class HomeController : Controller {
private InstagramService IGService = new InstagramService();
public async Task<ActionResult> About() {
var model = await IGService.GetInstagramUser();
return View(model);
}
}
I'm having a problem with an async method that I implemented. The method basically makes a HttpRequest to a resource and deserializes the string if the request is successful. I wrote a test for the method, and it works. But the method does never return when I call it from a controller?
public async Task<IEnumerable<T>> Get()
{
try
{
var resourceSegmentUri = new Uri(_uri, UriKind.Relative);
var response = await _client.GetAsync(resourceSegmentUri);
if (response.IsSuccessStatusCode)
{
var submission = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<T>>(submission);
}
if (response.Content != null)
{
var message = response.Content.ReadAsStringAsync();
throw new WebException(message.Result, (WebExceptionStatus)response.StatusCode);
}
}
catch (WebException e)
{
Logger.Error("GET Request failed with status: {0}", e.Status);
throw;
}
throw new Exception();
}
Code that never returns:
public ActionResult Index()
{
var api = new Api();
var test = api.Get().Result; //Never returns
return View();
}
Test that works:
[Test]
public void GetShouldReturnIfSuccessfulRequest()
{
var api = new Api();
var submission = api.Get();
Console.WriteLine(JsonConvert.SerializeObject(submission));
Assert.NotNull(submission);
}
Does anyone know the problem?
You've got a deadlock because you're calling .Result in your controller action.
If you use async/await then you have to use asynchronous actions too.
So something like this should fix it:
public async Task<ActionResult> Index()
{
var api = new Api();
var test = await api.Get(); // Should return
}
There's a comprehensive article about this here: Using Asynchronous Methods in ASP.NET MVC 4