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);
}
}
Related
I have a .NET appplication where there is a controller for receiving user requests, a service Service 1 which calls another service Service 2.
I have some code in the Service 2 where I query the database(DynamoDB) and get a 500 error in response when the user request values are incorrect. I want to handle this such that I catch this error/exception and send back the error message along with a 400 status code from the controller to the user. How should I modify the code to do this?
This is what I have tried. Currently, I'm just printing the error in Service 1 but I need to send it to the controller. Is sending the error message to the controller by throwing exceptions along the way the right way to do it?
The below code is similar to the actual code
Controller:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Service 1:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
Console.WriteLine(e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
Service 2:
private async Task<DynamoResult<TResponse>> GetPagedQueryResults(DynamoQuery query)
{
var results = new List<Document>();
try{
results = await search.GetNextSetAsync();
}
catch(Exception e){
throw new PaginationTokenException(e.Message);
}
return results;
}
[Serializable]
public class PaginationTokenException : Exception
{
public PaginationTokenException() { }
public PaginationTokenException(string message)
: base(message) {
throw new Exception(message);
}
public PaginationTokenException(string message, Exception inner)
: base(message, inner) { }
}
Assuming you want to hide implementation details from the controller (i.e. you don't want the controller to know/care that it's DynamoDB), I would create a custom exception and throw that from Service1.
Service1 would look something like this:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
throw new MyCustomException('My error message', e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
In the controller you can then capture that exception explicitly:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (MyCustomException e)
{
return BadRequest(e.Message);
}
}
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; }
}
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();
}
}
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.
I have figured out how to create response object metadata in swashbuckle:
[Route("X/{Y:Guid}")]
[HttpGet]
[SwaggerResponse(HttpStatusCode.OK, "Bla", typeof(BlaDto))]
public IHttpActionResult GetSomething([FromUri] Guid someGuid)
{
Bla returnObject;
try
{
returnObject = _service.Get(someGuid);
}
catch (DatabaseException databaseException)
{
ModelState.AddModelError("DatabaseException", databaseException.Message);
return BadRequest(ModelState);
}
return Ok(returnObject);
}
I am still looking for examples to define request object meta data and meta data for 400 errors etc. Any pointers would be very much appreciated.
Object metadata of an operation is detected by Swashbuckle automatically. Object metadata of errors can be specified by adding more SwaggerResponseAttributes.
Here's an example:
[Route("X}")]
[HttpPost]
[SwaggerResponse(HttpStatusCode.Created, "Bla", typeof(BlaDto))]
[SwaggerResponse(HttpStatusCode.Conflict, Type = typeof(ErrorResponse))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(ErrorResponse))]
public IHttpActionResult CreateBla(BlaDto bla)
{
BladDto returnObject;
try
{
returnObject = _service.Create(bla);
}
catch (DatabaseException databaseException)
{
var error = new ErrorResponse { Message = databaseException.Message);
return Content(HttpStatusCode.BadRequest, error);
}
catch (SomeOtherException ex)
{
var error = new ErrorResponse { Message = ex.Message);
return Content(HttpStatusCode.Conflict, error);
}
return Ok(returnObject);
}
public class ErrorResponse
{
string Message { get; set; }
}