Converted POST FromBody to HttpRequestMessage - Need to Desearialize Body of Request - c#

I have working REST code that accepts POST messages using FromBody. The parameter received is the object I need to work with. Now, I need to examine the authorization in the header. I think have this figured out by switching the parameter from the object to an HttpRequestMessage. Of course, now the content of the message must be converted to the original object and I'm having difficulty figuring it out.
Here is the original method:
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus)
{
try
{
if (cardStatus == null)
{
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card")
{
//... Process;
}
}
catch (Exception ex)
{
try
{
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
New Code, using HttpRequestMessage:
[HttpPost]
public IHttpActionResult Post(HttpRequestMessage request)
{
// Get the authentication from the header
var encoding = Encoding.GetEncoding("UTF-8");
var authValue = encoding.GetString(Convert.FromBase64String(request.Headers.Authorization.Parameter));
var validAuthorization = ConfigurationManager.AppSettings["ValidKey"];
if (authValue != validAuthorization)
{
return BadRequest("Not Authorized");
}
// This does NOT compile - Need help converting request.Content to a CardStatusRoot object
CardStatusRoot cardStatus = (CardStatusRoot)request.Content.ReadAsStringAsync().Result;
... Same as first method
}
How do I convert the content of the request to a CardStatusRoot object?

ApiController has access to the current request via the Request property.
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus) {
try {
HttpRequestMessage request = this.Request;
if (cardStatus == null) {
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card") {
//... Process;
}
} catch (Exception ex) {
try {
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
That said, this question feels more like an XY problem.
You should be looking into
Authentication Filters in ASP.NET Web API 2
Global Error Handling in ASP.NET Web API 2

Just use the previous signature method to compute with data. You can directly access headers value like this
Request.Headers.Authorization.Parameter
Where Request is the object provided in ApiController for each request.

Related

How to Decrypt the payload on server during authorization and map to model in web api

I've been trying to follow this answer trying to decrypt the Encrypted payload during the Authorization before it gets model mapped to the controller.
From the client only the Payload will be encrypted and on the server side I'm trying to decrypt. Thing is the entire Response.content cannot be decrypted as only the payload needs to decrypted.
Inside the content we're receiving the payload in Result and when I'm trying to change that it is showing that it is read only and I couldn't see any other options. In the image above the result is not encrypted yet, I was testing to see if we can change that.
I've done it in another way where I'll be passing the entire encrypted string to the controller and then decrypting it and mapping to model inside the controller like this:
[Route("api/xxxxxx")]
[HttpPost]
public HttpResponseMessage PostTest(string encryptedValue)
{
//creating an instance of class
HttpResponseMessage response = new HttpResponseMessage();
try
{
string decryptJson = AES.DecryptString(encryptedValue);
Model list = JsonConvert.DeserializeObject<Model>(decryptJson);
//rest of the operation
}
//to catch exceptions if any
catch (Exception ex)
{
output.Success = false;
output.Message = Literals.GetErrorMessage(ex.Message);
}
//creating response
response = Request.CreateResponse(HttpStatusCode.OK, JObject.FromObject(output));
//returning response
return response;
}
This is working as expected but I'm trying if at all it's possible to do this at Authorization instead of doing it individually to every controller.
Any advice is appreciated.
Using new StringContent() to add the decrypted string to the Response.Content:
public class LogAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
try
{
var resoponseContent = actionContext.Request.Content.ReadAsStringAsync();
var result = resoponseContent.Result;
var decryptedString = AESEncryptDecrypt.DecryptStringAES(result);
actionContext.Request.Content = new StringContent(decryptedString, Encoding.UTF8, "application/json");
var checkingDecryptedResponseContent = actionContext.Request.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
}
}
}
After updating the new content the Model will be auto mapped to the controller.
[LogAttribute]
[Route("api/xxxxxx")]
[HttpPost]
public HttpResponseMessage PostTest(Model data)
{
//creating an instance of class
HttpResponseMessage response = new HttpResponseMessage();
try
{
//rest of the operation
}
//to catch exceptions if any
catch (Exception ex)
{
output.Success = false;
output.Message = Literals.GetErrorMessage(ex.Message);
}
//creating response
response = Request.CreateResponse(HttpStatusCode.OK, JObject.FromObject(output));
//returning response
return response;
}

The WebAPI not working when sending data as json

I'm just started learn WebAPI,
When i'm trying to call my Api/TessterFunction and send data parameter as JSON ( {"Test":"TEST"} ) i got this response "No HTTP resource was found that matches the request",
But when trying to call it and send the data as query string (http ://localhost/myProject/myApi/TesterFunction?Test="TEST") it's work and Get Done.
[HttpPost]
[Route("TesterFunction")]
public HttpResponseMessage TesterFunction(string Test)
{
try
{
myClass myObject= new myClass();
if (myObject.myStordProcedure(CompanyCode))
{
return Request.CreateResponse(HttpStatusCode.OK, "Done");
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "SP not executed");
}
}
catch(Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}
That won't work because your web api method only accepts string parameter.
What you could do is;
Add a class where the properties will be bound
public class ReceiveModel{
public string Test {get;set;}
}
Then replace your web api method to use ReceiveModel parameter.
[HttpPost]
[Route("TesterFunction")]
public HttpResponseMessage TesterFunction(ReceiveModel model)
{
// see the property here
Console.WriteLine(model.Test);
try
{
myClass myObject= new myClass();
if (myObject.myStordProcedure(CompanyCode))
{
return Request.CreateResponse(HttpStatusCode.OK, "Done");
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "SP not executed");
}
}
catch(Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}

Sending Proper Response from web api

I am trying to send the proper response from Web API i.e. If any error send error else send the content. The code flow for the same is as follow.
[HttpPost]
public IActionResult GetInfo([FromBody] InfoModel info)
{
try
{
var result = new Info().ProcessInfoResponse(info);
if (result==null)
return BadRequest();
else
return Ok(result);
}
catch (Exception e)
{
Log.Error("some exception", e);
return StatusCode(500, e.Message);
}
}
and from middle layer i.e. from Info class we are having different method with there own returning type and from here we are calling the third party APIs which are in another class.
public InfoResponse ProcessInfoResponse(InfoModel info)
{
try
{
var result = serviceLayer.Post<InfoModel>(info);
if (result != null)
{
// Do Something
}
else
{
Log.Error("some error");
return null;
}
}
catch (Exception ex)
{
Log.Error("some error");
return null;
}
}
public InfoRequest ProcessInfoRequest()
{
}
And in service layer we are calling the third party api like below
public HttpResponseMessage Post<T>(T parm) where T : class
{
try
{
_client.DefaultRequestHeaders.Clear();
var postTask = _client.PostAsync("some third party url", Serialize<T>(parm));
postTask.Wait();
if (postTask.Result.IsSuccessStatusCode)
{
return postTask.Result;
}
else
{
Log.Error("some error in service layer");
}
}
catch (Exception ex)
{
Log.Error("some error in service layer");
}
return default(HttpResponseMessage);
}
So my question is how can return exceptions/errors if there are any and if there is no exceptions/error then send the response as it is. This is possible by keeping middle layer returning type as is
Right now if there are no errors then I am able to send the response properly, as my middle layer is getting the expected returning type.
The issue is my middle layer methods has own returning type which is causing me to send the exception/error as is. Because I am not able to map it to proper class OR same class.
I was thinking will add new Property under all returning classes/types which will refer to the exception class, then will bind the exception/error details to that class. This will save doing lot of code changes in all places.
Any help on this appreciated !
Why not create a custom response object so that:
public IActionResult<MyCustomResponseObject> GetInfo([FromBody] InfoModel info)
public class MyCustomResponseObject
{
public string Message { get; set; }
public object Content { get; set; }
public enum State { get; set; }
}

Returning multiple values from WebApi (post) to AngularJs

I am using .net core C#, WebApi & AngularJs.
For saving data my Angularjs code makes a $http call to my WebApi. I can return single data from my api fine but not sure whats the best method to return multiple values here. I can make it comma separated and then return as well, but wanted to know if there is a better approach to this.
So basically when the API saves data to my db, I want to return a variable, boolean value if the save was successful and an exception message in case the save was not successfully. Below is my code.
AngularJs Code:
service.saveData(data).then(function (res) {
//get someDataToReturn, dataSaved & exception raised if any from db save here.
}, function (err) {
});
WebApi Code:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
bool dataSaved = true;
string someDataToReturn = string.Empty;
//do some processing and updating someDataToReturn here
//Saving data to DB
dataSaved = SaveData(data);
//I want to return someDataToReturn, dataSaved(true or false) and exception raised from SaveData if any
return Ok(someDataToReturn);
}
//DB Call to save data
public bool SaveData(List<UserData> data)
{
try
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
return true;
}
catch (SqlException ex)
{
}
return false;
}
Let me know the best approach for this.
First of, you should check if the values in your request body is correctly populated.
Take a look at DataAnnotations.
You can use annotations to specify which properties in your model that are Required, Min and Maxlength etc.
Here's an example on how to define a Name property to be required on the UserData class
public class UserData
{
[Required]
public string Name { get; set; }
}
If the request model do not fulfill the specified rules set on the UserData class DataAnnotations, the context ModelState will be set to false and contain the DataAnnotations errors.
This can be used to determind if the current request is a bad request and return a proper http status code from that.
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //will return a 400 code
...
Then regarding the SaveData method. Capture the exception in the controller and return a proper status code from there
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //400 status code
try
{
SaveData(data);
}
catch(Exception e)
{
return InternalServerError(e); //500 status code
}
string someDataToReturn = string.Empty;
return Ok(someDataToReturn ); //200 status code
}
public void SaveData(List<UserData> data)
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
}
You can use the Controller class method Json(object data). Something like:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
return this.Json(SaveData(data));
}
See this.
you can create an entity and return it
public class BaseResult{
public bool Result{get;set;}
public string Errors{get;set;}
}
or only
return Ok( new { result = dataSaved , error= exception.Message});
the standard way is:
return 201 status code
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
[HttpPost("data/save")]
public async Task<IHttpActionResult> SaveData([FromBody] List<UserData> data)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// return response of 201 if you created the resource successfully
// typically return this with a uri to the new resource
return Created("location", saveData(data));
}
catch (Exception)
{
return InternalServerError();
}
}

Returning http status code from Web Api controller

I'm trying to return a status code of 304 not modified for a GET method in a web api controller.
The only way I succeeded was something like this:
public class TryController : ApiController
{
public User GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
throw new HttpResponseException(HttpStatusCode.NotModified);
}
return user;
}
}
The problem here is that it's not an exception, It's just not modified so the client cache is OK.
I also want the return type to be a User (as all the web api examples shows with GET) not return HttpResponseMessage or something like this.
I did not know the answer so asked the ASP.NET team here.
So the trick is to change the signature to HttpResponseMessage and use Request.CreateResponse.
[ResponseType(typeof(User))]
public HttpResponseMessage GetUser(HttpRequestMessage request, int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return new HttpResponseMessage(HttpStatusCode.NotModified);
}
return request.CreateResponse(HttpStatusCode.OK, user);
}
You can also do the following if you want to preserve the action signature as returning User:
public User GetUser(int userId, DateTime lastModifiedAtClient)
If you want to return something other than 200 then you throw an HttpResponseException in your action and pass in the HttpResponseMessage you want to send to the client.
Change the GetXxx API method to return HttpResponseMessage and then return a typed version for the full response and the untyped version for the NotModified response.
public HttpResponseMessage GetComputingDevice(string id)
{
ComputingDevice computingDevice =
_db.Devices.OfType<ComputingDevice>()
.SingleOrDefault(c => c.AssetId == id);
if (computingDevice == null)
{
return this.Request.CreateResponse(HttpStatusCode.NotFound);
}
if (this.Request.ClientHasStaleData(computingDevice.ModifiedDate))
{
return this.Request.CreateResponse<ComputingDevice>(
HttpStatusCode.OK, computingDevice);
}
else
{
return this.Request.CreateResponse(HttpStatusCode.NotModified);
}
}
*The ClientHasStale data is my extension for checking ETag and IfModifiedSince headers.
The MVC framework should still serialize and return your object.
NOTE
I think the generic version is being removed in some future version of the Web API.
In MVC 5, things got easier:
return new StatusCodeResult(HttpStatusCode.NotModified, this);
For ASP.NET Web Api 2, this post from MS suggests to change the method's return type to IHttpActionResult. You can then return a built in IHttpActionResult implementation like Ok, BadRequest, etc (see here) or return your own implementation.
For your code, it could be done like:
public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return StatusCode(HttpStatusCode.NotModified);
}
return Ok(user);
}
I hate bumping old articles but this is the first result for this in google search and I had a heck of a time with this problem (even with the support of you guys). So here goes nothing...
Hopefully my solution will help those that also was confused.
namespace MyApplication.WebAPI.Controllers
{
public class BaseController : ApiController
{
public T SendResponse<T>(T response, HttpStatusCode statusCode = HttpStatusCode.OK)
{
if (statusCode != HttpStatusCode.OK)
{
// leave it up to microsoft to make this way more complicated than it needs to be
// seriously i used to be able to just set the status and leave it at that but nooo... now
// i need to throw an exception
var badResponse =
new HttpResponseMessage(statusCode)
{
Content = new StringContent(JsonConvert.SerializeObject(response), Encoding.UTF8, "application/json")
};
throw new HttpResponseException(badResponse);
}
return response;
}
}
}
and then just inherit from the BaseController
[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...
and then using it
[HttpGet]
[Route("device/search/{property}/{value}")]
public SearchForDeviceResponse SearchForDevice(string property, string value)
{
//todo: limit search property here?
var response = new SearchForDeviceResponse();
var results = _deviceManagementBusiness.SearchForDevices(property, value);
response.Success = true;
response.Data = results;
var statusCode = results == null || !results.Any() ? HttpStatusCode.NoContent : HttpStatusCode.OK;
return SendResponse(response, statusCode);
}
.net core 2.2 returning 304 status code. This is using an ApiController.
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304);
}
Optionally you can return an object with the response
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304, YOUROBJECT);
}
I don't like having to change my signature to use the HttpCreateResponse type, so I came up with a little bit of an extended solution to hide that.
public class HttpActionResult : IHttpActionResult
{
public HttpActionResult(HttpRequestMessage request) : this(request, HttpStatusCode.OK)
{
}
public HttpActionResult(HttpRequestMessage request, HttpStatusCode code) : this(request, code, null)
{
}
public HttpActionResult(HttpRequestMessage request, HttpStatusCode code, object result)
{
Request = request;
Code = code;
Result = result;
}
public HttpRequestMessage Request { get; }
public HttpStatusCode Code { get; }
public object Result { get; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Request.CreateResponse(Code, Result));
}
}
You can then add a method to your ApiController (or better your base controller) like this:
protected IHttpActionResult CustomResult(HttpStatusCode code, object data)
{
// Request here is the property on the controller.
return new HttpActionResult(Request, code, data);
}
Then you can return it just like any of the built in methods:
[HttpPost]
public IHttpActionResult Post(Model model)
{
return model.Id == 1 ?
Ok() :
CustomResult(HttpStatusCode.NotAcceptable, new {
data = model,
error = "The ID needs to be 1."
});
}
Try this :
return new ContentResult() {
StatusCode = 404,
Content = "Not found"
};
If you need to return an IHttpActionResult and want to return the error code plus a message, use:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
Another option:
return new NotModified();
public class NotModified : IHttpActionResult
{
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotModified);
return Task.FromResult(response);
}
}
public HttpResponseMessage Post(Article article)
{
HttpResponseMessage response = Request.CreateResponse<Article>(HttpStatusCode.Created, article);
string uriToTheCreatedItem = Url.Route(null, new { id = article.Id });
response.Headers.Location = new Uri(Request.RequestUri, uriToTheCreatedItem);
return response;
}
An update to #Aliostads answer using the more moden IHttpActionResult introduced in Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results#ihttpactionresult
public class TryController : ApiController
{
public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return StatusCode(HttpStatusCode.NotModified);
// If you would like to return a Http Status code with any object instead:
// return Content(HttpStatusCode.InternalServerError, "My Message");
}
return Ok(user);
}
}
I know there are several good answers here but this is what I needed so I figured I'd add this code in case anyone else needs to return whatever status code and response body they wanted in 4.7.x with webAPI.
public class DuplicateResponseResult<TResponse> : IHttpActionResult
{
private TResponse _response;
private HttpStatusCode _statusCode;
private HttpRequestMessage _httpRequestMessage;
public DuplicateResponseResult(HttpRequestMessage httpRequestMessage, TResponse response, HttpStatusCode statusCode)
{
_httpRequestMessage = httpRequestMessage;
_response = response;
_statusCode = statusCode;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(_statusCode);
return Task.FromResult(_httpRequestMessage.CreateResponse(_statusCode, _response));
}
}

Categories