Sending Commands from an MVC4 site to Another - asp.net-mvc-4

I have two sites: one of them controls the other sending some commands through Web API. The idea is: the action of the controller site sends a command to the other site, gets the response and perform some business rules, without redirecting to the other site.
I have tons of examples explaining how to implement this via jQuery, but I want to make the controller post the data to the other site, instead of the view.
I found an approach at this answer: How to use System.Net.HttpClient to post a complex type?, but I want the answer for an JSON approach.
Can someone post a simple example using JSON showing how to do this?

As I didn't find a brief answer to my question, I'll post the solution I've made.
As the method uses an HttpClient method that requires async statements, the action below was implemented retuning a Task<ActionResult>. Another modification is if you're saving an object in context.
Instead of using:
context.SaveChanges();
You'll have to use:
await context.SaveChangesAsync();
The code below implements an Action from an ASP.NET MVC4 Controller:
[HttpPost]
public async Task<ActionResult> Create(MyModel model)
{
if (ModelState.IsValid)
{
// Logic to save the model.
// I usually reload saved data using something kind of the statement below:
var inserted = context.MyModels
.AsNoTracking()
.Where(m => m.SomeCondition == someVariable)
.SingleOrDefault();
// Send Command.
// APIMyModel is a simple class with public properties.
var apiModel = new APIMyModel();
apiModel.AProperty = inserted.AProperty;
apiModel.AnotherProperty = inserted.AnotherProperty;
DataContractJsonSerializer jsonSer = new DataContractJsonSerializer(typeof(APIMyModel));
// use the serializer to write the object to a MemoryStream
MemoryStream ms = new MemoryStream();
jsonSer.WriteObject(ms, apiModel);
ms.Position = 0;
//use a Stream reader to construct the StringContent (Json)
StreamReader sr = new StreamReader(ms);
// Note if the JSON is simple enough you could ignore the 5 lines above that do the serialization and construct it yourself
// then pass it as the first argument to the StringContent constructor
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, "application/json");
HttpClient aClient = new HttpClient();
Uri theUri = new Uri("http://yoursite/api/TheAPIAction");
HttpResponseMessage aResponse = await aClient.PostAsync(theUri, theContent);
if (aResponse.IsSuccessStatusCode)
{
// Success Logic. Yay!
}
else
{
// show the response status code
String failureMsg = "HTTP Status: " + aResponse.StatusCode.ToString() + " - Reason: " + aResponse.ReasonPhrase;
}
return RedirectToAction("Index");
}
// if Model is not valid, you can put your logic to reload ViewBag properties here.
}

Related

Alternate Model Binding On Controllers Post Action

EDIT: You can safely clone https://github.com/vslzl/68737969 and run the example. If you send
{
"intPropB":3
}
as POST body to: http://localhost:5000/api/Test
you'll see it binds clearly B object to A object.
#EDIT END
I'd like to implement alternate model binding using asp.net core 5 As you will see below, I have two alternative classes to bind at a single endpoint. Each request can contains only a valid A model or B model and A and B has different properties.
[HttpPost(Name = Add)]
public async Task<IActionResult> AddAsync(CancellationToken ct)
{
var aModel= new A();
var bModel= new B();
if (await TryUpdateModelAsync<A>(aModel))
{
_logger.LogDebug($"Model binded to A");
return Ok(aModel);
}
else if (await TryUpdateModelAsync<B>(bModel))
{
_logger.LogDebug($"Model binded to B");
return Ok(bModel);
}
_logger.LogDebug("Nothing binded!");
return BadRequest();
}
But this approach failed. Is there a proper way to implement this kind of solution?
By the way I'm using this to reduce complexity of my endpoints, I want to update a record partially and by doing this, each model will map the same record but with different logics.
Any suggestions will be appreciated. Thanks.
Couldn't manage to provide elegant way to do this but here is what I've done so far, It works but seems a little ugly in my opinion.
I changed the action method to this:
[HttpPost(Name = Add)]
[AllowAnonymous]
public IActionResult AddSync([FromBody] JObject body)
{
var aModel = JsonConvert.DeserializeObject<A>(body.ToString());
var bModel = JsonConvert.DeserializeObject<B>(body.ToString());
ModelState.Clear();
var aValid = TryValidateModel(aModel);
ModelState.Clear();
var bValid = TryValidateModel(bModel);
// some logic here...
return Ok(new {aModel,bModel, aValid, bValid });
}
I think It's not a good idea to interfere with ModelState this way.
But here is what I've done,
get the raw body of the request as JObject (requires Newtonsoft.Json package)
Parsed that json value to candidate objects
Check their validity via modelstate and decide which one to use.

net Core 3.1 Object null in WebApi method after PostAsJsonAsync

Im using this line to consume the API post method
var postTask = client.PostAsJsonAsync("AgregarNegocio", new StringContent(JsonConvert.SerializeObject(model).ToString(), Encoding.UTF8, "application/json"));
however when the API method is hit
public IActionResult AgregarNegocio([FromBody]NegocioViewModel model)
all the properties in model are null...
i already tried with and without [FromBody] and other solutions but none has worked yet, any suggestions?, thanks!
You need to construct your http client like this:
_client = new HttpClient { BaseAddress = new Uri("your http://my base url goes here"),
Timeout = new TimeSpan(0, 0, 0, 0, -1) };
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));//add json header
//_client.DefaultRequestHeaders.Add("Bearer", "some token goes here");
and you need to call your method like this:
var postTask = await _client.PostAsJsonAsync("AgregarNegocio", model);
make sure you call "await" on it because it is async.
NOTES:
Notice that I added MediaTypeWithQualityHeaderValue to indicate that it is json.
Also using Route usually is not a good idea... It is better to use HttPost("MyRoute") because it combined the ControllerName + Route. But it is up to you.
Try to use PostAsync instead of PostAsJsonAsync
var postTask = await client.PostAsync("AgregarNegocio", new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"));
You can use the HttpClient extension method :
https://learn.microsoft.com/en-us/previous-versions/aspnet/hh944682(v=vs.118)
PostAsJsonAsync(
this HttpClient client,
string requestUri,
T value
)
var postTask = client.PostAsJsonAsync<NegocioViewModel>("AgregarNegocio", model);
You can use PostAsync but also do not forget about using HttpClient in right way as i described in this article.

Accessing the Request.Content in the new ASP.NET vnext web api way of doing things?

I have searched high and low for this one and can't seem to find a way of accessing the Request.Content in an MVC web api. I basically am trying to create a File Service to and from Azure Blob and Table storage (table for storing metadata about the file, blob for the actual file)....
I was converting the steps in the following link, but this is where I have come unstuck
the back end I have working but can't find a way of the new unified controller passing a fileobject from json post through to the service! Any ideas would be greatly appreciated as always... or am I just going about this the wrong way?
Article here....
UPDATE: so to clarify, what I am trying to do in the new MVC 6 (where you no longer have an apicontroller to inherit from) is to access a file that has been uploaded to the api from a JSON post. That is the long and short of what I am trying to achieve.
I am trying to use the article based on the old Web API which uses the Request.Content to access it, however even if I use the WebAPIShim which they provide I still come unstuck with other objects or properties that are no longer available so I'm wondering if I need to approach it a different way, but either way, all I am trying to do is to get a file from a JSON post to a MVC 6 Web api and pass that file to my back end service....
ANY IDEAS?
Here is an example without relying on model binding.
You can always find the request data in Request.Body, or use Request.Form to get the request body as a form.
[HttpPost]
public async Task<IActionResult> UploadFile()
{
if (Request.Form.Files != null && Request.Form.Files.Count > 0)
{
var file = Request.Form.Files[0];
var contentType = file.ContentType;
using (var fileStream = file.OpenReadStream())
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream);
// do what you want with memoryStream.ToArray()
}
}
}
return new JsonResult(new { });
}
If the only thing in your request is a File you can use the IFormFile class in your action:
public FileDetails UploadSingle(IFormFile file)
{
FileDetails fileDetails;
using (var reader = new StreamReader(file.OpenReadStream()))
{
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
fileDetails = new FileDetails
{
Filename = parsedContentDisposition.FileName,
Content = fileContent
};
}
return fileDetails;
}

Marketo rest Api create lead

I have a question about this create/Update leads API, http://developers.marketo.com/documentation/rest/createupdate-leads/.
There is no sample code for C# or JAVA. Only ruby available. So I have to try it by myself. But I always get null return from the response.
Here is my code:
private async Task<CreateLeadResponseResult> CreateLead(string token)
{
string url = String.Format(marketoInstanceAddress+"/rest/v1/leads.json?access_token={0}", token);
var fullUri = new Uri(url, UriKind.Absolute);
CreateLeadResponseResult createLeadResponse = new CreateLeadResponseResult();
CreateLeadInput input = new CreateLeadInput { email = "123#123.com", lastName = "Lee", firstName = "testtesttest", postCode = "00000" };
CreateLeadInput input2 = new CreateLeadInput { email = "321#gagaga.com", lastName = "Lio", firstName = "ttttttt", postCode = "00000" };
List<CreateLeadInput> inputList = new List<CreateLeadInput>();
inputList.Add(input);
inputList.Add(input2);
CreateLeadRequest createLeadRequest = new CreateLeadRequest() { input = inputList };
JavaScriptSerializer createJsonString = new JavaScriptSerializer();
string inputJsonString = createJsonString.Serialize(createLeadRequest);
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsJsonAsync(fullUri.OriginalString, inputJsonString).ConfigureAwait(false);
// I can see the JSON string is in the message body in debugging mode.
if (response.IsSuccessStatusCode)
{
createLeadResponse = await response.Content.ReadAsAsync<CreateLeadResponseResult>();
}
else
{
if (response.StatusCode == HttpStatusCode.Forbidden)
throw new AuthenticationException("Invalid username/password combination.");
else
throw new ApplicationException("Not able to get token");
}
}
return createLeadResponse;}
//get null here.
Thank you.
-C.
The best way to debug this is to capture the exact URL, parameters and JSON that are submitted by your app and try submitting those manually via a tool like Postman (Chrome plug-in) or SOAP UI. Then you see the exact error message, which you can look up here: http://developers.marketo.com/documentation/rest/error-codes/. Based on that you can update your code. I don't know much about Java, but this is how I got my Python code to work.
Your example code was really helpful in getting my own implementation off the ground. Thanks!
After playing with it for a bit, I realized that the JavaScriptSerializer step is unnecessary since PostAsJsonAsync automatically serializes whatever object you pass to it. The double serialization prevents Marketo's API from processing the input.
Also, I agree with Jep that Postman is super helpful. But in the case of this error, Postman was working fine (using the contents of inputJsonString) but my C# code still didn't work properly. So I temporarily modified the code to return a dynamic object instead of a CreateLeadResponseResult. In debugging mode this allowed me to see fields that were discarded because they didn't fit the CreateLeadResponseResult type, which led me to the solution above.

Returning Azure BLOB from WCF service as a Stream - Do we need to close it?

I have a simple WCF service that exposes a REST endpoint, and fetches files from a BLOB container. The service returns the file as a stream. i stumbled this post about closing the stream after the response has been made :
http://devdump.wordpress.com/2008/12/07/disposing-return-values/
This is my code:
public class FileService
{
[OperationContract]
[WebGet(UriTemplate = "{*url}")]
public Stream ServeHttpRequest(string url)
{
var fileDir = Path.GetDirectoryName(url);
var fileName = Path.GetFileName(url);
var blobName = Path.Combine(fileDir, fileName);
return getBlob(blobName);
}
private Stream getBlob(string blobName)
{
var account = CloudStorageAccount.FromConfigurationSetting("ConnectingString");
var client = account.CreateCloudBlobClient();
var container = client.GetContainerReference("data");
var blob = container.GetBlobReference(blobName);
MemoryStream ms = new MemoryStream();
blob.DownloadToStream(ms);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
}
So I have two question :
Should I follow the pattern mentioned in the post ?
If I change my return type to Byte[], what are Cons/Pros ?
( My client is Silverlight 4.0, just in case it has any effect )
I'd consider changing your return type to byte[]. It's tidier.
Stream implements IDisposable, so in theory the consumer of your method will need to call your code in a using block:
using (var receivedStream = new FileService().ServeHttpRequest(someUrl))
{
// do something with the stream
}
If your client definitely needs access to something that Stream provides, then by all means go ahead and return that, but by returning a byte[] you keep control of any unmanaged resources that are hidden under the covers.
OperationBehaviorAttribute.AutoDisposeParameters is set to TRUE by default which calls dispose on all the inputs/outputs that are disposable. So everything just works.
This link :
http://devdump.wordpress.com/2008/12/07/disposing-return-values/
explains how to manually control the process.