How to receive MultipartFormData on ASP.NET C# - c#

I'm working with an iOS guy. He wants to upload images through WebAPI ASP.NET. I've to make a call that can receive those images.He said he is using AFNetworking to send data through AFMultipartFormData. My question is that how can I receive this at my end? Should I take the data in JSON format? Or what measures needs to be done for this purpose? I want to know the whole process as this is my first time working with MultipartFormData.UPDATEBased on the answer I used this:
[HttpPut]
public IHttpActionResult GetPatientFilesAction(int id, Model.Patients.PatientFiles patientFile)
{
Model.Patients.PatientFiles pFile=new Model.Patients.PatientFiles();
try
{
HttpPostedFile xmlFile = HttpContext.Current.Request.Files[0];
var fileForm = HttpContext.Current.Request.Form;
var fileKey = HttpContext.Current.Request.Form.Keys[0];
string[] jsonformat = fileForm.GetValues(fileKey);
pFile = Newtonsoft.Json.JsonConvert.DeserializeObject<Model.Patients.PatientFiles>(jsonformat[0]);
}
catch (Exception ex)
{
pFile.ErrorMessage = ex.ToString();
}
return Ok(pFile);
}
But the iOS guy got:
Request failed: unsupported media type (415)

Inside the web API controller you can access the image file using the code below :-
HttpPostedFile xmlFile = HttpContext.Current.Request.Files[0];
If you have more than one files posted, replace Files[0] with respective count 1 or 2 etc.
And then you can access the JSON using the code :
var fileForm = HttpContext.Current.Request.Form;
var fileKey = HttpContext.Current.Request.Form.Keys[0];
string[] jsonformat = fileForm.GetValues(fileKey);
var yourModel = JsonConvert.DeserializeObject<YourClassType>(jsonformat[0]);
If you have more than one json strings posted, replace jsonformat[0] with respective count 1 or 2 etc.

Related

How do I post XML to an API using ASP.NET Core 5?

I'm creating an application which talks to an external API, the external API only accepts XML posts for dealing with information. I've written most of it and I'm at a point where I need to feed the XML information which includes the DTD to the API and I cannot get it to work.
CarApiService
This is where I'm struggling, I have the required XML which I need to post and so I thought I would used the XmlDocument to load it, then, rather than using PostAsync I could use PostAsXmlAsync which is more appropriate.
public async Task<string> RequestParts(string partNumber = "0")
{
//Store any errors as a string
string errors;
//LoadXML
XmlDocument document = new();
document.LoadXml("<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE gmPartsRequest PUBLIC '-//GeneralMotors//DTD GmPartsRequest v1//EN' 'http://dtd.generalmotors.com/parts_request_version1.dtd'>" +
"<partsService version='1.4' dealershipId='xxx'></partsService>");
//Create HttpClient
var client = _clientFactory.CreateClient("external");
try
{
var postData = await client.PostAsXmlAsync(client.BaseAddress, document);
errors = null;
return something.ToString();
}
catch (Exception ex)
{
errorString = $"There was an error getting your parts data: { ex.Message }";
return errors;
}
}
There are a few things that bother me about my above code and I'm hoping someone can help:
PostAsXmlAsync returns an error when it executes and I'm not sure how to resolve it:
There was an error getting your parts data: Type
'System.Xml.XmlElement' with data contract name
'XmlElement:http://schemas.datacontract.org/2004/07/System.Xml' is not
expected. Add any types not known statically to the list of known
types - for example, by using the KnownTypeAttribute attribute or by
adding them to the list of known types passed to
DataContractSerializer.
Given that I store the API information in the startup file, is there any way to avoid having to supply the PostAsXmlAsync with the base address in this way?
Should this really be returning as a string given that I'm supposed to be posting and receiving XML?

Retrieve all contents of Zoho module via REST API c#

I am trying to get the full contents of my modules From Zoho to our local Server. The deluge code does work as it returns to me the data which is being sent via the API. However, once it reaches the API, it is null. Any idea?
Below is the deluge code:
// Create a map that holds the values of the new contact that needs to be created
evaluation_info = Map();
evaluation_info.put("BulkData",zoho.crm.getRecords("Publishers"));
data = Map();
data.put(evaluation_info);
response = invokeurl
[
url :"https://zohoapi.xxxxx.com/publisher/publish"
type :POST
parameters:data
connection:"zohowebapi"
];
info data; (data returns all the data from publishers)
Here is my ASP.NET core restful API. It does ping it and create the file but the content of the file is null.
Route("[controller]")]
[ApiController]
public class PublisherController : ControllerBase
{
[HttpGet("[action]"), HttpPost("[action]")]
public void Publish(string data)
{
(it's already null when it comes here. why?)
string JSONresult = JsonConvert.SerializeObject(data);
string path = #"C:\storage\journalytics_evaluationsv2.json";
using (var file = new StreamWriter(path, true))
{
file.WriteLine(JSONresult.ToString());
file.Close();
}
}
}
}
What am I missing? Thank you
After contacting Zoho support, the solution he offered was to loop through the data in order to get all the contents from a module (if they are more than 200 records. With the solution provided, one doesn't really need the deluge code anymore as long as you have the ZOHO api set to your account in code. This was my final solution. This solution is not scalable at all. It's best to work with the BULK CSV.
// Our own ZohoAPI which lets us connect and authenticate etc. Yours may look slightly different
ZohoApi zohoApi = new ZohoApi();
zohoApi.Initialize();
ZCRMRestClient restClient = ZCRMRestClient.GetInstance();
var allMedicalJournals = new List<ZCRMRecord>();
for (int i = 1; i <= 30; i++)
{
List<ZCRMRecord> accountAccessRecords2 =
restClient.GetModuleInstance("Journals").SearchByCriteria("Tag:equals:MedicalSet", i, 200).BulkData.ToList();
foreach (var newData in accountAccessRecords2)
allMedicalJournals.Add(newData);
}

How to create new AutoML DataSet for simple classification (C#)

As part of ML automation process I want to dynamically create new AutoML model. I'm using C# (.net framework) and Google.Cloud.AutoML.V1.
After trying to run CreateDataSet code:
var autoMlClient = AutoMlClient.Create();
var parent = LocationName.FromProjectLocation(_projectId, _locationId);
var dataset = new Google.Cloud.AutoML.V1.Dataset();
dataset.DisplayName = "NewDataSet";
var response = autoMlClient.CreateDataset(parent, dataset);
I get the following error:
Field: dataset.dataset_metadata; Message: Required field not set
According to this user manual I should set Dataset Metadata Type, but the list contains only specific types of classifications (Translation/ImageClassifications etc.), I can't find a simple classification type.
How do I create a simple classification data set with the API ? in the AutoML UI its just with a simple button click ("NEW DATASET") - and have to provide only name & region - no classification type.
I also tried to set:
dataset.TextClassificationDatasetMetadata =
new TextClassificationDatasetMetadata() { ClassificationType = ClassificationType.Multiclass };
But I was unable to import data to it (got too many errors of invalid inputs from the input CSV file), I guess its related to the reason that the input format is not suitable for Text Classification.
UPDATE
I've just notice that the Nuget works with AutoML v1 but v1 beta does contains TablesDatasetMetadata Dataset Metadata Type for normal classifications. I'm speechless.
I also experienced this scenario today while creating a dataset using the NodeJS client. Since the Google AutoML table service is in the beta level you need to use the beta version of the AutoML client. In the Google cloud documentation they have used the beta client to create a dataset.
In NodeJS importing the beta version require('#google-cloud/automl').v1beta1.AutoMlClient instead of importing the normal version (v1) require('#google-cloud/automl').v1 worked for me to successfully execute the create dataset functionality.
In C# you can achieve the same through a POST request. Hope this helps :)
After #RajithaWarusavitarana comment, and my last question update , below is the code that did the trick. The token is being generated by GoogleClientAPI nuget and AutoML is handled by REST.
string GcpGlobalEndPointUrl = "https://automl.googleapis.com";
string GcpGlobalLocation = "us-central1"; // api "parent" parameter
public string GetToken(string jsonFilePath)
{
var serviceAccountCredentialFileContents = System.IO.File.ReadAllText(jsonFilePath);
var credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(serviceAccountCredentialFileContents);
var initializer = new ServiceAccountCredential.Initializer(credentialParameters.ClientEmail)
{
Scopes = new List<string> { "https://www.googleapis.com/auth/cloud-platform" }
};
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(credentialParameters.PrivateKey));
string accessToken = cred.GetAccessTokenForRequestAsync("https://oauth2.googleapis.com/token").Result;
return accessToken;
}
public void GetDataSetList(string projectId, string token)
{
var restClient = new RestClient(GcpGlobalEndPointUrl);
var createDataSetReqUrl = $"v1beta1/projects/{projectId}/locations/{GcpGlobalLocation}/datasets";
var createDataSetReq = new RestRequest(createDataSetReqUrl, Method.GET);
createDataSetReq.AddHeader("Authorization", $"Bearer {token}");
var createDatasetResponse = restClient.Execute(createDataSetReq);
createDatasetResponse.Dump();
}
I took the token generation code from google-api-dotnet-client Test File

Moving files with Google Drive API v3

Im trying to move a file from one folder to another using the Google Drive API v3. I found documentation how to this here. I used the .NET sample code from the documentation page and created a method that looks like this:
public ActionResult MoveFile(string fileToMove, string destination)
{
DriveService service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = <USER CREDENTIAL>,
ApplicationName = "APPNAME"
});
var searchFiles = service.Files.List();
searchFiles.Corpus = FilesResource.ListRequest.CorpusEnum.User;
searchFiles.Q = "name = '" + fileToMove + "'";
searchFiles.Fields = "files(*)";
string fileToMoveId = searchFiles.Execute().Files[0].Id;
searchFiles.Q = "name = '" + destination + "'";
string destinationId = searchFiles.Execute().Files[0].Id;
//Code used from documentation
// Retrieve the existing parents to remove
var getRequest = service.Files.Get(fileToMoveId);
getRequest.Fields = "parents";
var file = getRequest.Execute();
var previousParents = String.Join(",", file.Parents);
// Move the file to the new folder
var updateRequest = service.Files.Update(file, fileToMoveId);
updateRequest.Fields = "id, parents";
updateRequest.AddParents = destinationId;
updateRequest.RemoveParents = previousParents;
file = updateRequest.Execute();
return RedirectToAction("Files", new {folderId = destinationId});
}
When I execute this code I get the following error:
The parents field is not directly writable in update requests. Use the
addParents and removeParents parameters instead.
The error doesn't really makes sense to me because this code sample came from the documentation page itself. I can't figure out what other paramters they mean. What addParents and removeParents parameters do they mean? Are updateRequest.AddParents and updateRequest.RemoveParents not the right parameters?
Ok here is the problem.
var updateRequest = service.Files.Update(file, fileToMoveId);
The method is requiring that you send a body of a file to be updated. This normally makes sense as any changes you want to make you can add to the body.
Now the problem you are having is that you got your file from a file.get. Which is totally normal. This is how you should be doing it. THe problem is there are some fields in that file that you cant update. So by sending the full file the API is rejecting your update. If you check Files: update under Request body you will see which fiends are updateable.
Issue:
Now this is either a problem with the client library or the API I am going to have to track down a few people at Google to see which is the case.
Fix:
I did some testing and sending an empty file object as the body works just fine. The file is moved.
var updateRequest = service.Files.Update(new Google.Apis.Drive.v3.Data.File(), fileToMove.Id);
updateRequest.AddParents = directoryToMove.Id;
updateRequest.RemoveParents = fileToMove.Parents[0];
var movedFile = updateRequest.Execute();
This method works well when working in your own drive, but not in a team drive where a file (folder) can only have 1 parent strictly. I do not have the solution in a team drive

ASP.Net Web API - Get POSTed file synchronously?

Is there a way to synchronously process an uploaded file POSTed to a controller in the ASP.Net Web API?
I've tried the process Microsoft proposed here, and it works as described, but I'd like to return something other than a Task<> from the Controller method in order to match the rest of my RESTful API.
Basically, I'm wondering if there is there any way to make this work:
public MyMugshotClass PostNewMugshot(MugshotData data){
//get the POSTed file from the mime/multipart stream <--can't figure this out
//save the file somewhere
//Update database with other data that was POSTed
//return a response
}
Again, I have made the asynchronous example work but am hoping for a way to process the uploaded file before responding to the client.
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var appData = HostingEnvironment.MapPath("~/App_Data");
var folder = Path.Combine(appData, Guid.NewGuid().ToString());
Directory.CreateDirectory(folder);
var provider = new MultipartFormDataStreamProvider(folder);
var result = await Request.Content.ReadAsMultipartAsync(provider);
if (result.FileData.Count < 1)
{
// no files were uploaded at all
// TODO: here you could return an error message to the client if you want
}
// at this stage all files that were uploaded by the user will be
// stored inside the folder we specified without us needing to do
// any additional steps
// we can now read some additional FormData
string caption = result.FormData["caption"];
// TODO: update your database with the other data that was posted
return Request.CreateResponse(HttpStatusCode.OK, "thanks for uploading");
}
}
You might notice that the uploaded files are stored inside the specified folder with names that might look like this: BodyPart_beddf4a5-04c9-4376-974e-4e32952426ab. That's a deliberate choice that the Web API team made that you could override if you want.

Categories