Send object as parameter in client.PutAsync - asp.net

I am facing a problem in using PutAsync. PutAsync update an object. Below is my code. (Mongodb database)
Controller Code:
stringData = JsonConvert.SerializeObject(businessUnit); //businessUnit is updated object
var contentData = new StringContent(stringData, System.Text.Encoding.UTF8, "application/json");
response = client.PutAsync(baseAddress + "/api/BusinessUnit/" + businessUnit.Id, contentData).Result;
API Controller Code :
[HttpPut("{id}")]
public async Task<string> Put(string id, BusinessUnit businessUnit)
{
if (string.IsNullOrEmpty(id)) return "Invalid id !!!";
return await _businessUnitRepository.Update(id, businessUnit);
}
Given code works good but my problem is in API controller businessUnit parameter's all fields become null instead of id.
My Confusion is, if businessUnit parameter's all fields are null then why its primary key "id" is not null ??
I want to get all fields as parameter in businessUnit object from controller to api controller. How can I do it?
Thanks in advance.

Add the [FromBody] attribute to the 'businessUnit' parameter:
public async Task<string> Put(string id, [FromBody] BusinessUnit businessUnit)
This is called an Model Binding and allows to map data from HTTP requests to action method parameters:
[FromBody]: Use the configured formatters to bind data from the request body. The formatter is selected based on content type of the request.

Related

ASP.NET Core 3.1: POST-REDIRECT-GET: Passing parameters

I'm trying to implement POST-REDIRECT-GET technique where I post a JSON string to my razor OnPost method. This in turn redirects to OnGet method that takes in that string parameter. How do I hide this input parameter string to my OnGet method?
Edit: I tried using ViewData but the value is always null in my OnGet method even though I set it before redirect.
public async Task<IActionResult> OnPostData(string input_JSON)
{
TempData["InputJSON"] = input_JSON;
return RedirectToPage("GetData");
}
public async Task<IActionResult> OnGetGetData()
{
string tempData = TempData["InputJSON"] as string;
//do somethig with string;
}
I do form post in my javascript and when the new window opens, I see the input_string in my URL. How do I pass parameters between methods?
https://learn.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-2.2#tempdata-provider-and-session-state-cookies-arent-essential
According to Microsoft, TempData provider and session state cookies aren't essential. I had to make it essential in my Startup.cs file's ConfigureServices:
services.Configure<CookieTempDataProviderOptions>(options => {
options.Cookie.IsEssential = true;
});

Can I disable model binding and use the raw request body in an action in dotnet core?

I want to setup an endpoint for testing webhooks from third parties. Their documentation is uniformly poor and there is no way ahead of time to tell exactly what I will be getting. What I've done is setup an ApiController that will just take a request and add a row to a table with what they are sending. This lets me at least verify they are calling the webhook, and to see the data so I can program to it.
// ANY api/webook/*
[Route("{*path}")]
public ActionResult Any(string path)
{
string method = Request.Method;
string name = "path";
string apiUrl = Request.Path;
string apiQuery = Request.QueryString.ToString();
string apiHeaders = JsonConvert.SerializeObject(Request.Headers);
string apiBody = null;
using (StreamReader reader = new StreamReader(Request.Body))
{
apiBody = reader.ReadToEnd();
}
Add(method, name, apiUrl, apiQuery, apiHeaders, apiBody);
return new JsonResult(new { }, JsonSettings.Default);
}
This works great, except for this new webhook I am usign that posts as form data so some middleware is reading the body and it ends up null in my code. Is there any way to disable the model processing so I can get at the request body?
You could actually use model binding to your advantage and skip all that stream reading, using the FromBody attribute. Try this:
[Route("{*path}")]
[HttpPost]
public ActionResult Any(string path, [FromBody] string apiBody)

Unable to call web api PUT method

[System.Web.Http.Route("{id}")]
[System.Web.Http.HttpPut]
public async Task<ComplaintVM> Put(int id,int? employeeId)
{
var obj = _resolver.GetService<Complaint>();
obj.FranchiseId = 1;
obj.Id = id;
await obj.GetDetailAsyc();
obj.EmployeeId = employeeId;
await obj.AllocateAndManageCallAsyc();
return obj.EntityToVM();
}
This is put method with [System.Web.Http.RoutePrefix("api/app/complaint")]
so when i call my method with a PUT request i get the following error "message: "The requested resource does not support http method 'PUT'.".
but when i make the same call when the method does not have int? employeeId parameter. The call happens fine.
I am passing the employeeId as a json formatted request. I am using fiddler to test the code
The issue with Put(int id,int? employeeId) is the variable int? employeeId, since it was not part of the URL the model binder was not able to bind the attribute and hence was not able call the method with 2 parameters.
Using the [FormBody] did not work, the value returned was null, this is because the input was in form of a json.
The issue was solved by using a parameter of type Employee which had a property of EmployeeId so the value was mapped to the EmployeeId of the employee object, the method signature is Put(int id,Employee employee),
if there is no model class to bind, a parameter of type JObject can used as a input parameter and later the value can be extracted from JObject.

ASP.NET MVC 5 Attribute Routing: Url.Action returns null

I am experiencing an issue with a refactoring of our payment processing action method (called by our 3rd-party online payment provider). We have a product controller with the [Authorize] and [RoutePrefix("products")] attributes at the class level, and action methods including the following:
Product(string contractNumber) with routing attribute [Route("{productCode}")]
MakePayment(string productCode, PaymentAmountType? amountSelection, decimal? amountValue) with routing attribute [Route("{productCode}")] and [HttpPost] attribute
ProcessPayment(string productCode, string result) with routing attribute [Route("{productCode}")]
Because our payment gateway needs to be able to call our ProcessPayment action before the visitor is redirected to that same URL, we've had to refactor that to a separate controller without the [Authorize] attribute. (We already have mechanisms to prevent double-crediting a payment.)
Before this refactoring, the MakePayment action method correctly formulated the correct return URL in the following call to Url.Action():
var rawCallbackUrl = Url.Action("ProcessPayment", new { productCode = productCode });
The ProcessPayment action method has now been moved out of the product controller and into a new controller, ExternalCallbackController, which has no attributes (let alone [Authorize]), in order to avoid having an HTTP 401 response returned to the payment provider.
The route attribute on ProcessPayment is now [Route("order-processing/{productCode}/process-payment")] to avoid clashing with the RoutePrefix on the product controller. All references to this updated action method are updated to specify the ExternalCallbackController.
Manually browsing to the URL causes the breakpoint set inside ProcessPayment to be hit, so the route apparently works successfully.
The problem is that in MakePayment, the following call returns null:
var rawCallbackUrl = Url.Action("ProcessPayment", "ExternalCallback", new { productCode = productCode });
Given that I am specifying both the controller and action method, why is Url.Action(...) not returning the expected URL in the form order-processing/{productCode}/process-payment?
From Day 1, our RegisterRoutes() method in RouteConfig has had attribute routing properly initialised with
routes.MapMvcAttributeRoutes();
How can I get the correct URL returned from the call to Url.Action(...)?
Doh - I've figured out what went wrong. Despite the sanitising of names in the source code (which were specific to our client), it turns out that there was a mismatch in the following call:
var rawCallbackUrl = Url.Action("ProcessPayment", "ExternalCallback", new { productCode = productCode });
and the ProcessPayment() action method.
This is akin to the following (note the use of productNumber instead of productCode):
var rawCallbackUrl = Url.Action("ProcessPayment", "ExternalCallback", new { productNumber = productNumber });
trying to reference the action method:
[Route("order-processing/{productCode}/process-payment")]
public ActionResult ProcessPayment(string productCode, string result)
{
...
}
It also turns out that I can also use the same prefix "products" instead of "order-processing", as MVC creates one Route per attribute route in the routing table. Hope that helps others stuck in a similar situation.
I got this Error : Url.Action return null.
public async Task<IActionResult> ForgotPassword(ForgotPassword forgotPassword)
{
if (ModelState.IsValid)
{
// Find the user by email
var user = await userManager.FindByEmailAsync(forgotPassword.Email);
// If the user is found AND Email is confirmed
if (user != null && await userManager.IsEmailConfirmedAsync(user))
{
// Generate the reset password token
var token = await userManager.GeneratePasswordResetTokenAsync(user);
// Build the password reset link
var passwordResetLink = Url.Action("ResetPassword", "Account",
new { email = forgotPassword.Email, token }, Request.Scheme);
ViewBag.PRL = passwordResetLink;
// Send the user to Forgot Password Confirmation view
return View("ForgotPasswordConfirmation");
}
return View("ForgotPasswordConfirmation");
}
return View(forgotPassword);
}

A simple POST request to Web API not hitting the API at all

From my MVC application, I am trying to make a POST request to these sample end-points (actions) in an API controller named MembershipController:
[HttpPost]
public string GetFoo([FromBody]string foo)
{
return string.Concat("This is foo: ", foo);
}
[HttpPost]
public string GetBar([FromBody]int bar)
{
return string.Concat("This is bar: ", bar.ToString());
}
[HttpPost]
public IUser CreateNew([FromBody]NewUserAccountInfo newUserAccountInfo)
{
return new User();
}
Here's the client code:
var num = new WebAPIClient().PostAsXmlAsync<int, string>("api/membership/GetBar", 4).Result;
And here's the code for my WebAPIClient class:
public class WebAPIClient
{
private string _baseUri = null;
public WebAPIClient()
{
// TO DO: Make this configurable
_baseUri = "http://localhost:54488/";
}
public async Task<R> PostAsXmlAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var requestUri = new Uri(client.BaseAddress, uri);
var response = await client.PostAsXmlAsync<T>(requestUri, value);
response.EnsureSuccessStatusCode();
var taskOfR = await response.Content.ReadAsAsync<R>();
return taskOfR;
}
}
}
I have the following default route defined for the Web API:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
UPDATE
My code breaks into the debugger until the time the PostAsXmlAsync method on the System.Net.HttpClient code is called. However, no request shows up in Fiddler.
However, if I try to compose a POST request in Fiddler or try to fire a GET request via the browser to one of the API end-points, the POST request composed via Fiddler tells me that I am not sending any data and that I must. The browser sent GET request rightly tells me that the action does not support a GET request.
It just seems like the System.Net.HttpClient class is not sending the POST request properly.
One of the most usual problems is that you don't use the appropriate attribute.
Take into account that there are attributes for ASP.NET MVC and ASP.NET Web API with the same name, but which live in different namespaces:
For Web API you must use the one in System.Web.Http
For MVC, the one in System.Web.MVc
This is a very very usual error, and it affects to allkind of things that exist for both MVC and Web API. So you must be very careful when using something which can exists in bith worlds (for example filters, attributes, or dependency injection registration).
I experienced a similar problem (may not be same one though). In my case, I hadn't given name attribute to the input element. I only figured that out when fiddler showed no post data being sent to the server (just like your case)
<input id="test" name="xyz" type="text" />
Adding the name attribute in the input tag fixed my problem.
However, there is one more thing to note. WebAPI does not put form data into parameters directly. Either you have to create an object with those properties and put that object in the parameter of the post controller. Or you could put no parameters at all like this:
[Route("name/add")]
public async Task Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return;
}
var provider = PostHelper.GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
var clientId = result.FormData["xyz"];
...
Try changing the FromBody to FromUri.
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Remove FromBody at all and don't make any restrictions in passing parameters (it can be passed at this time either in uri, query string or form submissions (which is kinda a similar to query strings)
[HttpPost]
public string GetFoo(string foo){...}
It will be implicitly parsed and passed.

Resources