ASP.NET Web API Conditionally return HttpResponseMessage - c#

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.

Related

HttpRequestMessage POST request become GET - Asp.Net Core

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.

How to handle a WEB API that has different responses types for success and failure?

I'm working on a WEB API application made with .NET Framework. Right now i'm working alonside a supplier to help and integrate their WEB API with our system. One of their latest change broke my code. Since i'm using RestSharp library i'm expecting the response to always correspond to a specific type of object, independently if with was a success or failure from their side.
IRestResponse<T> response = await restClient.ExecuteAsync<T>(request);
If the response is successful they will send a json response like this
{message:"", jobNr:"2312312", status:"1"}
And then they changed the code, so that if any request input was wrong they will send a list of errors, so the response will be like this:
[{code:100, message:"contact phone is wrong"},{code:101, message:"the email is not provided"}]
I don't like this approach, since i think they should give a response for errors like this:
{errorMessages:[{code:100, message:"contact phone is wrong"},{code:101, message:"the email is not provided"}]}
But since it is not in my hands. How should i deal with it? Just parse the json response and assing to different types of objects? Thanks.
Yes, the response should be parsed manually.
Assuming RestSharp is still in charge and the original API returns errors entitled with BadRequest status code, below extension tries to deserialize the content as an array of errors (a type consists of a code and a message just like mentioned in question).
public static Error[] AsErrors(this IRestResponse response)
{
var empty = new Error[] { };
try
{
return response.StatusCode == System.Net.HttpStatusCode.BadRequest
? JsonSerializer.Deserialize<Error[]>(response.Content)
: empty;
}
catch (JsonException)
{
return empty;
}
}
Considering the NormalContent is what you truly expect as response, the usage will be:
// after creating client and request ...
var response = client.Execute<NormalContent>(request);
var errors = response.AsErrors();
if (errors.Any())
{
//Deal with errors
}
else
{
//response.Data gets populated with RealResult
}
NOTE: Using the procedure as an extension depends on how common is the response pattern in your case.

REST api responses

Given this code, the entry for a C# REST API,
protected HttpResponseMessage ProcessRequest()
{
HttpResponseMessage _response = null;
try
{
//request is processed.
object _obj = ExecuteEvent();
//response object is created with success status and response content is assigned in the required mediatype format.
_response = Request.CreateResponse(HttpStatusCode.OK, Constants.ServiceConstants.VALUE);
_response.Content = new StringContent(System.Web.Helpers.Json.Encode(_obj), Encoding.UTF8, Constants.ServiceConstants.MEDIATYPE);
}
catch
{
}
return _response;
}
Returning a 200 response for every request and just setting the content to the serialized JSON as above, is this the right or wrong way to go about this?
I would have thought that we definitely should not just be returning a 200 OK for every request.
This code will throw an exception in ExecuteEvent, let's say if the user is unauthorized, however, shouldn't we actually be telling the caller that their request is unauthorized?
You should send back an HTTP status based ok what happened in your method. If everything worked correctly, you send back an OK. If there is a failure, you should send back the appropriate status code.
For example, if ExecuteEvent throws a message indicating that the user is not authorized, you would send back an HTTP stays code of Forbidden (a 403).
You can see all of Microsft's status codes here and you can get a list of the numbers and what they mean here. Most of the time these two lists with correspond to each other, they are just organized a little differently.
Hope that helps.

Calling two HttpStatusCode.Created at the same line in asp.net Web API

In my POST method in a Controller.cs, I have been writing only a single value into the database so far, so at the end I returned the status of the response with the following code:
var response = Request.CreateResponse<object>(HttpStatusCode.Created, iconOffset);
return response;
But now, I am writing two values into the database, and I would love to return the status of both at the end of the POST method, how do I do that? I tried the following:
var response = Request.CreateResponse<object>(HttpStatusCode.Created, HttpStatusCode.Created, iconOffset, tempOffset);
return response;
But didn't work.
You wouldn't return multiple http status codes to the browser.
You make a request for something, you get a response back - that response was either OK or another status code, there's no concept of multiple response codes.
If you need to elaborate with your response, return a model along with your status for whatever is consuming the endpoint, one example would be:
return Request.CreateResponse(HttpStatusCode.OK, new DatabaseUpdateResult(results));
If your application could accept/return json, then this could be consumed by the client.
You can't do that. The response code is the HTTP response code and there is only one. You could either split out the method into two endpoints or return a status that represents the state of the operation.
Your client should have no knowledge of what is going on behind the service (database, services, ...) so if you're posting something to that endpoint and semantically it gets created just return Created. If an error occurs you could return InternalServerError.
Here's a list of HTTP status codes you can use: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

how to conceptually make a post request to web api from c# program as if the it's coming from html form

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.

Categories

Resources