Rest API No action was found on the controller - c#

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.).

Related

Multiple ProducesResponseType for same status code 200OK

I am using swagger ui and I have action like this in my controller:
[HttpGet("{id}")]
[AllowAnonymous]
[ProducesResponseType(typeof(PublicDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PrivateDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUser(string id)
{
try
{
...
if (...)
{
var dto = _mapper.Map<PrivateDto>(user);
return Ok(dto);
}
else
{
var dto = _mapper.Map<PublicDto>(user);
return Ok(dto);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error inside action");
return StatusCode(500, "Internal server error");
}
}
The problem is that on my swagger UI page I can see only one of them in Responses area, and got scheme for only one. Is there a way to have more than one objects for response for one status code?
This is not a bug it’s a limitation of the open api specification, which SB is built on. Specifically you can only have one response object per response code. See https://spec.openapis.org/oas/v3.1.0#responses-object

How to read and iterate over HTTP GET parameters?

I'm doing a simple backend with .Net Core that reads data from GET and POST, but I'm not finding how to read GET params neither POST. I have this, a simple Controller:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
[HttpGet]
public string Get()
{
return "Test GET OK";
}
[HttpPost]
public string Post()
{
return "Test POST OK";
}
}
Client, a simple windows forms with net framework 4.6, is using HttpClient to sent http get request:
public async Task<string> GetAsyncHttpClient(string uri)
{
string responseBody = "";
try
{
UriBuilder builder = new UriBuilder(uri);
builder.Query = "name=testName";
HttpResponseMessage response = await client.GetAsync(builder.Uri);
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
}
catch (Exception e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
responseBody = "Error with GET operation, exception:\n" + e.ToString();
}
return responseBody;
}
And generated URL is like this:
http://localhost:5915/test?name=testName
Trust me that I've searched a lot and I didn't find how to read and iterate over GET params.
How should I do it?
Thanks!
Normally you would just add a parameter to your method:
[HttpGet]
public string Get(string name)
You can be explicit that it's a query string parameter like this:
[HttpGet]
public string Get([FromQuery]string name)
As for iterating the parameters, you'll have to use Request.Query:
foreach (KeyValuePair<string, StringValues> entry in Request.Query)
{
string key = entry.Key;
foreach (string value in entry.Value)
{
System.Diagnostics.Debug.WriteLine($"{key}={value}");
}
}
You'll need to add a using Microsoft.Extensions.Primitives; for the StringValues. The reason why it's StringValues is because you could have a URL like this: https://www.example.com/test?name=Brian&name=Jennifer, so you would end up with two values in the Query collection entry for "name".
I don't know exactly what you mean but if you just want to make a post or get request then you do it in your client like this:
using (var client = new HttpClient())
{
try
{
HttpResponseMessage response =
await client.PostAsync("https://localhost:YOURPORT/Test?username=test", YOURCONTENT);
var cont = await response.Content.ReadAsStringAsync();
Console.WriteLine(cont);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey();
}
make sure you are using http or https accordingly you have to adjust the url as well
if you mean Query Params you can access them by adding this to the API:
[HttpPost]
public void Post([FromQuery] string username){
//do something
}

SaveChangesAsync not working inside PUT method

I am Using ODataController to insert and Update Entries to Database
I am hitting the PUT method , using .UpdateEntryAsync()
Here is my Put Method.
public async Task<IHttpActionResult> Put([FromODataUri] string key, Delta<KYCBtnDetails> patch)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
KYCBtnDetails KYCBtnDetails = await _KYCDBModel.KYCBtnDetails.FindAsync(key);
if (KYCBtnDetails == null)
{
return NotFound();
}
patch.Put(KYCBtnDetails);
KYCBtnDetails.id = key;
try
{
await _KYCDBModel.SaveChangesAsync();
}
catch (Exception e)
{
}
return Updated(KYCBtnDetails);
}
catch (Exception ex)
{
}
return null;
}
on await _KYCDBModel.SaveChangesAsync() it give me error as
"New Transaction is not allowed because there are other threads running in the session"
"An error occurred while starting a transaction on the provider connection. See the inner exception for details."
Sometimes it runs and sometimes it gives error
Please Help me on this,
Thank You.
By convention, here's how the Put controller action signature is expected to look like:
public async Task<IHttpActionResult> Put([FromODataUri] string key, [FromBody]KYCBtnDetails kycBtnDetails)
{
// ...
}
If you decide to use Patch, here is how the controller action signature is expected to look like:
public async Task<IHttpActionResult> Patch([FromODataUri] string key, [FromBody]Delta<KYCBtnDetails> patch)
{
// ...
}
Take specific note of FromBody attribute

ASP.NET MVC: Why my custom ActionFilter changes my HTTP response code?

I've developed a custom action filter in order to use it for logging response of my web-service in ASP.NET MVC.
However I don't know why when I add this action filter to my method, HTTP status response of my controller changes to 500 and it returns the message: 500 Intenal Server Error. I put all logic inside try catch block but still problem persists.
Here is my custom ActionFilter:
public class LogActionFilter : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
try
{
Log("OnActionExecuting", actionExecutedContext);
}
catch (Exception)
{
}
}
private void Log(string methodName, HttpActionExecutedContext context)
{
try
{
string resopnseBody = getBodyFromResponse(context);
HttpResponseMessage response = context.Response;
var headers = response.Headers;
var content = response.Content;
var actionName = response.ToString();
var message = "";
message = String.Format("response:{0}", resopnseBody);
Debug.WriteLine(message, "");
}
catch (Exception e)
{
}
}
private string getBodyFromResponse(HttpActionExecutedContext context)
{
string data;
using (var stream = context.Response.Content.ReadAsStreamAsync().Result)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
data = context.Response.Content.ReadAsStringAsync().Result;
}
return data;
}
}
Update:
Furthur investigating my code I found that calling getBodyFromResponse leads to this error. I myself suspect to part which I will try to read stream .Result twice however since I copied! this code from elsewhere I don't understand its logic clearly.
Update2:
Here is a sample method in my controller:
[LogActionFilter]
[System.Web.Http.HttpPost]
public async Task<IHttpActionResult> Test()
{
return Ok(new WebServiceResult { responseCode = 0, responseMessage = null });
}
Update 3:
replacing
resopnseBody = getBodyFromResponse(context);
with below line fixed issue but I don't know why!
resopnseBody = context.Response.Content.ReadAsStringAsync().Result;
I got it to run by removing some lines from getBodyFromResponseAsync
private string getBodyFromResponseAsync(HttpActionExecutedContext context)
{
return context.Response.Content.ReadAsStringAsync().Result;
}
I hope the result is what you need.

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

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.

Categories

Resources