I am working on a MVC project that submits a request via a third party.
In my controller, I have a SubmitClaims() action that receive ajax request and then calls RunAsync(). RunAsync submits a request by using HttpClient.
I am not sure if I did a right thing here.
Also I have two version of SubmitClaims(), both work. But I don't know which version is better.
version 1
[HttpPost]
public async Task<string> SubmitClaims()
{
string result = "";
result = await RunAsync();
return result;
}
version 2 learn from Cannot implicitly convert type 'string' to 'System.Threading.Tasks.Task<string>'
[HttpPost]
public async Task<string> SubmitClaims()
{
return await Task.Run(() =>
{
return RunAsync();
});
}
static async Task<string> RunAsync()
{
string result = "Failed.";
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://peter:8001/internal/uickpost");
client.DefaultRequestHeaders.Add("contenttype", "application/xml");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.role", "REQUEST");
client.DefaultRequestHeaders.Add("hiconline.protocol.content.transactionid", "asdfsdf");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.contenttype", "TestDataType");
client.DefaultRequestHeaders.Add("hiconline.protocol.remote.mode", "P");
client.DefaultRequestHeaders.Host = "peter:8001";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
string opv = "Test Data";
HttpContent _content = new StringContent(opv);
_content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
_content.Headers.Add("contenttype", "TestDataType");
HttpResponseMessage response1 = await client.PostAsync(client.BaseAddress, _content);
if (response1.IsSuccessStatusCode)
{
Uri gizmoUrl = response1.Headers.Location;
result = response1.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
}
Option 1 is better. RunAsync() already returns a task, so why create another one?
Even better would be return await RunAsync();. Even better would just be calling RunAsync directly, since the wrapper doesn't add anything.
Related
My Action method is returning HttpResponseMessage but, I want to get rid off Microsoft.AspNetCore.Mvc.WebApiCompatShim NuGet Package (which is basically provided to bridge the gap while porting Asp.Net Web API code into .Net Core) and use IActionResult/ActionResult instead of HttpResponseMessage.
My Action method looks like this:
[HttpGet]
[Route("GetTemplate")]
public async Task<HttpResponseMessage> GetTemplate(string id) {
var userAgent = this.Request.Headers.UserAgent;
bool IsWindows = true;
if(userAgent.ToString().ToLower().Contains("apple")) {
IsWindows = false; //false
}
var template = await _templateService.GetTemplateContent(id);
HttpResponseMessage responseMsg = new HttpResponseMessage();
if(IsWindows) {
responseMsg.Content = new StringContent(JsonConvert.SerializeObject(template));
responseMsg.RequestMessage = Request;
responseMsg.StatusCode = HttpStatusCode.OK;
responseMsg.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
} else {
responseMsg.Content = new ByteArrayContent(template.ContentBytes);
responseMsg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileNameStar = template.Name };
responseMsg.Content.Headers.Add("x-filename", template.Name);
responseMsg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
responseMsg.Content.Headers.ContentLength = template.ContentBytes.Length;
responseMsg.RequestMessage = Request;
responseMsg.StatusCode = HttpStatusCode.OK;
}
return (responseMsg);
}
Since you aren’t doing anything fancy there, you can translate your return object directly into corresponding action results here. In your case, you want a JsonResult and a FileResult with a custom response header:
[HttpGet]
[Route("GetTemplate")]
public async Task<HttpResponseMessage> GetTemplate(string id)
{
var userAgent = this.Request.Headers.UserAgent;
bool IsWindows = !userAgent.ToString().ToLower().Contains("apple");
var template = await _templateService.GetTemplateContent(id);
if (IsWindows)
{
return Json(template);
}
else
{
Response.Headers.Add("x-filename", template.Name);
return File(template.ContentBytes, "application/octet-stream", template.Name);
}
}
There are a lot similar utility methods on the Controller and ControllerBase type that help you create a variety of different response messages. For most use cases, there should be a built-in way to produce the response.
1stly change the signature of your action to this:
public async Task<IActionResult> GetTemplate
You will then return your data in the response something like this return Ok(data). You do not have to serialize your data, you can send a POCO class. This would represent .StatusCode = HttpStatusCode.OK
If you want to add extra headers to your response, you will do so using the Response field from ControllerBase. Eg. Response.Headers.Add for adding key value pairs to your Response header.
This is my register method inside the AuthController.
[HttpPost(ApiRoutes.Auth.Register)]
public async Task<IActionResult> Register(UserRegistrationRequest request)
{
var authResponse = await _authService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
return Ok(new AuthSuccessResponse
{
Token = authResponse.Token,
RefreshToken = authResponse.RefreshToken
});
}
I'm trying to call this method by using TestClient.PostAsync() method, unfortunately it always returns Bad Request. I've already tried calling the TestClient.PostAsJsonAsync(ApiRoutes.Auth.Register, user) method by importing Microsoft.AspNet.WebApi.Client package, the result is the same.
var user = new UserRegistrationRequest
{
Email = "user1#testtest.com",
Password = "P#ssw0rd1!!!!!"
};
var response = await TestClient.PostAsync(
ApiRoutes.Auth.Register,
new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8)
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
});
You are missing the FromBody attribute from you action parameter. When you are sending json data to a controller that will be part of the request body. You can tell to the controller how to bind the incoming data, in your case from the body. So you code should look like:
public async Task<IActionResult> Register([FromBody]UserRegistrationRequest request)
{
…
}
You could read more about bindings in the official documentation.
I encountered an issue using HttpClientFactory. I need to call two web methods from one third party web api.
getOrderNumber.
getShippingLabelFile.
Call #2 depends on #1's result since it needs to pass orderNumber to it e.g.:
await _client.getAsync("http://xxx/api/getLabel?orderNumber=[returnedOrderNumber]&fileType=1")
When I set break-point and debug, it works as expected. Without debugging mode, #2 web method always failed. I have done investigation. If I pass static query parameter like:
http://xxx/api/getLabel?orderNumber=123&fileType=1
it works fine. It seems #2 evaluates the query string and execute api call before orderNumber gives to it. It is very frustrating, can you please shed on some light on this issue?
On Controller:
private readonly ISite1AuthHttpClient _site1HttpClient;
public OrderShippingOrdersController(site1AuthHttpClient)
{
_site1HttpClient=site1AuthHttpClient
}
[HttpGet("{id}")]
public async Task<IActionResult> GetShippingLabel(int id)
{
string token=await _site1HttpClient.GetToken(username.ToString(),password);
string orderNumber=await _site1HttpClient.CreateOrder(Order,token);
if (orderNumber!=null && orderNumber!="")
{
//this API call always failed during runtime. It works on debugging mode.
var streamFile=(MemoryStream)(await _site1HttpClient.getShippingLabel(orderNumber,token));
}
}
HttpClient Type Class:
public interface ISite1HttpClient
{
Task<string> CreateOrder(AueCreateOrder order,string token);
Task<Stream> GetShippingLabel(string orderNumber,string token);
}
public class Site1HttpClient:ISite1HttpClient
{
private readonly HttpClient _client;
public Site1HttpClient(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("http://abcapi.Site1.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
_client = httpClient;
}
public async Task<string> CreateOrder(AbcCreateOrder order,string token)
{
var jsonInString=JsonConvert.SerializeObject(order);
jsonInString="[ " + jsonInString + " ]";
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
HttpResponseMessage response = await _client.PostAsync(
"api/AgentShipmentOrder/Create", new StringContent(jsonInString, Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
var contents = await response.Content.ReadAsStringAsync();
AbcOrderCreateResponse abcRes = JsonConvert.DeserializeObject<AbcOrderCreateResponse>(contents);
return abcRes.Message;
}
else
{
var errorResponse = await response.Content.ReadAsStringAsync();
throw new Exception(errorResponse);
}
}
public async Task<Stream> GetShippingLabel(string orderNumber,string token)
{
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
HttpResponseMessage response = await _client.GetAsync("api/GetOrderLabel?orderId="+orderNumber+"&fileType=1");
if (response.IsSuccessStatusCode)
{
Stream streamFile= await response.Content.ReadAsStreamAsync();
return streamFile;
}
else
{
throw new Exception("failed to get label.");
}
}
}
string token = _site1HttpClient.GetToken(username.ToString(),password);
string orderNumber = await _site1HttpClient.CreateOrder(Order,token);
I guess the problem occurs because of first await keyword. When you use await for the first function call (calling an async function), you declare that your program does not need to hold on for the response. So the token variable is used in the second function when it is not set. As you can see above, you should be good to go without the first await for the token variable.
I have written a code to get the projects from the TFS 2013 server using the tfs web api. As the methord is taking too long to execute i.e like 1.5 minutes, so i wanted to make this methord as async. So i added the async and task in the return type. When i see in visual studio is shows a message that the methord does not have await keyword. where should i add the await keyword. I am very new to async programming.
public override async Task<List<Project>> GetProjects()
{
List<Project> retunResult = new List<Project>();
using (var http = new HttpClient(GetHttpHandler()))
{
var response = http.GetAsync(_baseUrl + "_apis/projectCollections?" + tfsWebApiVersionSring).Result;
if (response.IsSuccessStatusCode)
{
response.EnsureSuccessStatusCode();
TFS2013TeamProjectCollection.Rootobject obj = JsonConvert.DeserializeObject<TFS2013TeamProjectCollection.Rootobject>(response.Content.ReadAsStringAsync().Result);
if (obj != null)
{
foreach (TFS2013TeamProjectCollection.Value projColl in obj.value)
{
if (projColl.state == "Started")
{
var responseProj = http.GetAsync(_baseUrl + projColl.name + "/_apis/projects?" + tfsWebApiVersionSring).Result;
if (responseProj.IsSuccessStatusCode)
{
responseProj.EnsureSuccessStatusCode();
TFS2013TeamProject.Rootobject obj1 = JsonConvert.DeserializeObject<TFS2013TeamProject.Rootobject>(responseProj.Content.ReadAsStringAsync().Result);
if (obj1 != null)
{
Project p;
foreach (TFS2013TeamProject.Value TeamProj in obj1.value)
{
p = new Project();
p.collectionName = TeamProj.collection.name;
p.description = TeamProj.description;
p.id = TeamProj.id;
p.name = TeamProj.name;
p.collectionName = TeamProj.collection.name;
p.url = TeamProj.url;
retunResult.Add(p);
}
}
}
}
}
return retunResult;
}
}
}
return null;
}
You could refer to this code snippet about using TFS REST API to get something in async way.
public static async void RESTAPIMethod()
{
HttpClientHandler authtHandler = new HttpClientHandler()
{
Credentials = CredentialCache.DefaultNetworkCredentials
};
using (HttpClient client = new HttpClient(authtHandler))
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = client.GetAsync(
"Put the REST API URL here").Result)
{
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
}
}
}
Another method about using TFS REST API:
You could also install this Nuget package for your project. Then using these assemblies in this package to run a REST API with C#. This is different with the method above, it's much more convenient. Here is an example about get a build information using Microsoft.TeamFoundation.Build.WebApi assembly in Async method.
Example:
What object returns from TFS2015 RestAPI
You shouldn't use Result at all. Every place in your code where you have:
var resp = http.GetAsync(url).Result;
you should have:
var resp = await http.GetAsync(url);
I'm getting a acknowledgement but no response message (details) i.e. list of notebooks from the OneNote API. Below is my code. I am able to receive the header and JSON details from a POST message but not the GET. I have tried to convert the POST code in order to submit a GET request.
private async void getRequestClick(object sender, RoutedEventArgs e)
{
await GetRequests(true, "test");
}
async public Task<StandardResponse> GetRequests(bool debug, string sectionName)
{
Uri PagesEndPoint1 = new Uri("https://www.onenote.com/api/v1.0/notebooks");
var client = new HttpClient();
//// Note: API only supports JSON return type.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//// This allows you to see what happens when an unauthenticated call is made.
if (IsAuthenticated)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authClient.Session.AccessToken);
}
HttpResponseMessage response;
HttpRequestMessage createMessage = new HttpRequestMessage(HttpMethod.Get, PagesEndPoint1);
response = await client.SendAsync(createMessage);
tbResponse.Text = response.ToString();
return await TranslateResponse(response);
}
private async static Task<StandardResponse> TranslateResponse(HttpResponseMessage response)
{
StandardResponse standardResponse;
if (response.StatusCode == HttpStatusCode.Created)
{
dynamic responseObject = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
standardResponse = new CreateSuccessResponse
{
StatusCode = response.StatusCode,
OneNoteClientUrl = responseObject.links.oneNoteClientUrl.href,
OneNoteWebUrl = responseObject.links.oneNoteWebUrl.href
};
}
else
{
standardResponse = new StandardErrorResponse
{
StatusCode = response.StatusCode,
Message = await response.Content.ReadAsStringAsync()
};
}
// Extract the correlation id. Apps should log this if they want to collcet the data to diagnose failures with Microsoft support
IEnumerable<string> correlationValues;
if (response.Headers.TryGetValues("X-CorrelationId", out correlationValues))
{
standardResponse.CorrelationId = correlationValues.FirstOrDefault();
}
return standardResponse;
}
My POST messages are working OK. I can create a new page etc.
I think you need to change the expected status code from HttpStatusCode.Created to HttpStatusCode.OK for Get requests, since they return a 200 and not a 201. Try doing that in your TranslateResponse method.