Unable to invoke [HttpPost] web api - c#

Below is my post method
[HttpPost]
public string Happay(string fileName)
{
//some code
}
When I try to invoke it from postman using this URL https://localhost:44313/SAPAPI/Happay?fileName=Payload1 it works but when I try to do the same from the browser it give me an exception that says
Resource not found
I removed the [HttpPost] attribute from the top an then I was able to invoke the method from the browser.

The QueryString (in your case ?fileName=Payload1) is only applicable for GET requests.
In case of POST you can provide parameters
as part of the request route, like POST /SAPAPI/Happay/Payload1
as a request body, like filename=Payload1 as raw
as a request header, like "x-file-name": "Payload1"
Depending on the mode you need to specify from where do you expect the parameter
public string Happay([FromRoute]string fileName)
public string Happay([FromBody]string fileName)
public string Happay([FromHeader(Name = "x-file-name")]string fileName)
Parameter as
Sample
Code change
Route
POST /SAPAPI/Happay/Payload1
[FromRoute(Name="fileName")]string fileName
Body
fileName=Payload1
[FromBody(Name="fileName")]string fileName
Header
"x-file-name": "Payload1"
[FromHeader(Name="x-file-name")]string fileName

Add [FromBody] before the parameter.
[HttpPost]
public string Happay([FromBody] string fileName)
{
//some code
}

If you are calling the url from a browser, this is a GET request. Please learn more about REST.

Related

C# AWS Lambda API Gateway from browser - parameters not working

With a simple C# AWS Lambda
public string FunctionHandler(string myParam1, ILambdaContext context)
{
return myParam1;
}
How should I pass parameters to an AWS Lambda function + API Gateway, via a browser GET request?
I'd like something like this for example:
https://[API ID].execute-api.[REGION].amazonaws.com/myFunc?myParam1=myValue1
In the browser it says {"message":"Internal Server Error"}
In the logs it says Error converting the Lambda event JSON payload to a string. JSON strings must be quoted, for example "Hello World" in order to be converted to a string: The JSON value could not be converted to System.String.
Without parameters it works, for example:
public string FunctionHandler(ILambdaContext context)
{
return Utf8Json.JsonSerializer.ToJsonString(context);
}
When sending a GET request in the browser https://[API ID].execute-api.[REGION].amazonaws.com/myFunc returns successfully {"AwsRequestId":"86ca2da9-438c-4865-8a0b-29d3ced37176","FunctionName":....
Ok I found a solution, instead of using the built-in parsing of parameters, it's possible to read a full JSON of parameters by reading a stream instead:
public string FunctionHandler(Stream requestStream, ILambdaContext context) { ... }
The requestStream here will have the parameters of GET/POST inside or a larger JSON, but have to be manually parsed. Note that the parameters may be sent b64 encoded (or probably also compressed). A good way would be to find a library which does this parsing.
In my case, I also write the consumer JS code, so I can ensure the parameters will always come in the same fashion so my problem is solved but if someone has knows a good library for this, please tell.
Example of a manual POST request data extraction which also supports b64 encoding:
public class StreamBody
{
public string body;
public bool isBase64Encoded;
}
public string FunctionHandler(Stream requestStream, ILambdaContext context)
{
using var sr = new StreamReader(requestStream);
var input = sr.ReadToEnd();
var sbody = Utf8Json.JsonSerializer.Deserialize<StreamBody>(input);
var body = !sbody.isBase64Encoded ? sbody.body : Encoding.UTF8.GetString(Convert.FromBase64String(sbody.body));

Web API Null Content C# from Postman

Trying this so hard.
Been calling a function API from Postman but when I tried to check the content it is null and the method is GET but I have set to POST.
Here is the receiving code
[System.Web.Http.HttpPost()]
// POST: Stream_Technical
public void Update_Activation(HttpRequestMessage request)
{
string message = request.Content.ReadAsStringAsync().Result;
}
Change your method like this
[HttpPost("collection")]
// POST: Stream_Technical
public void Update_Activation([FromBody]HttpRequestMessage request)
{
string s = JsonConvert.SerializeObject(request);
// string message = request.Content.ReadAsStringAsync().Result;
}
Here I also attached a screen shot
post man request

How to write API action method that would accept a file?

I am trying to write an API endpoint that would allow a user to send a file to my server using Asp.NET Core 3.1.
I have the following action method that needs to respond to the client's request and process the file validation and store the file.
[HttpPost, Route("api/store")]
public async Task<IActionResult> Store([FromBody] MultipartFormDataContent content)
{
// Validate the file, store it.
return Ok();
}
I am trying to send the file from the client-side like this
using HttpClient client = new HttpClient();
var multiForm = new MultipartFormDataContent();
multiForm.Add(new StringContent("1/12/1.jpg"), "custom_file_name");
FileStream fs = System.IO.File.OpenRead("1.jpg");
multiForm.Add(new StreamContent(fs), "file", "1.jpg");
// send request to API
var url = "https://localhost:123/api/store";
var response = await client.PostAsync(url, multiForm);
But the server returns HTTP Code 404 Not Found.
How can I correctly tweak the Store method above so it accepts the file being sent by the client?
You shouldn't include api/ in the action method route. The controller's route is already prefixed with api. Also, api/[controller] means the controller route will be api/ControllerName, then your action will be api/ControllerName/store.
If your controller class is named ThingsController then its route is api/things, and your Store action should be api/things/store.
You don't need to use both HttpPost and Route attributes on an action. The Route attribute accepts all HTTP verbs, so use one or the other (typically just HttpPost or whatever HTTP verb you will be handling with the action method. So:
[ApiController]
[Route("api/[controller]")]
public class ThingsController : ControllerBase
{
// POST api/things/store
[HttpPost("store")]
public async Task<IActionResult> Store([FromBody] MultipartFormDataContent content)
{
// Do stuff
}
}
Also, the controller should be using IFormFile to deal with file uploads in your controller. The documentation is here:
Upload files in ASP.NET Core .
Your controller would accept a IFormFile files parameter rather than MultipartFormDataContent content.
Example from MSDN, adapted to your action
[HttpPost("store")]
public async Task<IActionResult> Store(List<IFormFile> files)
{
// Get total size
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
// Gets a temporary file name
var filePath = Path.GetTempFileName();
// Creates the file at filePath and copies the contents
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
// Process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size, filePath });
}
If you're dealing with a single file, you can just pass IFormFile file instead of a list, and get rid of the looping in the action method.
I have not tested this code myself, wrote it on a mobile device. Hopefully it solves the problem though.

Can I specify that Web-Api method expects a file in Swashbuckle/Swagger generated page/json

I got a Web-Api with the following simplified method for obtaining a file:
[HttpPut("Test/{id}")]
public IActionResult PutTest(string id)
{
Stream file = Request.Body;
//rest of method
return StatusCode(201);
}
It works fine, although in the SwaggerUI page that gets created there's no mention of the method expecting a file in the body. Is there a way to specify the method expects a file in the generated SwaggerUI page?
I can simulate the input using the following piece of code:
var content = new StreamContent(new FileStream("C:\temp\test.txt", FileMode.Open));
using (var client = new HttpClient())
using (var response = client.PutAsync(url, content))
{
var result = response.Result;
//more code
}
The file upload is not documented because it's not a param in the action method. Swagger has no way of knowing what you're doing inside the action itself. Attempting to handle a file upload this way is bad practice anyways. You can use IFormFile, but that only works if your request body is encoded as multipart/form-data. If you're dealing with JSON or basically anything that qualifies as FromBody, then you need to bind to a byte[]:
[HttpPut("Test/{id}")]
public IActionResult PutTest(string id, byte[] file)
{
//rest of method
return StatusCode(201);
}
Now, the automatic FromBody applied by the [ApiController] attribute only works with class types, so I'm not sure off the top of my head whether it will apply to a byte[]. If not, just do:
public IActionResult PutTest(string id, [FromBody]byte[] file)

string is null when posting from Angular to Web API?

When I try to post a string to my web api, the value is null. I have tried wrapping it in quotes, but it is still null.
AngularJS code:
return $http.post("http://localhost:59437/api/Recaptcha/Post",
vcRecaptchaService.getResponse());
Web Api code:
[EnableCors("*", "*", "*")]
public class RecaptchaController : ApiController
{
public string Post([FromBody] string response)
{
return response;
}
}
I also am not sure how this even works because, i don't have a response in my form body. vcRecaptchaService.getResponse() just returns a response string and then I am going to send that to google's verify api to verify the recaptcha, so the [FromBody] part doesn't make sense to me if that is not part of the body
Your post call should be sending data in json format like {response: 'something'}
return $http.post("http://localhost:59437/api/Recaptcha/Post",
{ response: vcRecaptchaService.getResponse() } //data
);
Wrapping it in quotes worked, but I had to do it just like this:
return $http.post("http://localhost:59437/api/Recaptcha/Post",
'"' + vcRecaptchaService.getResponse() + '"'
);

Categories