Validate Json String against Model asp.net core api - c#

I need to send Model data along with Image so I am using FormData. As we can't pass model directly, I am using JSON.stringify.How do I validate this Json string against Model (Same as we do ModelState validation)?

yes you need to extract the model from the form data first
e.g.
var request = HttpContext.Current.Request;
var model = new yourViewModel();
model.field1 = request.Form["field1"];
model.field2 = request.Form["field2"];
model.Document = request.Files["Document"];
ModelState.Clear();
this.Validate(model);
if (ModelState.IsValid) {
}
Read more here

Related

How do I pass an IFormFile to my service layer?

I'm working on an ASP.NET Core 2 MVC application, trying my hand at setting up a service layer. I have a pretty basic application in which I want to:
upload an image alongside a name
add a record to the database
store the file on disk in the wwwroot folder with a naming convention that corresponds to the primary key of the record I just added.
Right now I've got that all that working just fine in my controller with a viewmodel. But I'd like to instead have a nice method in my service layer that does both the database add and the file system add, but I'm not finding a way to pass the file over to the service.
I figured out how to make the database add in the service layer too, but not how to get the file over there to do anything with it.
This is my controller:
[HttpPost]
public async Task<IActionResult> Create(CreateMentorViewModel model)
{
var mentor = new Mentor
{
FirstName = model.FirstName,
LastName = model.LastName,
Created = DateTime.Now,
};
_ctx.Add(mentor);
await _ctx.SaveChangesAsync();
int newid = mentor.Id;
if (model.Image != null)
{
var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "img/profilepics");
var fileId = newid + ".jpg";
var filePath = Path.Combine(uploads, fileId);
model.Image.CopyTo(new FileStream(filePath, FileMode.Create));
}
return RedirectToAction("Index", "Mentor");
}
What I'm looking for is some pointers on how to send the file from the viewmodel in the controller to the service or maybe more to the point - should I do that at all?
You shouldn't depend on ASP.NET in your service layer. Pass on Stream instead:
var result = await myService.ProcessFileAsync(formFile.OpenReadStream());
You can simply pass the IFormFile as a parameter to the service e.g.
public void MyMethod(IFormFile file)
{
...
}
and pass model.Image
[HttpPost]
public async Task<IActionResult> Create(CreateMentorViewModel model)
{
...
myService.MyMethod(model.Image);
...
}

Post Multiple Parameters Without DTO Class

I have an action on my web project which calls to an API
[HttpPost]
public async Task<IActionResult> ExpireSurvey(int id)
{
var token = await HttpContext.GetTokenAsync("access_token");
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var path = "/api/forms/ExpireSurvey";
var url = Domain + path;
var data = JsonConvert.SerializeObject(id);
HttpContent httpContent = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PutAsync(url, httpContent);
return Json(response);
}
}
In the API project this is received as follows:
[HttpPut]
public IActionResult ExpireSurvey([FromBody] int surveyId)
{
_repository.ExpireSurvey(surveyId, expiryDate);
return Ok();
}
This works fine - however, say I want to pass in an int id and a DateTime variable, how do I serialise and pass them both into the HttpContent? I can do it with a DTO object, but I don't want to be setting up DTO objects when there is only two fields.
You can use anonymous types like this
var x = new { id = 2, date = DateTime.Now };
var data = JsonConvert.SerializeObject(x);
When receiving the data, you can only have one [FromBody] parameter. So that doesn't work for receiving multiple parameters (unless you can put all but one into the URL). If you don't want to declare a DTO, you can use a dynamic object like this:
[HttpPost]
public void Post([FromBody] dynamic data)
{
Console.WriteLine(data.id);
Console.WriteLine(data.date);
}
Don't overdo using anonymous types and dynamic variables though. They're very convenient for working with JSON, but you lose all type checking which is one of the things that makes C# really nice to work with.
I think it would be helpful to recognize that ASP.NET Core is REST-based and REST fundamentally deals with the concept of resources. While not an unbreakable rule, the general idea is that you should have what you're calling DTOs here. In other words, you're not posting distinct and unrelated bits of data, but an object that represents something.
This becomes increasingly important if you start mixing in things like Swagger to generate documentation for your API. The objects you create become part of that documentation, giving consumers of your API a template for follow in the development of their apps.
Long and short, I'd say embrace the concept of resources/objects/DTOs/whatever. Model the data your API works with. It will help both you as a developer of the API and any consumers of your API.
You can pass multiple parameters in as URL as below example
Parameter name must be the same (case-insensitive), If names do not
match then values of the parameters will not be set.
[HttpPost]
[Route("{surveyId}/{expiryDate}")]
public IActionResult Post(int surveyId, DateTime expiryDate)
{
return Ok(new { surveyId, expiryDate });
}
Call URL
http://localhost:[port]/api/[controller]/1/3-29-2018
Based on the answers above, I got the following code working. Hope this helps someone! (thanks to others of course for getting me on the right track)
/// <summary>
/// Post api/dostuff/{id}
[HttpPost]
[Route("dostuff/{id}")]
public async Task<IActionResult> DoStuff([FromBody]Model model, int id)
{
// Both model and id are available for use!
}
You can replace the line
var data = JsonConvert.SerializeObject(id);
with
var data = new StringContent(JsonConvert.SerializeObject((surveyId, expiryDate)), Encoding.UTF8, "application/json");
The trick is that you use a Tuple object containig your parameters as a single parameter.
You should use the Tuple type on the the server side as well. It could look like:
[HttpPost]
public Task MyWebApiMethod([FromBody] (int SurveyId, DateTime ExpiryDate) parameters)
{
int surveyId = parameters.SurveyId;
DateTime expiryDate = parameters.ExpiryDate;
// Process your parameters ...
}
I do not have Visual Studio now and sorry if there are any compilation issues etc.
you can do it with a dictionary
Dictionary<int, object> dict = new Dictionary<int, object>();
dict["id"] = 1
dict["date"] = DateTime.Now;
JsonConvert.SerializeObject(dict);

How to get data between controller action and post method

I'm using asp.net mvc,
I have a POST Method that called from an outside service(differnet domain) and i want when it called to get data from my site.
Before the method was called I tried to save that data in Session object and in a Cookie,
But the data is not there.
Maybe it could be because the post request sent from a different domain?
What am I doing wrong?
[HttpPost]
public ActionResult Callback(Callback callback)
{
Not Working ->
var userMail = HttpContext.Request.Cookies.Get("Current")["UserMail"];
}
public ActionResult SomeSitePage()
{
var cookie = new HttpCookie("Current");
var userMail = (Session["user"] as ApplicationUser).Email;
cookie["UserMail"] = userMail;
HttpContext.Response.Cookies.Add(cookie);
}
Thanks.
Apparently If a post method is called from another domain,
saving data in session or cookies does not count,
I needed to save it in the database first and then pull it out in the post method.

serializing form data to a web api

is there a better way of implementing this code:
public Payment SendPaymentToSagePay(Payment paymentModel)
{
var webClient = new WebClient()
{
BaseAddress = "http://localhost:64317/api/PaymentStart"
};
string price = paymentModel.price.ToString();
string productId = paymentModel.productId.ToString();
string paymentMode = paymentModel.paymentMode.ToString();
string context = paymentModel.context.ToString();
var collection = new NameValueCollection();
collection.Add("title", paymentModel.title);
collection.Add("firstName", paymentModel.firstName);
collection.Add("lastName", paymentModel.lastName);
collection.Add("email", paymentModel.email);
collection.Add("context", context);
collection.Add("programmeName", paymentModel.programmeName);
collection.Add("productId", productId);
collection.Add("price", price);
collection.Add("cardHolderTitle", paymentModel.cardHolderTitle);
collection.Add("cardHolderLastName", paymentModel.cardHolderLastName);
collection.Add("cardHolderFirstName,", paymentModel.cardHolderFirstName);
collection.Add("cardHolderEmail", paymentModel.cardHolderEmail);
collection.Add("selfAddress1", paymentModel.selfAddress1);
collection.Add("selfAddress2", paymentModel.selfAddress2);
collection.Add("selfCity", paymentModel.selfCity);
collection.Add("selfCountry", paymentModel.selfCountry);
collection.Add("selfState", paymentModel.selfState);
collection.Add("selfPhone", paymentModel.selfPhone);
collection.Add("selfPostCode", paymentModel.selfPostCode);
collection.Add("otherAddress1", paymentModel.otherAddress1);
collection.Add("otherAddress2", paymentModel.otherAddress2);
collection.Add("otherCity", paymentModel.otherCity);
collection.Add("otherCountry", paymentModel.otherCountry);
collection.Add("otherState", paymentModel.otherState);
collection.Add("otherPhone", paymentModel.otherPhone);
collection.Add("otherPostCode", paymentModel.otherPostCode);
collection.Add("ipAddress", paymentModel.ipAddress);
collection.Add("additionalInfo", paymentModel.additionalInfo);
collection.Add("paymentMode", paymentMode);
byte[] responseBytes = webClient.UploadValues("", "POST", collection);
string response = Encoding.UTF8.GetString(responseBytes);
string decodedResponse = HttpUtility.UrlDecode(response);
JavaScriptSerializer js = new JavaScriptSerializer();
var payment = js.Deserialize<Payment>(decodedResponse);
return payment;
}
I am using this method to serialize my payment model and then send it to an external web api which then adds this data to various repositories.
Is there a more succinct method of doing this?
Ideally I want a solution that's more MVC4 and Dependency Injection (I am currently using the Unity container) friendly, I created this serializer as an interim solution whilst I researched other methods, so far this is the only one I have been able to successfully implement.
Any advice would be very much appreciated.
Can you post JSON instead of a form collection? If you can, you can use the same JavaScriptSerializer you're using to deserialize the response to serialize your Payment:
string serializedPayment = js.Serialize(paymentModel);

How to call a Post api with multiple parameters

How can i call a Post method with multiple parameters using HttpClient?
I am using the following code with a single parameter:
var paymentServicePostClient = new HttpClient();
paymentServicePostClient.BaseAddress =
new Uri(ConfigurationManager.AppSettings["PaymentServiceUri"]);
PaymentReceipt payData = SetPostParameter(card);
var paymentServiceResponse =
paymentServicePostClient.PostAsJsonAsync("api/billpayment/", payData).Result;
I need to add another parameter userid. How can i send the parameter along with the 'postData'?
WebApi POST method prototype:
public int Post(PaymentReceipt paymentReceipt,string userid)
Simply use a view model on your Web Api controller that contains both properties. So instead of:
public HttpresponseMessage Post(PaymentReceipt model, int userid)
{
...
}
use:
public HttpresponseMessage Post(PaymentReceiptViewModel model)
{
...
}
where the PaymentReceiptViewModel will obviously contain the userid property. Then you will be able to call the method normally:
var model = new PaymentReceiptViewModel()
model.PayData = ...
model.UserId = ...
var paymentServiceResponse = paymentServicePostClient
.PostAsJsonAsync("api/billpayment/", model)
.Result;
UserId should be in query string:
var paymentServiceResponse = paymentServicePostClient
.PostAsJsonAsync("api/billpayment?userId=" + userId.ToString(), payData)
.Result;
In my case my existing ViewModels don't line up very nicely with the data I want to post to my WebAPI. So, instead of creating an entire new set of model classes, I posted an anonymous type, and had my Controller accept a dynamic.
var paymentServiceResponse = paymentServicePostClient.PostAsJsonAsync("api/billpayment/", new { payData, userid }).Result;
public int Post([FromBody]dynamic model)
{
PaymentReceipt paymentReceipt = (PaymentReceipt)model.paymentReceipt;
string userid = (string)model.userid;
...
}
(I'd be curious to hear some feedback on this approach. It's definitely a lot less code.)

Categories