Call Web API with multiple parameters - asp.net-core

WebAPI
public ActionResult Save([FromBody] string SaveId, List<Product> Products, List<Category> Categories)
{
}
How to call this web API ActionMethod from Controller Action Method?
asp.net Core MVC
public IActionResult SaveConfirmedDocument()
{
var Body = new {
documentID,
Products,
Categories
};
var client = new RestClient(Url);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + Authorization);
request.AddParameter("application/json", Body, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
}

How to call this web API ActionMethod from Controller Action Method?
Please note that once the request stream is read by an input formatter, it's no longer available to be read again.
For more information, please check the [FromBody] attribute that can not be applied to more than one parameter per action method.
In your action method, you applied [FromBody] attribute to the first parameter, to pass and bind data to List<Product> Products and List<Category> Categories parameters, you can make request with data, like below.
var client = new RestClient(Url);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + Authorization);
//to make the data can be bound to action parameters well
//please generate query string data based on your actual model class and properties, like below
request.AddParameter("Products[0].Id", 1, ParameterType.QueryString);
request.AddParameter("Products[0].Name", "PName1", ParameterType.QueryString);
request.AddParameter("Products[1].Id", 2, ParameterType.QueryString);
request.AddParameter("Products[1].Name", "PName2", ParameterType.QueryString);
request.AddParameter("Categories[0].Id", 1, ParameterType.QueryString);
request.AddParameter("Categories[0].Name", "CategoryName1", ParameterType.QueryString);
request.AddParameter("Categories[1].Id", 2, ParameterType.QueryString);
request.AddParameter("Categories[1].Name", "CategoryName2", ParameterType.QueryString);
request.AddJsonBody(Body);
IRestResponse response = client.Execute(request);
Test Result
Besides, if you'd like to pass both of these data through request body, you can try to modify the code like below.
Action method
public ActionResult Save([FromBody] ProductCategoryViewModel productsAndCategories)
{
//...
Model class
public class ProductCategoryViewModel
{
public string SaveId { get; set; }
public List<Product> Products { get; set; }
public List<Category> Categories { get; set; }
}
Code of making request(s) with test data
var Body = new {
SaveId = "123",
Products = new List<Product> {
new Product {
Id = 1,
Name = "PName1"
},
new Product {
Id = 2,
Name = "PName2"
},
new Product {
Id = 3,
Name = "PName3"
}
},
Categories = new List<Category>
{
new Category
{
Id = 1,
Name = "CategoryName1"
}
}
};
var client = new RestClient(Url);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + Authorization);
request.AddJsonBody(Body);
IRestResponse response = client.Execute(request);

Just create new class like this:
public class MyViewModel
{
public string SaveId {get; set;}
public List<Product> Products {get; set;}
public List<Category> Categories {get; set;}
}
And change your action to this:
public ActionResult Save(MyViewModel viewModel)
{
}
Just select what do you prefer - DocumentId or SaveId. It should be the same in the both places.

Related

Pass Composite Object to a Post Web API Method

I have the following composite object which I want to pass in web API post method in Xamarin:
public class ShoppingCartCustomizedItems
{
public AddToCart addToCart { get; set; }
public List<AddCustomizedProductSelectionsToCart> AddCustomizedProductSelectionsToCart { get; set; }
}
Below is the API service method to pass the data to the web API:
public static async Task<bool> AddCustomizedItemsInCart(ShoppingCartCustomizedItems addToCart)
{
var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(addToCart);
var content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Preferences.Get("accessToken", string.Empty));
var response = await httpClient.PostAsync(AppSettings.ApiUrl + "api/ShoppingCartItems/addCustomizedShoppingCartItem", content);
if (!response.IsSuccessStatusCode) return false;
return true;
}
Finally the web API post method has the following signature:
[HttpPost("[action]")]
public IActionResult addCustomizedShoppingCartItem([FromBody] ShoppingCartCustomizedItem shoppingCartCustomizedItem)
Now whenever I send the post method from Xamarin, the shoppingCartCustomizedItem is always null. How can I address this and what is the best practice to pass composite object in web API method?

How to send complex data to controller endpoint

I have this basic case:
[HttpPost("endpoint")]
public IActionResult Endpoint(DateTime date, string value, bool modifier)
{
return Ok($"{date}-{value}-{modifier}");
}
and I'm able to send a request to it with
var testContent = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "date", DateTime.Today.ToShortDateString() },
{ "value", "value1" },
{ "modifier", true.ToString() }
});
Instead I want my endpoint to be this instead
[HttpPost("endpointwithlist")]
public IActionResult EndpointWithList(DateTime date, List<string> value, bool modifier)
{
return Ok($"{date}-{value.FirstOrDefault()}-{modifier}");
}
How do I send this? I have tried the below, nothing works
var json = JsonConvert.SerializeObject(new { date, value = valueCollection.ToArray(), modifier });
var testContentWithList = new ByteArrayContent(Encoding.UTF8.GetBytes(json));
testContentWithList.Headers.ContentType = new MediaTypeHeaderValue("application/json");
You might create a model class for the payload
public class EndpointWithListModel
{
public DateTime Date {get; set;}
public List<string> Value {get; set;}
public bool Modifier {get; set;}
}
the method parameter then could use [FromBody] attribute
public IActionResult EndpointWithList([FromBody]EndpointWithListModel model)
then send the json to your POST method, example is here. Using HttpClient:
using (var client = new HttpClient())
{
var response = await client.PostAsync(
"http://yourUrl",
new StringContent(json, Encoding.UTF8, "application/json"));
}
if your variables(date, valueController and modifier) are in the right type, following code should work.
var json = JsonConvert.SerializeObject(new { date:date, value : valueCollection.ToArray(), modifier:modifier });

Send json data in Content of httpresponse in .net core API

I am trying to insert multiple users in database using a API. Now, suppose there are three users to be inserted and assume that one user didn't get inserted but other two are suceessfully inserted. So, I have a requirement to show response which shows that user first is successfully inserted , user 2nd have an error. That is why I have useed list of httpResponseMessage and in each httpresponsemessageobject, I will add complete user json indicating that this was the user, who have failed or success status
So, as per my implementation
I am using foreach loop to insert multiple users in db and returning reponse like this but my respose from api do not shows "user" object in content:
public async Task<List<HttpResponseMessage>> InserUsers(JArray obj)
{
List<myDTO> users = obj.ToObject<List<myDTO>>();
List<HttpResponseMessage> list = new List<HttpResponseMessage>();
foreach (var user in users)
{
var response = Insert(user);
list.Add(new HttpResponseMessage
{
Content = new StringContent(JsonConvert.SerializeObject(user), System.Text.Encoding.UTF8, "application/json"),
ReasonPhrase = response.ReasonPhrase,
StatusCode = response.StatusCode
});
}
return returnResposeList;
}
public HttpResponseMessage Insert(User user)
{
// do insert and if there is error
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Error,
ReasonPhrase = $"Error"
};
else
{
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
ReasonPhrase = $"Successfully inserted"
};
}
}
Thanks & regards
As per some of the comments, I don't understand why you need a status code for each insert rather than returning a bool or something like the inserted Id.
I think something like this works with what you have shown already along with return the user objects:
public async Task<ObjectResult> InserUsers(JArray obj)
{
List<myDTO> users = obj.ToObject<List<myDTO>>();
List<InsertUserResponseDto> list = new List<InsertUserResponseDto>();
foreach (var user in users)
{
var isSuccess = Insert(user);
list.Add(new InsertUserResponseDto
{
User = user,
StatusCode = isSuccess ? StatusCodes.Status200OK : StatusCodes.Status500InternalServerError,
});
}
var isSuccessfullInsert = list.Any(x => x.StatusCode == StatusCodes.Status207MultiStatus);
return StatusCode(isSuccessfullInsert ? StatusCodes.Status207MultiStatus : StatusCodes.Status200OK, list);
}
public bool Insert(User user)
{
try
{
// Complete insert
return true;
}
catch (Exception ex)
{
// Log Exception
return false;
}
}
public class InsertUserResponseDto
{
public User User { get; set; }
public int StatusCode { get; set; }
}
I have used the response code of 207 if any of the inserts fail otherwise I have used a 200. Each object within the list will contain the original user object along with an associated status code that you wanted including handling for success and failure.
For HttpResponseMessage, it used to describe the whole response, you should avoid returning HttpResponseMessage with multiple HttpResponseMessage.
For another option, you could try create your own HttpResponseMessage like
public class ApiResponseMessage
{
//
// Summary:
// Gets or sets the reason phrase which typically is sent by servers together with
// the status code.
//
// Returns:
// The reason phrase sent by the server.
public string ReasonPhrase { get; set; }
//
// Summary:
// Gets or sets the status code of the HTTP response.
//
// Returns:
// The status code of the HTTP response.
public HttpStatusCode StatusCode { get; set; }
//
// Summary:
// Gets or sets the content of a HTTP response message.
//
// Returns:
// The content of the HTTP response message.
public object Content { get; set; }
}
And then
[HttpPost("InserUsers")]
public async Task<List<ApiResponseMessage>> InserUsers()
{
List<User> users = new List<User> {
new User{ Name = "Jack" },
new User{ Name = "Tom"},
new User{ Name = "Tony"}
};
List<ApiResponseMessage> list = new List<ApiResponseMessage>();
foreach (var user in users)
{
var response = Insert(user);
list.Add(new ApiResponseMessage
{
Content = new { user },
ReasonPhrase = response.ReasonPhrase,
StatusCode = response.StatusCode
});
}
return list;
}
public ApiResponseMessage Insert(User user)
{
// do insert and if there is error
if (user.Name == "Tom")
{
return new ApiResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = $"Error"
};
}
else
{
return new ApiResponseMessage()
{
StatusCode = HttpStatusCode.OK,
ReasonPhrase = $"Successfully inserted"
};
}
}

ASP.NET Core Paypal Implementation

I try to implement a PayPal cart payment in ASP.NET Core. I have a working example in ASP.NET MVC 5 and I try to convert it to ASP.NET Core but I had no success. The point that I can not resolve is how to get the values that I have to get the transactionID, amount paid and Order ID. In ASP.NET MVC 5 the IPN action is as follows:
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string response = GetPayPalResponse(formVals, true);
if (response == "VERIFIED")
{
string transactionID = Request["txn_id"];
string sAmountPaid = Request["mc_gross"];
string orderID = Request["custom"];
:
:
In my ASP.NET Core application the IPN action is executed by PayPal and I have a VERIFIED response but I can not get the next three values. I have tried various ways to get these values without success.
My initial approach was the following:
string transactionID = Request.Query["txn_id"];
string sAmountPaid = Request.Query["mc_gross"];
string orderID = Request.Query["custom"];
Can someone suggest me a way to get these values?
I found a solution to my problem and I will post it just in case someone wants to do something similar.
[Route("PayPal/IPN")]
[HttpPost]
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
PayPalRespond response = GetPayPalResponse();
if (response.RespondType == RespondTypeEnum.Verified)
{
System.IO.File.AppendAllText(_env.WebRootPath + Path.DirectorySeparatorChar.ToString() + "data.txt", $"{DateTime.Now.ToString()} {response.JsonData}." + Environment.NewLine);
Order order = GetOrder(154);
//check the amount paid
if (order.Total <= response.AmountPaid)
{
// IPN Order successfully transacted. Save changes to database
return Ok();
}
else
{
// Amount Paid is incorrect
}
}
else
{
// Not verified
}
return Content("");
}
PayPalRespond GetPayPalResponse()
{
PayPalRespond output = new PayPalRespond();
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string paypalUrl = UseSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);
// Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param;
using (var ms = new MemoryStream(2048))
{
Request.Body.CopyTo(ms);
param = ms.ToArray();
}
string strRequest = Encoding.ASCII.GetString(param);
var QueryValues = System.Web.HttpUtility.ParseQueryString(strRequest);
output.Data = new List<QueryValue>();
foreach (var item in QueryValues.AllKeys)
{
if (item.Equals("txn_id"))
output.TransactionID = QueryValues[item];
else if (item.Equals("mc_gross"))
{
CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
NumberStyles style = NumberStyles.Number;
Decimal amountPaid = 0;
Decimal.TryParse(QueryValues[item], style, culture, out amountPaid);
output.AmountPaid = amountPaid;
}
else if (item.Equals("custom"))
output.OrderID = QueryValues[item];
output.Data.Add(new QueryValue { Name = item, Value = QueryValues[item] });
}
output.JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(output.Data);
StringBuilder sb = new StringBuilder();
sb.Append(strRequest);
foreach (string key in formVals.Keys)
{
sb.AppendFormat("&{0}={1}", key, formVals[key]);
}
strRequest += sb.ToString();
req.ContentLength = strRequest.Length;
//Send the request to PayPal and get the response
string response = "";
using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
{
streamOut.Write(strRequest);
streamOut.Close();
using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
{
response = streamIn.ReadToEnd();
}
}
output.RespondType = response.Equals("VERIFIED") ? RespondTypeEnum.Verified : RespondTypeEnum.Invalid;
return output;
}
The enumerator and the classes that you will need are the following:
public enum RespondTypeEnum { Verified, Invalid }
public class PayPalRespond
{
public RespondTypeEnum RespondType { get; set; }
public List<QueryValue> Data { get; set; }
public string JsonData { get; set; }
public string TransactionID { get; set; }
public string OrderID { get; set; }
public Decimal AmountPaid { get; set; }
}
public class QueryValue
{
public string Name { get; set; }
public string Value { get; set; }
}

PostAsJsonAsync And Anonymous Types - 404 Not Found Errors

I've been unsuccessfully trying to get this working.
I'm using AttributeRouting on the API and I have this method defined on my WebAPI:
[POST("update"), JsonExceptionFilter]
public HttpResponseMessage PostUpdate([FromJson] long id, DateTime oriDt, string notes, int score)
When I try to call this with the following code:
using (var httpClient = new HttpClient(CreateAuthorizingHandler(AuthorizationState)))
{
var args = new { id, oriDt, notes, score };
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("id", id.ToString()));
postData.Add(new KeyValuePair<string, string>("oriDt", oriDt.ToString(_dateService.DefaultDateFormatStringWithTime)));
postData.Add(new KeyValuePair<string, string>("notes ", notes));
postData.Add(new KeyValuePair<string, string>("score ", score.ToString(CultureInfo.InvariantCulture)));
var response = httpClient.PostAsJsonAsync(ApiRootUrl + "update", postData).Result;
if (response.IsSuccessStatusCode)
{
var data = response.Content.ReadAsAsync<bool>().Result;
return data;
}
return null;
}
The response is always 404 - not found. What am I missing here? I've tried using an anonymous object called args in the code with the same issue.
I've also tried it with and witout the [FromJson] attribute as well with the same results.
First, remove [FromJson]. With that, you have this action method.
public HttpResponseMessage PostUpdate(long id, DateTime oriDt,
string notes, int score)
If you POST to the URI below, it will work.
/update/123?oridt=somedate&notes=somenote&score=89
If you want to use request body to POST the fields (as you are doing with the client code), declare a class containing properties with name same as the field in request body.
public class MyDto
{
public long Id { get; set; }
public DateTime OriDt { get; set; }
public string Notes { get; set; }
public int Score { get; set; }
}
Then change the action method like this.
public HttpResponseMessage PostUpdate(MyDto dto)