Android use WCF(webservice) - c#

I am a freshman in android development. Today I use KSOAP2 to use WCF which I have finished on the server. Firstly, I try to use the WCF in windows form. It runs OK and the data have been upload. Then I use WCF with KSOAP2. The string cannot send well and the error is :
Method threw 'org.ksoap2.SoapFault' exception.
The detial of the error is:
a:InternalServiceFault
Value cannot be null.
Parameter name: s
I do not have the parameter named 's' in server program and android program. The vision of .NET is framework 4.0.
If I use .NET framework 4.5, The android can use it with KSOAP2.
However, I must use 4.0
How can I solve this problems?
Cheers.
The code in android is shown below:
transferthread = new Thread(new Runnable() {
#Override
public void run() {
while (true)
{
SoapObject request = new SoapObject(NAMESPACE,METHOD_NAME);
int a = 1;
request.addProperty("userid",a);
request.addProperty("healthData",info);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
envelope.dotNet = true;
envelope.bodyOut = request;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.debug = true;
try {
androidHttpTransport.call(SOAP_ACTION, envelope);
// final SoapPrimitive result = (SoapPrimitive)envelope.getResponse();
envelope.getResponse();
Log.e("str",envelope.getResponse().toString());
a=1;
//Log.e("aaa",envelope.getResponse().toString());
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
transferthread.start();
I think is the ksoap2 problem.

My answer is another alternative solution for your question about how to implement Android use WCF. I used to give a try on KSOAP2. For some reason (I forgot) I give up using it.
This is what I am doing to do the same thing.
You can install Wizdler(Chrome-extension) to generate your envelope. and copy paste to envelope code.
Call getYourData in your asynctask.
public ArrayList<YourData> getYourData(String username, String password) {
ArrayList<YourData> resultList;
String resultData;
try {
//Put your envelope here.
final String envelope = "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <Body>\n" +
" <Something xmlns=\"http://www.example.com/RemoteServices/url/\">\n" +
" <!-- Optional -->\n" +
" <request>\n" +
" <Authentication xmlns=\"http://schemas.datacontract.org/RemoteServicesv2.Core\">\n" +
" <Password>" +
password +
"</Password>\n" +
" <Username>" +
username +
"</Username>\n"
" </Something>\n" +
" </Body>\n" +
"</Envelope>";
resultData = CallWebService(URL, "http://www.example.com/webserviceURL", envelope);
Log.e("resultData for Something", ""+resultData);
resultList = parseFunction(resultData);
} catch (Exception e) {
resultList = null;
}
return resultList;
}
// How to send SOAP envelope to web service
private String CallWebService(String url,
String soapAction,
String envelope) {
final DefaultHttpClient httpClient = new DefaultHttpClient();
// request parameters
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 20000);
HttpConnectionParams.setSoTimeout(params, 25000);
// set parameter
HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), true);
// POST the envelope
HttpPost httppost = new HttpPost(url);
// add headers
httppost.setHeader("soapaction", soapAction);
httppost.setHeader("Content-Type", "text/xml; charset=utf-8");
String responseString = "";
try {
// the entity holds the request
HttpEntity entity = new StringEntity(envelope);
httppost.setEntity(entity);
// Response handler
ResponseHandler<String> rh = new ResponseHandler<String>() {
// invoked when client receives response
public String handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
// get response entity
HttpEntity entity = response.getEntity();
// read the response as byte array
StringBuffer out = new StringBuffer();
byte[] b = EntityUtils.toByteArray(entity);
// write the response byte array to a string buffer
out.append(new String(b, 0, b.length));
return out.toString();
}
};
responseString = httpClient.execute(httppost, rh);
} catch (Exception e) {
Log.v("exception", e.toString());
}
String xml = responseString.toString();
// close the connection
System.out.println("xml file ------" + xml);
httpClient.getConnectionManager().shutdown();
return responseString;
}

Related

Need help to find where does API takes data from in ASP.NET MVC

I am a begginer and i work in a MVC project which I cant understand it well yet.
I can't understand where does the API takes data from when I try to connect in Login Screen.
It doesn't use Entity Framework and there isn't a json with the data.
When I enter Id and Pass it calls an API (GetAPIResponse) which somehow finds that is correct.
Need help to understand the code and the logic behind it.
LoginBL class contains:
public bool IsAuthenticated(LoginEntity user)
{
string url = string.Empty;
string callType = string.Empty;
string server = string.Empty;
try
{
// get URL, Call type, Server from config file
url = ConfigurationManager.AppSettings["login_url"].ToString();
callType = ConfigurationManager.AppSettings["calltype"].ToString();
server = ConfigurationManager.AppSettings["server"].ToString();
// Encrypt password
string password = Scrambler.GenerateMD5Hash(user.Password);
// Prepare content for the POST request
string content = #"calltype=" + callType + "&server=" + server + "&user=" + user.UserName + "&pass=" + password + "";
Debug.WriteLine("Callcenter login url: " + content);
HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);
LoginResponseEntity obj = new LoginResponseEntity();
obj = JsonConvert.DeserializeObject<LoginResponseEntity>(json_list.Content.ReadAsStringAsync().Result);
Debug.WriteLine(callType + " Response: " + json_list.Content.ReadAsStringAsync().Result);
//if API resultCode return 0 then user details and token save in session for further use
if (obj.ResultCode == 0)
{
int restrict = obj.UserInfo.RestrictCallType.HasValue ?
obj.UserInfo.RestrictCallType.Value : 0;
HttpContext.Current.Session["user_id"] = obj.UserInfo.usr_id;
HttpContext.Current.Session["user_name"] = obj.UserInfo.usr_username;
HttpContext.Current.Session["user_group_id"] = obj.UserInfo.UserGroupID;
HttpContext.Current.Session["groupid"] = obj.UserInfo.groupid;
HttpContext.Current.Session["token"] = obj.Token;
HttpContext.Current.Session["web_server_url"] = obj.ServerInfo.web_server_url;
HttpContext.Current.Session["centerX"] = obj.ServerInfo.DefaultGeoX;
HttpContext.Current.Session["centerY"] = obj.ServerInfo.DefaultGeoY;
HttpContext.Current.Session["dateFormat"] = obj.ServerInfo.dateFormat;
HttpContext.Current.Session["currency"] = obj.ServerInfo.currency;
HttpContext.Current.Session["customer_img"] = obj.ServerInfo.customer_img;
HttpContext.Current.Session["groups"] = obj.groups;
HttpContext.Current.Session["restrict_call_type"] = restrict ;
Debug.WriteLine("obj.UserInfo.UserGroupID " + obj.UserInfo.UserGroupID);
Debug.WriteLine("obj.UserInfo.groups " + obj.groups);
//HttpContext.Current.Session["defaultLanguage"] = obj.ServerInfo.defaultLanguage;
HttpCookie cookie = new HttpCookie("Login");
// if remember me checked then user name and password stored in cookie else cookes is expired
if (user.RememberMe)
{
cookie.Values.Add("user_name", obj.UserInfo.usr_username);
cookie.Values.Add("pwd", user.Password);
cookie.Expires = DateTime.Now.AddDays(15);
HttpContext.Current.Response.Cookies.Add(cookie);
}
else
{
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
return true;
}
else
{
//ResultCode -5 :Invalid Login ,-1:Database Error ,-2:Server Error ,-3:Invalid Parameter specified ,-4:Invalid Token
return false;
}
}
catch
{
throw;
}
finally
{
url = string.Empty;
callType = string.Empty;
server = string.Empty;
}
}
Okay here after converts pass to MD5 creates a "string content" with the information given.
Then in next line (HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);) calls the API with the url and content as parameters where it finds if the data exists.
API code:
public static HttpResponseMessage GetAPIResponse(string url, string content)
{
StringBuilder traceLog = null;
HttpContent httpContent = null;
try
{
traceLog = new StringBuilder();
traceLog.AppendLine("Start: BusinessLayer getAPIResponse() Request Data:- " + DateTime.Now + "URL = " + url + "&content = " + httpContent);
using (HttpClient client = new HttpClient())
{
httpContent = new StringContent(content);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var resp = client.PostAsync(url, httpContent).Result;
Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);
traceLog.AppendLine("End: BusinessLayer getAPIResponse() call completed HttpResponseMessage received");
return resp;
}
}
catch
{
throw;
}
finally
{
traceLog = null;
httpContent.Dispose();
url = string.Empty;
content = string.Empty;
}
}
In the following line, console prints the result that I cant understand where it cames from (Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);)
Sorry for the confusion , I am in my first job with zero work experience and I am called to learn how this works alone without proper education on ASP.NET from them.
You will not go very far without debbugger. Learn how to debug in Visual Studio (YouTube tutorials might be fastest way). Place debug points along critical points in code (for example moment when client sends and receives response is line var resp = client.PostAsync...) and check variables.
Url for API server is actually defined in the line
url = ConfigurationManager.AppSettings["login_url"].ToString();
ConfigurationManager means Web.config file, check it's appSettings section for login_url entry, there is your url.
Btw, using (HttpClient client = new HttpClient()) is not a good way to use a HttpClient and will lead to port exhaustion. It's ok for small number of requests, but for larger ones you must reuse it, or use HttpClientFactory (for .NET Core).

Handling Parallel POST to API

I have a API Post method that takes is a string which represents a Bae64 string of bytes from a word document that the API converts to PDF. My test client sends multiple documents, each on its own task, to the API to be converted. The problem is with concurrency and writing the files. I end up with a file in use since the calls are parallel. I have tried a lot of different way to block the conversion process until a document is converted but none of it has worked. Everything works fine if it's jsut a single file being converted but as soon as it's 2 or more, the problem happens. Can anyone guide me in the correct direction to solve this issue?
API:
[HttpPost]
public async Task<SimpleResponse> Post([FromBody]string request)
{
var response = new SimpleResponse();
Task t = Task.Factory.StartNew(async () =>
{
try
{
Converter convert = new Converter();
var result = await convert.CovertDocToPDF(request, WebConfigurationManager.AppSettings["tempDocPath"], WebConfigurationManager.AppSettings["tempPdfPath"]);
response.Result = result;
response.Success = true;
}
catch (Exception ex)
{
response.Exception = ex;
response.Success = false;
response.Errors = new List<string>();
response.Errors.Add(string.Format("{0}, {1}", ex.Message, ex.InnerException?.Message ?? ""));
}
});
t.Wait();
return response;
}
Conversion code
public Task<string> CovertDocToPDF(string blob, string tempDocPath, string tempPdfPath)
{
try
{
// Convert blob back to bytes
byte[] bte = Convert.FromBase64String(blob);
// Process and return blob
return Process(bte, tempDocPath, tempPdfPath);
}
catch (Exception Ex)
{
throw Ex;
}
}
private async Task<string> Process(byte[] bytes, string tempDocPath, string tempPdfPath)
{
try
{
string rs = RandomString(16, true);
tempDocPath = tempDocPath + rs + ".docx";
tempPdfPath = tempPdfPath + rs + ".pdf";
// This is where the problem happens with concurrent calls. I added
// the try catch when the file is in use to generate a new
// filename but the error still happens.
try
{
// Create a temp file
File.WriteAllBytes(tempDocPath, bytes);
}
catch (Exception Ex)
{
rs = RandomString(16, true);
tempDocPath = tempDocPath + rs + ".docx";
tempPdfPath = tempPdfPath + rs + ".pdf";
File.WriteAllBytes(tempDocPath, bytes);
}
word.Application app = new word.Application();
word.Document doc = app.Documents.Open(tempDocPath);
doc.SaveAs2(tempPdfPath, word.WdSaveFormat.wdFormatPDF);
doc.Close();
app.Quit(); // Clean up the word instance.
// Need the bytes to return the blob
byte[] pdfFileBytes = File.ReadAllBytes(tempPdfPath);
// Delete temp files
File.Delete(tempDocPath);
File.Delete(tempPdfPath);
// return blob
return Convert.ToBase64String(pdfFileBytes);
}
catch (Exception Ex)
{
throw Ex;
}
}
Client:
public async void btnConvert_Click(object sender, EventArgs e)
{
var response = await StartConvert();
foreach (SimpleResponse sr in response)
{
if (sr.Success)
{
byte[] bte = Convert.FromBase64String(sr.Result.ToString());
string rs = RandomString(16, true);
string pdfFileName = tempPdfPath + rs + ".pdf";
if (File.Exists(pdfFileName))
{
File.Delete(pdfFileName);
}
System.IO.File.WriteAllBytes(pdfFileName, bte);
}
else
{
}
}
}
private async Task<IEnumerable<SimpleResponse>> StartConvert()
{
var tasks = new List<Task<SimpleResponse>>();
foreach (string s in docPaths)
{
byte[] bte = File.ReadAllBytes(s);
tasks.Add(ConvertDocuments(Convert.ToBase64String(bte)));
}
return (await Task.WhenAll(tasks));
}
private async Task<SimpleResponse> ConvertDocuments(string requests)
{
using (var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true }))
{
client.BaseAddress = new Uri(BaseApiUrl);
client.DefaultRequestHeaders.Add("Accept", "application/json");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));//application/json
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, BaseApiUrl + ApiUrl);
var data = JsonConvert.SerializeObject(requests);
request.Content = new StringContent(data, Encoding.UTF8, "application/json");
HttpResponseMessage response1 = await client.PostAsync(BaseApiUrl + ApiUrl, request.Content).ConfigureAwait(false);
var response = JsonConvert.DeserializeObject<SimpleResponse>(await response1.Content.ReadAsStringAsync());
return response;
}
}
Random String Generator
public string RandomString(int size, bool lowerCase = false)
{
var builder = new StringBuilder(size);
// Unicode/ASCII Letters are divided into two blocks
// (Letters 65–90 / 97–122):
// The first group containing the uppercase letters and
// the second group containing the lowercase.
// char is a single Unicode character
char offset = lowerCase ? 'a' : 'A';
const int lettersOffset = 26; // A...Z or a..z: length = 26
for (var i = 0; i < size; i++)
{
var #char = (char)_random.Next(offset, offset + lettersOffset);
builder.Append(#char);
}
return lowerCase ? builder.ToString().ToLower() : builder.ToString();
}
First, get rid of Task.Factory.StartNew ... t.Wait() - you don't need an additional task, the root level method is async and your blocking Wait just spoils the benefits of async by blocking synchronously.
Second, like a comment suggested above, the file name random string generator is most likely to be not really random. Either do not supply anything to the seed value of your pseudo-random gen, or use something like Environment.TickCount which should be sufficient for this. Guid.NewGuid() will work too.
Another good option for temp files is Path.GetTempFileName (also generates an empty file for you): https://learn.microsoft.com/en-us/dotnet/api/system.io.path.gettempfilename?view=netstandard-2.0
[HttpPost]
public async Task<SimpleResponse> Post([FromBody]string request)
{
var response = new SimpleResponse();
try
{
...
var result = await convert.CovertDocToPDF(...);
...
}
catch (Exception ex)
{
...
}
return response;
}
Based on your code it seems that you have a "faulty" random string generator for file name (I would say _random.Next is a suspect, possibly some locking and/or "app wide" instance could fix the issue). You can use Guid.NewGuid to create random part of file name (which in theory can have collisions also but in most practical cases should be fine) or Path.GetTempFileName:
var rs = Guid.NewGuid().ToString("N");
tempDocPath = tempDocPath + rs + ".docx";
tempPdfPath = tempPdfPath + rs + ".pdf";

Posting multipart request containing jpg and json causes interal server error and win32 status 64 on IIS

My winforms app sends PUT/POST requests to asp.net web api service. Mostly it sends json object as request's content and those request work well. Sometimes, when it needs to send jpg along with json object, so it creates multiPart request where jpg is content and json is passed in url, like so:
example.com/EditPart?id=193&PartJson=<serialized json object>
Here's full definition of the method sending the request:
public async void Edit(string attachmentPath)
{
using (var client = new HttpClient())
{
var serializedProduct = JsonConvert.SerializeObject(this, new JsonSerializerSettings { DateFormatString = "yyyy-MM-ddTHH:mm:ss.fff" });
string url = Secrets.ApiAddress + $"Edit{typeof(T).Name}?token=" + Secrets.TenantToken + $"&id={this.Id}&UserId={RuntimeSettings.UserId}" + $"&{typeof(T).Name}Json={serializedProduct}";
MultipartFormDataContent content = new MultipartFormDataContent();
try
{
using (var fileStream = System.IO.File.OpenRead(attachmentPath))
{
var fileInfo = new FileInfo(attachmentPath);
StreamContent fcontent = new StreamContent(fileStream);
fcontent.Headers.Add("Content-Type", "application/octet-stream");
fcontent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileInfo.Name + "\"");
content.Add(fcontent, "file", fileInfo.Name);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var result = await client.PutAsync(url, content);//<--stops here
if (result.IsSuccessStatusCode)
{
MessageBox.Show("Edycja zakończona powodzeniem!");
}
else
{
MessageBox.Show("Serwer zwrócił błąd przy próbie edycji. Wiadomość: " + result.ReasonPhrase);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Problem z wysyłką żądania do serwera. Wiadomość: " + ex.Message + ". " + ex.InnerException.Message, "Błąd żądania", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
It goes to await client.PutAsync(url, content); and then straight to exception saying: An error occurred while sending the request. The underlaying connection was closed. An unexpected error occurred on receive.
When I check in IIS logs, I see the request gets properly to the server, but ends with status 500 and win32 status 64.. I even put logging with NLog to EditPart method, but it never fires.. It looks like the method isn't called at all, but of course from IIS logs I know it is.
Here's complete EditPart definition on asp.net web api:
[HttpPut]
[Route("EditPart")]
[ResponseType(typeof(void))]
public HttpResponseMessage EditPart(string token, int id, int UserId, string PartJson)
{
try
{
JavaScriptSerializer jss = new JavaScriptSerializer();
JDE_Parts item = jss.Deserialize<JDE_Parts>(PartJson);
try
{
var items = db.JDE_Parts.Where(u => u.PartId == id);
if (items.Any())
{
Logger.Info("EditPart: Znalazłem odpowiednią część. Przystępuję do edycji Id={id}, UserId={UserId}", id, UserId);
JDE_Parts orgItem = items.FirstOrDefault();
//handle image
var httpRequest = HttpContext.Current.Request;
if (httpRequest.ContentLength > 0)
{
//there's a new content
if (httpRequest.ContentLength > Static.RuntimeSettings.MaxFileContentLength)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{item.Name} przekracza dopuszczalną wielość pliku ({Static.RuntimeSettings.MaxFileContentLength} MB) i został odrzucony");
}
var postedFile = httpRequest.Files[0];
string filePath = "";
if (postedFile != null && postedFile.ContentLength > 0)
{
Logger.Info("EditPart: Znaleziono nowe pliki. Przystępuję do zapisu na dysku. Id={id}, UserId={UserId}", id, UserId);
var ext = postedFile.FileName.Substring(postedFile.FileName.LastIndexOf('.'));
filePath = $"{Static.RuntimeSettings.Path2Files}{item.Token + ext.ToLower()}";
string oFileName = db.JDE_Parts.Where(p => p.PartId == id).FirstOrDefault().Image;
if (!string.IsNullOrEmpty(oFileName))
{
// There was a file, must delete it first
Logger.Info("EditPart: Istnieją poprzednie pliki pod tą nazwą. Przystępuję do usuwania. Id={id}, UserId={UserId}", id, UserId);
System.IO.File.Delete(Path.Combine(RuntimeSettings.Path2Files, oFileName));
System.IO.File.Delete(Path.Combine(RuntimeSettings.Path2Thumbs, oFileName));
}
postedFile.SaveAs(filePath);
Logger.Info("EditPart: Zapisano pliki. Przystępuję do utworzenia miniatury.. Id={id}, UserId={UserId}", id, UserId);
Static.Utilities.ProduceThumbnail(filePath);
item.Image = item.Token + ext.ToLower();
}
}
try
{
Logger.Info("EditPart: Przystępuję do zapisu zmian w bazie danych. Id={id}, UserId={UserId}", id, UserId);
db.Entry(orgItem).CurrentValues.SetValues(item);
db.Entry(orgItem).State = EntityState.Modified;
db.SaveChanges();
Logger.Info("EditPart: Zapisano zmiany w bazie. Id={id}, UserId={UserId}", id, UserId);
}
catch (Exception ex)
{
Logger.Error("Błąd w EditPart. Id={id}, UserId={UserId}. Szczegóły: {Message}, nowa wartość: {item}", id, UserId, ex.ToString(), item);
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
}
catch (Exception ex)
{
Logger.Error("Błąd w EditPart. Id={id}, UserId={UserId}. Szczegóły: {Message}, nowa wartość: {item}", id, UserId, ex.ToString(), item);
return Request.CreateResponse(HttpStatusCode.NoContent);
}
}
catch (Exception ex)
{
Logger.Error("Błąd w EditPart. Id={id}, UserId={UserId}. Szczegóły: {Message}", id, UserId, ex.ToString());
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
return Request.CreateResponse(HttpStatusCode.NoContent);
}
What's weird is that all this worked perfectly well for months, until some time ago it stopped.. Also, when I debug the asp.net application on my machine, the request runs without any problem.. What else I can do to trace this issue?
When you debug the asp.net api application, it can run without any problem. This shows that there is no problem with the api application.
But the status in IIS logs is 500, errors greater than 500 are generally caused by the server, the client is normal. This contradicts the above conclusion that the api is no problem.
Since I don’t know the version of .net you are using, I’m not sure whether to use TLS 1.0, 1.1 or 1.2. Different versions of .net target different TLS, the latest 4.6 currently supports 1.2. So the most safe and effective way is to set all TLS and SSL.
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;
It turned out that the problem was single line in my Edit method in the client app. After changing fcontent.Headers.Add("Content-Type", "application/octet-stream") to fcontent.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(fileInfo.Name)) it works flawlessly. In other words, my request wasn't even sent to the server. What's puzzling, though, is the fact that the same code had been working for months and then stopped..
public async void Edit(string attachmentPath)
{
using (var client = new HttpClient())
{
var serializedProduct = JsonConvert.SerializeObject(this, new JsonSerializerSettings { DateFormatString = "yyyy-MM-ddTHH:mm:ss.fff" });
string url = Secrets.ApiAddress + $"Edit{typeof(T).Name}?token=" + Secrets.TenantToken + $"&id={this.Id}&UserId={RuntimeSettings.UserId}" + $"&{typeof(T).Name}Json={serializedProduct}";
MultipartFormDataContent content = new MultipartFormDataContent();
try
{
using (var fileStream = System.IO.File.OpenRead(attachmentPath))
{
var fileInfo = new FileInfo(attachmentPath);
StreamContent fcontent = new StreamContent(fileStream);
fcontent.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(fileInfo.Name)); //fcontent.Headers.Add("Content-Type", "application/octet-stream");
fcontent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileInfo.Name + "\"");
content.Add(fcontent, "file", fileInfo.Name);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var result = await client.PutAsync(url, content);//<--stops here
if (result.IsSuccessStatusCode)
{
MessageBox.Show("Edycja zakończona powodzeniem!");
}
else
{
MessageBox.Show("Serwer zwrócił błąd przy próbie edycji. Wiadomość: " + result.ReasonPhrase);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Problem z wysyłką żądania do serwera. Wiadomość: " + ex.Message + ". " + ex.InnerException.Message, "Błąd żądania", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}

Can I read messages from gmail using gmail api and c#?

I want read all messages in my gmail account using c# and gmail api.
Can I do this?
I read a lot of articles in Gmail API, but i couldn't read messages.
Also I want to read a body of messages or header.
I will be very glad if someone can help me :)
I use this code snippet:
public static List<Message> ListMessages(GmailService service, String userId)
{
List<Message> result = new List<Message>();
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List(userId);
do
{
try
{
ListMessagesResponse response = request.Execute();
result.AddRange(response.Messages);
request.PageToken = response.NextPageToken;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
And this:
foreach (var item in ListMessages(service,"me"))
MessageBox.Show(item.Snippet);
But in a result I have empty message box.
It works for me
var inboxlistRequest = service.Users.Messages.List("your-email-address");
inboxlistRequest.LabelIds = "INBOX";
inboxlistRequest.IncludeSpamTrash = false;
//get our emails
var emailListResponse = inboxlistRequest.Execute();
foreach (var mail in emailListResponse.Messages)
{
var mailId = mail.Id;
var threadId = mail.ThreadId;
Message message = service.Users.Messages.Get("your-email-address", mailId).Execute();
Console.WriteLine(message.Snippet);
}
Yes you should have no issue doing what you say. I would suggest reading the documentation a bit more.
First you have to authenticate - the following shows how to do this with a service account (more details here https://developers.google.com/gmail/api/auth/web-server)
serviceAccountEmail = primaryLink.serviceEmailAddress;
certificate = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "certs//" + primaryLink.certificate, primaryLink.certificatePassword, X509KeyStorageFlags.Exportable);
try
{
credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = z.us.emailAccount,
Scopes = new[] { "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://mail.google.com/" }
}.FromCertificate(certificate));
if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
{
gs = new GmailService(
new Google.Apis.Services.BaseClientService.Initializer()
{
ApplicationName = "Example",
HttpClientInitializer = credential
});
}
else
{
throw new Exception("gmail authentication Error.");
}
}
catch (Exception ex)
{
throw ex;
}
ListMessagesResponse respM = reqM.Execute();
if (respM.Messages != null)
{
foreach (Message m in respM.Messages)
{}
}
Once you have the message List you can iterate through the messages and either use a MIME parser or traverse the message structure to get the header, body etc.
There are lots of posts in this forum which go through how to do that.
I searched for a full example, without luck, but this is my working example. Based on https://developers.google.com/gmail/api/guides
Authenticate : https://developers.google.com/gmail/api/auth/web-server
get ALL emails
loop through all emails by Id, and request message etc.
here is the code snippet to get the first email's atachments, but you can simply loop over all foundIds to get all emails, and use message.snippet to get body :
List<string> foundIds = new List<string>();
string outputDir = "/EXAMPLE/EXAMPLE/"; // your preferred Dir to save attachments to
List<Google.Apis.Gmail.v1.Data.Thread> resultThread = new List<Google.Apis.Gmail.v1.Data.Thread>();
UsersResource.ThreadsResource.ListRequest requestThread = service.Users.Threads.List("me");
do
{
try
{
ListThreadsResponse responseThread = requestThread.Execute();
resultThread.AddRange(responseThread.Threads);
foreach (var item in responseThread.Threads )
{
foundIds.Add(item.Id);
}
requestThread.PageToken = responseThread.NextPageToken;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
} while (!String.IsNullOrEmpty(requestThread.PageToken));
try
{
Message message = service.Users.Messages.Get("me", foundIds[0]).Execute();
IList<MessagePart> parts = message.Payload.Parts;
foreach (MessagePart part in parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
String attId = part.Body.AttachmentId;
MessagePartBody attachPart = service.Users.Messages.Attachments.Get("me", foundIds[0], attId).Execute();
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
String attachData = attachPart.Data.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
File.WriteAllBytes(Path.Combine(outputDir, part.Filename), data);
}
}
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
I would suggest taking a look at this article on how to handle the actual response structure. It can be somewhat complex with the way the Gmail API gives the results back to you.
https://sigparser.com/developers/email-parsing/gmail-api/
#BlackCat, Your ListMessages looks good. Now, to get the message body, you have to decode MimeType "text/plain" or "text/html". For eg:
public static void GetBody(GmailService service, String userId, String messageId, String outputDir)
{
try
{
Message message = service.Users.Messages.Get(userId, messageId).Execute();
Console.WriteLine(message.InternalDate);
if (message.Payload.MimeType == "text/plain")
{
byte[] data = FromBase64ForUrlString(message.Payload.Body.Data);
string decodedString = Encoding.UTF8.GetString(data);
Console.WriteLine(decodedString);
}
else
{
IList<MessagePart> parts = message.Payload.Parts;
if (parts != null && parts.Count > 0)
{
foreach (MessagePart part in parts)
{
if (part.MimeType == "text/html")
{
byte[] data = FromBase64ForUrlString(part.Body.Data);
string decodedString = Encoding.UTF8.GetString(data);
Console.WriteLine(decodedString);
}
}
}
}
Console.WriteLine("----");
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
}

WCF Streaming File Transfer ON .NET 4

I need a good example on WCF Streaming File Transfer.
I have found several and tried them but the posts are old and I am wokding on .net 4 and IIS 7 so there are some problems.
Can you gives me a good and up-to-date example on that.
The following answers detail using a few techniques for a posting binary data to a restful service.
Post binary data to a RESTful application
What is a good way to transfer binary data to a HTTP REST API service?
Bad idea to transfer large payload using web services?
The following code is a sample of how you could write a RESTful WCF service and is by no means complete but does give you an indication on where you could start.
Sample Service, note that this is NOT production ready code.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class FileService
{
private IncomingWebRequestContext m_Request;
private OutgoingWebResponseContext m_Response;
[WebGet(UriTemplate = "{appName}/{id}?action={action}")]
public Stream GetFile(string appName, string id, string action)
{
var repository = new FileRepository();
var response = WebOperationContext.Current.OutgoingResponse;
var result = repository.GetById(int.Parse(id));
if (action != null && action.Equals("download", StringComparison.InvariantCultureIgnoreCase))
{
response.Headers.Add("Content-Disposition", string.Format("attachment; filename={0}", result.Name));
}
response.Headers.Add(HttpResponseHeader.ContentType, result.ContentType);
response.Headers.Add("X-Filename", result.Name);
return result.Content;
}
[WebInvoke(UriTemplate = "{appName}", Method = "POST")]
public void Save(string appName, Stream fileContent)
{
try
{
if (WebOperationContext.Current == null) throw new InvalidOperationException("WebOperationContext is null.");
m_Request = WebOperationContext.Current.IncomingRequest;
m_Response = WebOperationContext.Current.OutgoingResponse;
var file = CreateFileResource(fileContent, appName);
if (!FileIsValid(file)) throw new WebFaultException(HttpStatusCode.BadRequest);
SaveFile(file);
SetStatusAsCreated(file);
}
catch (Exception ex)
{
if (ex.GetType() == typeof(WebFaultException)) throw;
if (ex.GetType().IsGenericType && ex.GetType().GetGenericTypeDefinition() == typeof(WebFaultException<>)) throw;
throw new WebFaultException<string>("An unexpected error occurred.", HttpStatusCode.InternalServerError);
}
}
private FileResource CreateFileResource(Stream fileContent, string appName)
{
var result = new FileResource();
fileContent.CopyTo(result.Content);
result.ApplicationName = appName;
result.Name = m_Request.Headers["X-Filename"];
result.Location = #"C:\SomeFolder\" + result.Name;
result.ContentType = m_Request.Headers[HttpRequestHeader.ContentType] ?? this.GetContentType(result.Name);
result.DateUploaded = DateTime.Now;
return result;
}
private string GetContentType(string filename)
{
// this should be replaced with some form of logic to determine the correct file content type (I.E., use registry, extension, xml file, etc.,)
return "application/octet-stream";
}
private bool FileIsValid(FileResource file)
{
var validator = new FileResourceValidator();
var clientHash = m_Request.Headers[HttpRequestHeader.ContentMd5];
return validator.IsValid(file, clientHash);
}
private void SaveFile(FileResource file)
{
// This will persist the meta data about the file to a database (I.E., size, filename, file location, etc)
new FileRepository().AddFile(file);
}
private void SetStatusAsCreated(FileResource file)
{
var location = new Uri(m_Request.UriTemplateMatch.RequestUri.AbsoluteUri + "/" + file.Id);
m_Response.SetStatusAsCreated(location);
}
}
Sample Client, note that this is NOT production ready code.
// *********************************
// Sample Client
// *********************************
private void UploadButton_Click(object sender, EventArgs e)
{
var uri = "http://dev-fileservice/SampleApplication"
var fullFilename = #"C:\somefile.txt";
var fileContent = File.ReadAllBytes(fullFilename);
using (var webClient = new WebClient())
{
try
{
webClient.Proxy = null;
webClient.Headers.Add(HttpRequestHeader.ContentMd5, this.CalculateFileHash());
webClient.Headers.Add("X-DaysToKeep", DurationNumericUpDown.Value.ToString());
webClient.Headers.Add("X-Filename", Path.GetFileName(fullFilename));
webClient.UploadData(uri, "POST", fileContent);
var fileUri = webClient.ResponseHeaders[HttpResponseHeader.Location];
Console.WriteLine("File can be downloaded at" + fileUri);
}
catch (Exception ex)
{
var exception = ex.Message;
}
}
}
private string CalculateFileHash()
{
var hash = MD5.Create().ComputeHash(File.ReadAllBytes(#"C:\somefile.txt"));
var sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("x2"));
}
return sb.ToString();
}
private void DownloadFile()
{
var uri = "http://dev-fileservice/SampleApplication/1" // this is the URL returned by the Restful file service
using (var webClient = new WebClient())
{
try
{
webClient.Proxy = null;
var fileContent = webClient.DownloadData(uri);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

Categories