Why doesn't WebApi config for WebHook work on POST? - c#

i want to get posted data from IFTTT from WebHook. It works when using GET but it doesn't when using POST.
[HttpPost]
[Route("InsertData")]
public IActionResult InsertData([FromBody] string FromAddress)
{
try
{
//var fromAddress = Request.Form["FromAddress"].ToString();
_webHookDb.UserData.Add(new UserData()
{
FromAddress = FromAddress,
DateTime = DateTime.Now
});
_webHookDb.SaveChanges();
return new JsonResult(FromAddress);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}

Create a model to hold the data
public class Model {
public string FromAddress { get; set; }
}
use that in the action endoint.
[HttpPost]
[Route("InsertData")]
public async Task<IActionResult> InsertData([FromBody] Model model) {
try {
if(ModelState.IsValid) {
_webHookDb.UserData.Add(new UserData() {
FromAddress = model.FromAddress,
DateTime = DateTime.Now
});
await _webHookDb.SaveChangesAsync();
return new Ok(model);
}
return BadRequest(ModelState); //Bad data?
} catch (Exception ex) {
return StatusCode(500, ex.Message); //Something wrong with my code?
}
}
Review the message returned from the response in the web-hook to get details about why the request failed.
If HTTP Status Code 500 then something is wrong with how the data is being saved.
If HTTP Status Code 400 then something is wrong with how the data is being sent.

Related

Rest API No action was found on the controller

I call your experience to help me solve a problem.
I have this code:
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
If i left just one method like this, i can call it from Postman with no problem, but if i try to make another one, like this:
{
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
[ActionName("PrintareAwbHtml")]
[HttpPost]
public async Task<object> PrintareAwbHtml([FromBody]FanCourier fanCourier)
{
try
{
var responseString =
await fanCourier.PrintareAwbHtml(fanCourier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
The response from Postman call is:
"Multiple actions were found that match the request: \r\nImportAwb on type Courier.Rest.Controllers.FanController\r\nPrintareAwbHtml on type Courier.Rest.Controllers.FanController"
I was tried to add a [Route("api/[controller]")] before public class FanController : ApiController and the error was change to:
No action was found on the controller 'Fan' that matches the request.
I have tried to find something on the internet but i found nothing to help my situations.
You should define routes for each of your actions so you know which will be called when specific API is called.
Use [Route] tag to accomplish that
[ActionName("ImportAwb")]
[HttpPost]
[Route("Action1")]
public async Task<object> ImportAwb([FromBody]JObject data)
{
...
And from postman, call your endpoint with url being. http://yoururl.com/Action1.
(YourUrl would be the path you set up for this controller... might include /api or what you might have configured. Add /Action1 to the end of that url)
You can have multiple routes to the same URL as long as they are different methods (post, get, delete, patch etc.).

Table value parameters not working with Get method

Actually I'm working using dapper.
LoginAuditModel:
public class LoginAuditModel
{
public IList<GuidIdTableType> UserIdTableType { get; set; } = new List<GuidIdTableType>();
public DateTime StartingDate { get; set; }
public DateTime EndingDate { get; set; }
}
Repository:
public async Task<IEnumerable<LoginAuditGetViewModel>> LoginAuditGet(LoginAuditModel model)
{
try
{
async Task<IEnumerable<LoginAuditGetViewModel>> DoLoginAuditGet()
{
using (var connection = _connectionManager.GetOpenConnection(_configuration.GetConnectionString(connectionstring)))
{
return await connection.QueryAsync<LoginAuditGetViewModel>("[dbo].[spName]", param: new
{
UserIdTableType = ((List<GuidIdTableType>)model.UserIdTableType).ToDataTable(),
model.StartingDate,
model.EndingDate
}
, commandType: CommandType.StoredProcedure);
}
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
throw ex;
}
}
Service:
public async Task<IEnumerable<LoginAuditGetViewModel>> LoginAuditGet(LoginAuditModel model)
{
async Task<IEnumerable<LoginAuditGetViewModel>> DoLoginAuditGet()
{
return await _employeeRepository.LoginAuditGet(model);
}
return await DoLoginAuditGet();
}
Controller:
[HttpGet]
public async Task<IActionResult> LoginAuditGet([FromQuery]LoginAuditModel model)
{
try
{
async Task<IActionResult> DoLoginAuditGet()
{
var rModel = await _employeeService.LoginAuditGet(model);
if (rModel is null || !rModel.Any()) return NotFound();
return Ok(rModel);
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
When I execute this code using swagger in my case, table valued parameter is always passing with count = 0 (UserIdTableType), but for some reason, when I change controller method to [HttpPost]
it pass parameter correctly! and everything it's working fine:
[HttpPost]
public async Task<IActionResult> LoginAuditGet(LoginAuditModel model)
{
try
{
async Task<IActionResult> DoLoginAuditGet()
{
var rModel = await _employeeService.LoginAuditGet(model);
if (rModel is null || !rModel.Any()) return NotFound();
return Ok(rModel);
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
So, my question is why it is working as a Post method and not with Get? I need to change something to works with Get? Regards
In your case, you want to send an array of objects as a query string in URL, I think this is impossible, but you can send an array of base data types such as int, string ... etc.
but in Post and Put, it is sending the data as body and have another type of data transfer.
and you should know, there is a limitation on Query String length, you can have a look here: click me

how to generate a correlation id in web api 2/app insights?

I am using App Insights in my web api 2 project, which is being called form a React Front End.
When something wrong happens I would like to show the user a generic error like: Please contact the admin, and show them a Guid, or error number.
Then with that error number I could check in App Insights what the real exception was.
Is this possible?
My web api code is below
namespace LuloWebApi.Controllers
{
[Authorize]
public class ClientController : ApiController
{
[HttpGet]
public async Task<List<Client>> GetClients()
{
//var telemetry = new TelemetryClient();
//try
//{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
return await clientStore.Query().ToListAsync();
//}
//catch (System.Exception ex)
//{
// telemetry.TrackException(ex);
//}
}
[HttpGet]
public async Task<IHttpActionResult> GetClient(string clientId)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
var client = await clientStore.Query().FirstOrDefaultAsync(x => x.Id == clientId);
if (client == null)
{
return NotFound();
}
return Ok(client);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
[HttpPut]
public async Task<IHttpActionResult> UpdateClient(string id,[FromBody]Client client)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await clientStore.UpdateAsync(client);
return Ok(result);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
[HttpPost]
public async Task<IHttpActionResult> AddCLient([FromBody]Client Client)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var added = await clientStore.AddAsync(Client);
return StatusCode(HttpStatusCode.NoContent);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
public async Task<IHttpActionResult> DeleteClient(string clientId)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
await clientStore.RemoveByIdAsync(clientId);
return Ok(clientId);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
}
}
Please correct me if I misunderstand you.
I think it's as easy as manually creating a guid, and add to the exception telemetry as well to the BadRequest().
try
{
//some code here
}
catch(Exception ex)
{
string guid = Guid.NewGuid().ToString();
Dictionary<string,string> dt = new Dictionary<string, string>();
dt.Add("my error number1", guid);
telemetryClient.TrackException(ex,dt);
return BadRequest("Unknown error:"+guid);
}
And when you get the guid, you can search the related error in azure portal:

Web Api always returns http status code 200 when an exception occurs

public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new NiceInternalServerExceptionResponse("The current operation could not be completed sucessfully.);
}
}
When a call this Get action:
[HttpGet]
public async Task<IHttpActionResult> Get()
{
Convert.ToInt16("this causes an exception state");
var data = await service.Get();
return Ok(data);
}
An exception is raised... and my global exc handler is triggered.
When my custom response is returned to the client my fiddler always says:
Result: 200
I could also change the return Ok(data); to return NotFound();
That will not change anything in the result status code.
How can I overwrite/intercept the http status creation and return my own status code 500 instead?
On my web client I need to show a nice error dialog with a logging id + error message ONLY when status code 500 is returned.
You need to set the status code on the IHttpActionResult:
public class NiceInternalServerExceptionResponse : IHttpActionResult
{
public string Message { get; private set; }
public HttpStatusCode StatusCode { get; private set; }
public NiceInternalServerExceptionResponse(
string message,
HttpStatusCode code)
{
Message = message;
StatusCode = code;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(StatusCode);
response.Content = new StringContent(Message);
return Task.FromResult(response);
}
}
And in your GlobalExceptionHandler pass HttpStatusCode.InternalServerError (500):
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new NiceInternalServerExceptionResponse(
"The current operation could not be completed sucessfully.",
HttpStatusCode.InternalServerError);
}
I do it like this...
[HttpPost]
public HttpResponseMessage Post()
{
try
{
// Do stuff
}
catch (Exception ex)
{
// Something went wrong - Return Status Internal Server Error
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
Works same for a Get.
You could use next code for custom error:
return Content(HttpStatusCode.NotFound, "Foo does not exist.");

Web API 2 Http Post Method

I am disgusted not have found a solution to this problem.
I started creating a new api using Web API 2 and just cannot get the POST and PUT to work. The Get all and Get single item works perfectly fine.
There are no related articles anywhere, and those that i've found relates only to Gets and Web API, but not Web API 2.
Any assistance would do please.
// POST: api/checkOuts
[HttpPost]
[ResponseType(typeof(checkOut))]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IHttpActionResult> PostcheckOut(checkOut co)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.id }, checkOut);
}
So basically, I'm just attempting to get a debug into the method.
Was especially disappointed in this link as it covered almost everything, but ai. http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing
Regards
This is a working code
// POST api/values
[HttpPost]
[ResponseType(typeof(CheckOut))]
public async Task<IHttpActionResult> Post([FromBody] CheckOut checkOut)
{
if (checkOut == null)
{
return BadRequest("Invalid passed data");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.Id }, checkOut);
}
I've declared CheckOut class to be like this :
public class CheckOut
{
public int Id { get; set; }
public string Property2 { get; set; }
}
The Key things here are :
1- You need to add [FromBody] to your Api method.
2- I've tested it using Fiddler,
i- by choosing POST action.
ii- content-type: application/json.
iii- passing {"Id":1,"Property2":"Anything"} in the message body.
Hope that helps.

Categories

Resources