I am pretty new to Asp.Net Core and I managed to create a mvc project. In This project I have created an API and it is secured with token based authorization. I am trying to consume this api and make a post request to save data to database. To achieve this I have created one API controller and one MVC controller. These two controllers are used with different purposes. In order to consume the api I have to generate a JWT token and attach token to request header. I use MVC controller for that purpose and after attach authorization header, I consume API post endpoint by sending request from MVC controller to API controller. Here is the process.
I have a form to collect product data in view. Data is send to the MVC controller through ajax. Ajax coding part is successfully working and I can see all the data have passed to controller.
MVC Controller
[HttpPost]
public async Task<IActionResult> stockIn([FromBody] Products products)
{
var user = await _userManager.GetUserAsync(User);
var token = getToken(user);
var json = JsonConvert.SerializeObject(products);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpClient = _clientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Post,
"https://localhost:7015/api/stocks/stockIn/");
request.Headers.Add("Authorization", "Bearer " + token);
request.Content = content;
HttpResponseMessage response = await httpClient.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var apiData = await response.Content.ReadAsStringAsync();
return Ok(apiData);
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
This code(MVC controller) also works fine, when I debug this just before the request is sent, I can see token and content also have generated and request is attached with them. Request method is also set to POST.
Then I put a breakpoint on API controller and once the request is sent, the Request Uri - Api endpoint is hiiting and I can see that request method has become GET and the content become Null
API Controller
[HttpPost]
[Route("StockIn")]
public async Task<IActionResult> StockAdd(HttpRequestMessage httpRequestMessage)
{
var content = httpRequestMessage.Content;
string jsonContent = content.ReadAsStringAsync().Result;
Products products = new Products();
products = JsonConvert.DeserializeObject<Products>(jsonContent);
await _context.StoresProducts.AddAsync(products);
await _context.SaveChangesAsync();
return Ok(new { success = "Stock updated successfully" });
}
When I am hovering over the received httpRequestMessage on API controller :
When I am debuging line by line API controller, A null exception is thrown When request message content access.
I found that there are many posts regarding this issue. I have tried almost every solution mentioned on them.
Tried fixes: None of them work
var httpClient = _clientFactory.CreateClient();
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
I also tried changing request Url by adding '/' to end of it, does not work either. Some of the posts has guessed that there must be a redirection but I can not find a redirection also. I think sometime this caused because I am calling the api endpoint via MVC controller action. Since I want to attach token to request header before api calling, I can not find a way to call api endpoint directly without MVC controller action. Please help me to find the issue here or show me how to achieve this task correctly. Thank you.
Any particula reason you're expecting the body to bind to the HttpRequestMessage?
Have you tried changing the post action to bind to the correct object?
[HttpPost]
[Route("StockIn")]
public async Task<IActionResult> StockAdd(IEnumerable<Products> products)
{
await _context.StoresProducts.AddAsync(products);
await _context.SaveChangesAsync();
return Ok(new { success = "Stock updated successfully" });
}
Most likely, this problem is related to redirect.
When you send request with some method and method changes after sending request, it will be about redirect.
Redirect occures in diffrent scenario:
Wrong api address
Authentication needed
As you said, sometimes url's that does not end with '/' cause to redirect
So send request with postman and check postman log (bottom-left) for better detail.
check if Redirect occoured or not,
if redirect occured, so check for reason. also check if WWW_Authenticate exist in headers.
Related
I can obtain the details of my http request including headers via Fiddler, however I'd like to display the value of my http request and response as part of my MVC web service to ensure the request is valid.
I want to return all of the details of the request as a string so I can return the result to the View for this specific controller.
I am using C# to create the request:
var _httpClientManager = new HttpClientManager(_httpClientPool, _Identity, _errorLogger);
_httpClientManager.HttpRequestHeaders = new Dictionary<string, string>();
_httpClientManager.HttpRequestHeaders.Add("Accept", "application/json");
... extra headers omitted.....
... url and request JSON is omitted
var response = await _httpClientManager.PostAsJsonAsync<IdentityVerificationRequest>(url, request, null);
return View(response);
Basically I'd like to obtain what I am about to Post to the server and include that in the View response.
Its probably a bit of a naïve question, I am quite new working with this.
Is it possible?
Thankyou.
I am trying to implement a Xamarin app that works with the Asana API.
I have successfully implemented the OAuth as documented in the Asana documentation here... at least I assume it is successful. I get an access token from the token endpoint in an HTTPResponse with HTTP Status "OK".
But then when I turn around and try to make an API call with that same access token, I get a 403 Forbidden error. I tried the same API call in my browser (after logging in to Asana), and it works fine, which leads me to believe that I do have access to the resource, I must have an issue with authorizing the request on my end.
The API call in question is (documented here): https://app.asana.com/api/1.0/workspaces.
My C# code is as follows (abbreviated to relevant parts, and assume that ACCESS_TOKEN contains the access token I got from the token exchange endpoint):
HttpClient client = new HttpClient();
client.BaseAddress = "https://app.asana.com/api/1.0";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", ACCESS_TOKEN);
client.DefaultRequestHeaders.Add("Accept", "application/json");
And then I use this HttpClient (named client) in the following function:
// Returns a list of the Asana workspace names for the logged in user.
private async Task<List<string>> GetWorkspacesAsync()
{
List<string> namesList = new List<string>();
// Send the HTTP Request and get a response.
this.UpdateToken(); // Refreshes the token if needed using the refresh token.
using (HttpResponseMessage response = await client.GetAsync("/workspaces"))
{
// Handle a bad (not ok) response.
if (response.StatusCode != HttpStatusCode.OK)
{
// !!!THIS KEEPS TRIGGERING WITH response.StatusCode AS 403 Forbidden!!!
// Set up a stream reader to read the response.
// This is for TESTING ONLY
using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
// Extract the json object from the response.
string content = reader.ReadToEnd();
Debug.WriteLine(content);
}
throw new HttpRequestException("Bad HTTP Response was returned.");
}
// If execution reaches this point, the Http Response returned with code OK.
// Set up a stream reader to read the response.
using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
{
// Extract the json object from the response.
string content = reader.ReadToEnd();
JsonValue responseJson = JsonValue.Parse(content);
foreach (JsonValue workspaceJson in responseJson["data"])
{
string workspaceName = workspaceJson["name"];
Debug.WriteLine("Workspace Name: " + workspaceName);
namesList.Add(workspaceName);
}
}
}
// I have other awaited interactions with app storage in here, hence the need for the function to be async.
return namesList;
}
Finally found the answer. It looks like I was using HttpClient incorrectly; a subtle thing that should be equivalent, but is not due to the way it is implemented.
The answer
I needed to place the final slash at the end of the BaseAddress property of HttpClient, and NOT at the start of the relative address for the specific request. This answered question explains this.
To fix my code
I needed to change the setting up of the BaseAddress:
HttpClient client = new HttpClient();
client.BaseAddress = "https://app.asana.com/api/1.0/"; // FINAL SLASH NEEDED HERE
And remove the slash from the request's relative address:
// DO NOT put slash before relative address "workspaces" here
using (HttpResponseMessage response = await client.GetAsync("workspaces"))
Why I got the original error
When HttpClient combined the BaseAddress with the relative URI I specified in GetAsync(), it dropped off some of the base address, since the final slash was not included. The resulting address from combining the BaseAddress with the relative URI was a valid URL, but not a valid page/API call in Asana. Asana thus did an automatic redirect to a login page, which, of course, the rest of the API call would be forbidden from there.
How I discovered this
In debugging, I grabbed the access token returned during my app's authorization with Asana. I then recreated the request to the "/workspaces" API myself in Postman, and the request worked as expected. This confirmed that my authorization worked fine, and the issue must be with the specific request rather than the authorization. In debugging I then looked into the HttpResponseMessage, which has a property called RequestMessage, that includes the actual URL the GetAsync() made the request against. I observed the Login URL from Asana, rather than the BaseAddress I specified... which led me to the question/
answer linked above.
Hope this explanation helps anyone who comes across a similar error!
I have a ASP.NET Web API, and I have been responding to request with this format,
[HttpPost]
[Route("")]
public HttpResponseMessage AlexaSkill()
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent("put json here", Encoding.UTF8);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return response;
}
and that has been working great. The issue is that there are certain situation where the requester does not expect a response. I cannot figure out how to not give a response to the requester who is posting to the url. How can I be able to return a response like a have above and also have the option to have the function not give a respons essentially acting as a void function?
You should always return a response. There's a status code 204 for when you don't want to send content in your response. From the spec:
10.2.5 204 No Content
The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.
If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.
The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
So your code could be something like this:
[HttpPost]
public HttpResponseMessage SomeMethod()
{
// Do things
return Request.CreateResponse(HttpStatusCode.NoContent);
}
Even a void method will return an HTTP status code to the client invoking the API. See this link
You'll probably need to ask for changes or another alternative to your client.
If you want to just terminate the request, try this:
HttpContext.Current.Response.End();
throw new Exception("Terminating request.");
It seems like a strange thing for an HTTP server to do, but if that's what you really need, give that a shot. If you follow by throwing an exception, then an error won't be sent to the client because you've already ended the response.
What I'm trying to do:
I'm trying to practise making HTTP calls (...if that is what it's called) from a simple ASP.NET MVC web application. To do this, I am attempting to get weather details from OpenWeatherMap. You can do this by:
Add the following parameter to the GET request: APPID=APIKEY
Example: api.openweathermap.org/data/2.5/forecast/city?id=524901&APPID=1111111111
My understanding, from my learning:
The controller is the one to make the above HTTP call.
My question:
How do I actually make that HTTP GET request, in ASP.NET MVC?
Use System.Net.Http.HttpClient.
You can do some basic reading from a website using something like the following:
using (var client = new HttpClient())
{
var uri = new Uri("http://www.google.com/");
var response = await client.GetAsync(uri);
string textResult = await response.Content.ReadAsStringAsync();
}
You may want to make sure to test response.IsSuccessStatusCode (checks for an HTTP 200 result) to make sure the result is what you expect before you parse it.
I have been banging my head against the wall for the past 1 week now but without any success. Actually I'm writing a C# code(a web api controller action) to call another web api to make a post request with some json data payload in the request body. Syntax-wise there is nothing wrong with the code. But when I directly call the service(web api service) from web browser I get an Html form that has a multiline text box in it, rollback property (as radio button for true and false value for this property), drop down box with 2 options such as html and json (to get response in either format) and a button(for sending request to the server and making edits in the database). Now when I manually put json data inside text box and click the button on that html form edits are done successfully in the database but when programmatically(from my C# code) I send the same json data payload and make a post request edits are never done successfully rather I get an html response body through Fiddler that says status code success 200 but unable to complete operation,some parameters couldn't be recognized.
Here is my code
private static async Task<HttpResponseMessage> GeometryUpdateAsync(Feature updatedFeature, FeatureType featureType, int? objectid = null)
{
var jsonObject = new JObject();
dynamic esriId = jsonObject;
if (objectid == null)
{
objectid = updatedFeature.OBJECTID;
}
esriId.OBJECTID = objectid;
var mergedJsonString = JsonConvert.SerializeObject(new
{
geometry = JObject.Parse(updatedFeature.Geometry.ToString()),
attributes = JObject.Parse(esriId.ToString())
});
mergedJsonString = String.Format("[{0}]", mergedJsonString);
HttpResponseMessage response = null;
using (var client = new HttpClient())
{
//string arguments = "rollbackOnFailure=true&f=pjson&features=";
client.BaseAddress = new Uri("somebaseaddress");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(500.00);
//response = await client.PostAsJsonAsync("someuri", arguments + mergedJsonString);
response = await client.PostAsync("someuri", mergedJsonString, new System.Net.Http.Formatting.JsonMediaTypeFormatter());
if (response.IsSuccessStatusCode)
{
var v = response.Content.ReadAsStringAsync().Result;
}
}
}
When I look at the request body (through fiddler while making a post request through Html form) request body looks like
features=%5B%7B%22geometry%22%3A%7B%22paths%22%3A%5B%5B%5B-91.3888577181506%2C39.703158271352621%5D%91.381838690201192%2C39.690323806398723%5D%2C%5B-91.383241723424632%2C39.689645139311914%5D%2C%5B-91.3849700567206%2C39.6888078408094%5D%2C%5B-91.3861256828518%2C39.688248198995353%5D%5D%5D%7D%2C%22attributes%22%3A%7B%22OBJECTID%22%3A21%7D%5D&gdbVersion=&rollbackOnFailure=true&f=pjson
and the request body for the post request made programmatically looks likes
"[{\"geometry\":{\"paths\":[[[-91.3888577181506,39.703158271352621],[-91.381838690201192,39.690323806398723],[-91.383241723424632,39.689645139311914],[-91.3849700567206,39.6888078408094],[-91.3861256828518,39.688248198995353]]]},\"attributes\":{\"OBJECTID\":21}}]"
Even I tried appending this
string arguments = "rollbackOnFailure=true&f=pjson&features=";
in my commented out code above (where I'm using PostAsJsonAsync) to make the request body look like as if it's coming from Html form. But no success, even I'm not sure whether the JSonFormatter takes this arguments string in to account or just leaves it while serializing/deserializing during the run time. And the post request body that I get after appending "arguments" string to Json string looks like this
"rollbackOnFailure=true&f=pjson&features=[{\"geometry\":{\"paths\":[[[-91.3877577181506,39.703158271352621],[-91.36047320856953,39.702616420911333],[-91.383241723424632,39.689645139311914],[-91.3849700567206,39.6888078408094],[-91.3861256828518,39.688248198995353]]]},\"attributes\":{\"OBJECTID\":21}}]"
But still no success, Now I'm totally running out of ideas as to how to call web api service from my C# code so that web api thinks it's coming from that Html form and end up successfully doing edits in the database programmatically. All suggestions and ideas will be highly appreciated.
The trick lies somewhere else, I was using HttpClient to simulate browser post request and get result in c#. But in this particular scenario HttpClient is of no use. I changed to HttpWebRequest after seeing a code at How to make a post call to a Web Api Action? from utlimate_programmer_BR and it did the trick, again HttpClient was a bad choice by me to get this particular thing done.