I have a action method like this
[ResponseType(typeof(DiaryDeviceDTO))]
[HttpPost]
[Route("api/Device/Register")]
public async Task<IHttpActionResult> Register(DeviceRegistration deviceRegistration)
{
if (deviceRegistration == null)
{
return BadRequest("Request body is null");
}
DiaryDevice device = await _deviceBl.Register(deviceRegistration.RegistrationCode);
var deviceDto = Mapper.Map<DiaryDevice, DiaryDeviceDTO>(device);
return Ok(deviceDto);
}
When I call this api from PostMan with below request body, I get deviceRegistration object as null. I also set ContentType header as application/json
{
"ApiKey" : "apikey",
"RegistrationCode" : "123",
"ImeiNo" : "12345"
}
Then I try to read the request content as below-
string body = await Request.Content.ReadAsStringAsync();
This time I also get body = ""
But when I run my Unit test I get deviceRegistration as I wanted. So what's wrong with my code. Why my code only work for unit testing. I am using Web Api 2.2
Update / Solution
Sorry for asking this question. Actually it was my mistake. I accidentally read the request body inside Application_BeginRequest() method for logging. I move those logging codes inside Application_EndRequest() method and everything becomes ok.
Given what you've shown, this should work for the requests to api/device/register
[ResponseType(typeof(DiaryDeviceDTO))]
[HttpPost]
[Route("api/Device/Register")]
public async Task<IHttpActionResult> Register([FromBody]DeviceRegistration deviceRegistration)
{
if (deviceRegistration == null)
{
return BadRequest("Request body is null");
}
DiaryDevice device = await _deviceBl.Register(deviceRegistration.RegistrationCode);
var deviceDto = Mapper.Map<DiaryDevice, DiaryDeviceDTO>(device);
return Ok(deviceDto);
}
Note the [FromBody] attribute.
Related
I have built a Web API that is connected to a database for persons. I am now trying to call this Web API from a separate MVC-application which is supposed to have full CRUD. So far i have managed to do so with the Get and Post-methods to create a new person and see a list of the persons currently in the database.
When trying to do a similar call for the Put-method, i get the following error:
This is how my method UpdatePerson is written in my API-application:
[HttpPut]
[Route("{id:guid}")]
public async Task<IActionResult> UpdatePerson([FromRoute] Guid id, UpdatePersonRequest updatePersonRequest)
{
var person = await dbContext.Persons.FindAsync(id);
if (person != null)
{
person.Name = updatePersonRequest.Name;
person.Email = updatePersonRequest.Email;
person.Phone = updatePersonRequest.Phone;
person.Address = updatePersonRequest.Address;
await dbContext.SaveChangesAsync();
return Ok(person);
}
And this is how i am trying to consume the API in my separate MVC-project:
[HttpGet]
public IActionResult Edit()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Edit(PersonViewModel pvm)
{
HttpClient client = new();
StringContent sContent = new StringContent(JsonConvert.SerializeObject(pvm), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PutAsync("https://localhost:7281/api/Persons/", sContent);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Get");
}
else
{
return NotFound();
}
}
Everything is working fine when i try to update the database through the API-app so i am not really sure what is wrong with my request. I hope that someone here can spot the issue right away or at least help me out as i am quite a beginner with WEB APIs.
I have mostly tried changing the URL in my MVC-project but the issue remains.
Are you sure you are receiving the request? It seems that your URI is
"https://localhost:7281/api/Persons/"
and your API is expecting
"https://localhost:7281/api/Persons/{id}" -> where {id} should be the guid
you need to append the guid in the URI
Looks like the request doesn't receive the correct the correct parameters, because the URI that appears in your picture seems a generic method.
I'm trying to get a response from a web api(C#), but, in a specific method, the end point address is pointed to the port the front end is running on, but in the others methods it's working as well.
Future<void> onInit() async {
httpClient.baseUrl = await baseUrlGetter();
httpClient.addRequestModifier((Request request) {
request.headers['Accept'] = 'application/json';
request.headers['Content-Type'] = 'application/json';
request.headers['origemAcesso'] = 't2painel';
return request;
});
httpClient.addAuthenticator((Request request) {
var token = _storageService.token;
var headers = {'Authorization': "Bearer $token"};
request.headers.addAll(headers);
return request;
});
super.onInit();
}
Ahead, it'ts the onInit method in API class, the function baseUrlGetter is working, returnig the rigth url of the API.
But, in the method below:
Future<ImageModel> fetchDistribuidorImage() async {
var response = _errorHandler(await get('/api/Imagens/DistribuidorImagem'));
var data = ImageModel.fromJson(response.body);
return data;
}
Take a look to this images (read the images descriptions)
Only in the method "fetchDistribuidorImage" is not correct.
I can't seem to get my webapi to work in PostMan and it gives me a 404 when POSTing, but works only when using GET (even though the api specifically set to accept only POSTs! - go figure!)
Here's the controller code (that works) - NOTE: I can't use formdata as this is dotnet fw 4.72
[Route("api/GetProfile")]
[HttpPost]
public async Task<IHttpActionResult> GetProfile(string UserId)
{
var retval = new Profile();
if (UserId != null)
{
if (await dbv.IsValidUserIdAsync(UserId))
{
retval = await profile_Data.GetProfileAsync(UserId);
}
}
return Ok(retval);
}
The code works fine for GET (even though it's set to accept POST!), which it shouldn't.
In PostMan, the URI is
https://localhost:44371/api/GetProfile
The route is 100% correct!
On the Body tab, it is set to RAW and the following JSON is inside
{"UserId" : "69d40311-f9e0-4499-82ea-959949fc34fe"}
The parameter is 100% correct!
The error when attempting to POST is
{
"Message": "No HTTP resource was found that matches the request URI 'https://localhost:44371/api/GetProfile'.",
"MessageDetail": "No action was found on the controller 'Accounts' that matches the request."
}
If I put the parameters in the querystring, it works (even though the controller is set to accept POST).
If I change the controller to GET and PostMan to GET (and set the parameters in params), it works.
Is PostMan not compatible with ASP.Net webapi 2.0 ?
Why would GET work and POST not work? Makes no sense?
Try to set Content-Type application/json on postman and in your controller's POST method add the attribute FromBody
[FromBody] isn't inferred for simple types such as string or int. Therefore, the [FromBody] attribute should be used for simple types when that functionality is needed.
[Route("api/GetProfile")]
[HttpPost]
public async Task<IHttpActionResult> GetProfile([FromBody] string UserId)
{
var retval = new Profile();
if (UserId != null)
{
if (await dbv.IsValidUserIdAsync(UserId))
{
retval = await profile_Data.GetProfileAsync(UserId);
}
}
return Ok(retval);
}
Also consider to return CreatedAtAction or CreatedAtRoute instead of Ok
I am creating an HTTP Partial method in my ASP.NET Web API controller and I read this document http://benfoster.io/blog/aspnet-core-json-patch-partial-api-updates on how to achieve HTTP Partial methods in a controller. I get an exception when I hit the HTTP Partial endpoint that says
Here is my code for the Patch method in the controller:
[HttpPatch("{userId}")]
public IActionResult Patch([FromRoute(Name = "userId")]Guid userId, [FromBody] JsonPatchDocument<User> userProperties)
{
var indexOfUserToPartiallyUpdate = UsersInMemory.List.FindIndex(user => user.Id == userId);
if (indexOfUserToPartiallyUpdate == -1)
{
return BadRequest($"user with {userId} not found.");
}
var originalUser = UsersInMemory.List[indexOfUserToPartiallyUpdate];
userProperties.ApplyTo(UsersInMemory.List[indexOfUserToPartiallyUpdate], ModelState);
if (!ModelState.IsValid)
{
return new BadRequestObjectResult(ModelState);
}
var model = new
{
beforePatch = originalUser,
afterPatch = UsersInMemory.List[indexOfUserToPartiallyUpdate]
};
return Ok(model);
}
And here is the JSON body I'm sending through postman in the HTTP PATCH request:
I feel like I need to do something in the Startup.cs file such as configuring the JsonPatchDocument but I don't know how. Any help is much appreciated.
I think i found your issue: "Note that we always send an array of operations even if you're only sending a single operation."
Try to change your request in:
[
{
"op": "replace",
"path": "/email",
"value": "THIS_SOME_OTHER_EMAIL#gmail.com"
}
]
I am implementing webhook test code and I am running into a problem.
After I POST a sample notification to my webhook listener the Params are not in the request:
Request.Params["bt_signature"]
Request.Params["bt_payload"]
So the listener fails.
Below is both my Post Webhook code and Listener code; I'm not sure if I'm using gateway.WebhookTesting.SampleNotification correctly.
POST Test Webhook
private async Task PostTestNotification()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:50709/");
var gateway = config.GetGateway();
// Create sample notification
Dictionary<String, String> sampleNotification = gateway.WebhookTesting.SampleNotification(
WebhookKind.SUBSCRIPTION_CHARGED_SUCCESSFULLY, "sub_id_1234"
);
// Convert sample notification to JSON
string payloadJson = JsonConvert.SerializeObject(sampleNotification);
// Create StringContent of json sample notificaiton
var data = new StringContent(payloadJson);
// data looks like this when debugging { "bt_payload":"PG5vdGlmaWNhdGlvbj48dGltZXN0YW1wIHR5cGU9ImRhdGV0aW1lIj4yMDE2LTA1LTI3IDEzOjM2OjEwWjwvdGltZXN0YW1wPjxraW5kPnN1YnNjcmlwdGlvbl9jaGFyZ2VkX3N1Y2Nlc3NmdWxseTwva2luZD48c3ViamVjdD48c3Vic2NyaXB0aW9uPjxpZD5zdWJfaWRfMTIzNDwvaWQ+PHRyYW5zYWN0aW9ucz48dHJhbnNhY3Rpb24+PGlkPnN1Yl9pZF8xMjM0PC9pZD48YW1vdW50PjQ5Ljk5PC9hbW91bnQ+PHN0YXR1cz5zdWJtaXR0ZWRfZm9yX3NldHRsZW1lbnQ8L3N0YXR1cz48ZGlzYnVyc2VtZW50LWRldGFpbHM+PGRpc2J1cnNlbWVudC1kYXRlIHR5cGU9ImRhdGUiPjIwMTMtMDctMDk8L2Rpc2J1cnNlbWVudC1kYXRlPjwvZGlzYnVyc2VtZW50LWRldGFpbHM+PGJpbGxpbmc+PC9iaWxsaW5nPjxjcmVkaXQtY2FyZD48L2NyZWRpdC1jYXJkPjxjdXN0b21lcj48L2N1c3RvbWVyPjxkZXNjcmlwdG9yPjwvZGVzY3JpcHRvcj48c2hpcHBpbmc+PC9zaGlwcGluZz48c3Vic2NyaXB0aW9uPjwvc3Vic2NyaXB0aW9uPjwvdHJhbnNhY3Rpb24+PC90cmFuc2FjdGlvbnM+PGFkZF9vbnMgdHlwZT0iYXJyYXkiPjwvYWRkX29ucz48ZGlzY291bnRzIHR5cGU9ImFycmF5Ij48L2Rpc2NvdW50cz48L3N1YnNjcmlwdGlvbj48L3N1YmplY3Q+PC9ub3RpZmljYXRpb24+\n","bt_signature":"69r68j6hnzjpnq4j|508a7b4b3bbbe15c241c742331acfc5bacf37c54"}
// POST
HttpResponseMessage response = await client.PostAsync("webhooks/accept", data);
// RESPONSE
if (response.IsSuccessStatusCode == true)
{
// SUCCESS CONTENT
string resultJSON = await response.Content.ReadAsStringAsync();
}
else
{
// FAIL CONTENT
dynamic problem = await response.Content.ReadAsStringAsync();
}
}
}
catch (Exception ex)
{
//
Console.WriteLine("Exception: " + ex.Message);
}
}
Webhook LISTENER
// webhooks/accept endpoint
public async Task<ActionResult> accept()
{
try
{
var gateway = config.GetGateway();
if (Request.HttpMethod == "POST")
{
var bt_signature = Request.Params["bt_signature"]; <<<<<<< ALWAYS EMPTY >>>>>>>>>
var bt_payload = Request.Params["bt_payload"]; <<<<<<< ALWAYS EMPTY >>>>>>>>>
WebhookNotification webhookNotification = gateway.WebhookNotification.Parse(
Request.Params["bt_signature"],
Request.Params["bt_payload"]
); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EXCEPTION WHEN HIT - Value cannot be null, Parameter name: Input >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// ACTION Webhook
if (webhookNotification.Kind == WebhookKind.SUBSCRIPTION_CANCELED)
{
IsActive = false;
await Logger.LogInsight("", "WEBHOOK: SUBSCRIPTION_CANCELED " + webhookNotification.Subscription.Id );
}
else if (webhookNotification.Kind == WebhookKind.SUBSCRIPTION_CHARGED_SUCCESSFULLY)
{
IsActive = true;
await Logger.LogInsight("", "WEBHOOK: SUBSCRIPTION_CHARGED_SUCCESSFULLY " + webhookNotification.Subscription.Id);
}
// code ommitted for brevity, similar to above checking all 'kind' values
}
}
}
Why are the Braintree Request Params empty?
The problem was in the Webhook endpoint, the Braintree sample code is incorrect, it implies using MVC but developers need to use the Web Api for this, and the sample code will not work.
To get this working I left the Sample Notification POST above unchanged and created a new Webhook listener :
First create a class to receive the two braintree strings from the POST:
public class bt
{
public string bt_payload { get; set; }
public string bt_signature { get; set; }
}
And now create an empty Web Api 2 Controller:
[HttpPost]
[Route("api/webhooks/accept")]
public async Task<IHttpActionResult> accept(bt bt_lot)
{
var gateway = config.GetGateway();
WebhookNotification webhookNotification = gateway.WebhookNotification.Parse(
bt_lot.bt_signature,
bt_lot.bt_payload
);
if (webhookNotification.Kind == WebhookKind.SUBSCRIPTION_CANCELED)
{
// take your action here...
}
Im posting all my experiences with Braintree here on SO as there isn't a great deal of help here and I hope it helps others.
I have to say the Braintree Help staff are excellent and always answer questions with very detailed answers that 95% of the time resolved any issues I had, but this issue had me scratching my head as their example didn't work and the help staff assumed like me that the code should work.