Getting if-modified-since header to work with WCF - wcf

I am trying to get the "if-modified-since" header to work with my WCF web service.
When a user makes a request to my service, I add an ETag to the outgoing response that contains the timestamp of the request as follows:
var tag = String.Format("\"{0:o}\"", new DateTimeOffset(DateTime.Now));
This results in the following ETag header:
ETag: "2011-10-27T13:09:39.6242263-04:00"
I then take this value and echo it back as the if-modified-since header for subsequent requests like this:
If-Modified-Since:2011-10-27T13:09:39.6242263-04:00
When I examine WebOperationContext.Current.Headers.IfModifiedSince, I never get the value provided. The value is fixed at "12/31/1969 7:00:00 PM".
What am I doing wrong?
UPDATE
I should add that using Fiddler, I can set any value to the If-Modified-Since header and still get the same 1969 value in code.

First off, If-Modified-Since is about conditional GETs regarding the time of the last modification of the resource, while ETag is about conditional GETs regarding an identifier of the resources, so please be careful with mixing the two concepts.
The correct way of implementing support for If-Modified-Since in a WCF service is to use the CheckConditionalRetrieve passing a DateTime value in the WebOperationContext.Current.IncomingRequest object - see the code below for that. If the value of the IMS header is before the date you pass to CheckConditionalRetrieve, the method will exit at that point returning a 304 (Not Modified) response. Otherwise it will just continue. The code below shows that.
Another issue: even through the date format you're using (ISO 8601) works, but it's not correct based on the specification (section 14.25 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, and section 3.3.1 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1), so you should consider using a valid format to prevent future problems.
You can find a good post about conditional GET support in WCF at http://blogs.msdn.com/b/endpoint/archive/2010/02/25/conditional-get-and-etag-support-in-wcf-webhttp-services.aspx.
public class StackOverflow_7919718
{
[ServiceContract]
public class Service
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public string GetData()
{
Console.WriteLine("If-Modified-Since header (1): {0}", WebOperationContext.Current.IncomingRequest.IfModifiedSince);
WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(DateTime.UtcNow);
return "Data";
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
Console.WriteLine("Not sending If-Modified-Since header (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null);
Console.WriteLine("Sending data in the past, ISO 8601 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2011-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the future, ISO 8601 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "2021-10-25T13:09:39.6242263-04:00" } });
Console.WriteLine("Sending data in the past, RFC 1123 format (should return 200):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Wed, 26 Oct 2011 01:00:00 GMT" } });
Console.WriteLine("Sending data in the future, RFC 1123 format (should return 304):");
Util.SendRequest(baseAddress + "/GetData", "GET", null, null,
new Dictionary<string, string> { { "If-Modified-Since", "Mon, 27 Oct 2031 10:00:00 GMT" } });
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
public static class Util
{
public static string SendRequest(string uri, string method, string contentType, string body)
{
return SendRequest(uri, method, contentType, body, null);
}
public static string SendRequest(string uri, string method, string contentType, string body, Dictionary<string, string> headers)
{
string responseBody = null;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = method;
if (headers != null)
{
foreach (string headerName in headers.Keys)
{
switch (headerName)
{
case "If-Modified-Since":
req.IfModifiedSince = DateTime.Parse(headers[headerName]);
break;
default:
req.Headers[headerName] = headers[headerName];
break;
}
}
}
if (!String.IsNullOrEmpty(contentType))
{
req.ContentType = contentType;
}
if (body != null)
{
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
req.GetRequestStream().Close();
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
if (resp == null)
{
responseBody = null;
Console.WriteLine("Response is null");
}
else
{
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Stream respStream = resp.GetResponseStream();
if (respStream != null)
{
responseBody = new StreamReader(respStream).ReadToEnd();
Console.WriteLine(responseBody);
}
else
{
Console.WriteLine("HttpWebResponse.GetResponseStream returned null");
}
}
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
return responseBody;
}
}

Related

How to specify date format in RestSharp request

I have this piece of code:
var client = new RestClient(fullUri);
var request = new RestRequest(GetMethod(method));
client.UseSerializer(
() => new RestSharp.Serialization.Json.JsonSerializer { DateFormat = "yyyy-MM-dd HH:mm:ss" }
);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
if (body != null)
request.AddJsonBody(body);
var response = client.Execute(request);
if (response.ErrorException != null)
throw response.ErrorException;
return JsonConvert.DeserializeObject<ResponseData>(response.Content);
Note the
client.UseSerializer(
() => new RestSharp.Serialization.Json.JsonSerializer { DateFormat = "yyyy-MM-dd HH:mm:ss" }
);
I have added that code because I need the date format to be yyyy-MM-dd HH:mm:ss instead of yyyy-MM-ddTHH:mm:ss.zzzzz (the default)
RestRequest.DateFormat is obsolete.
When the calling is made, I saw that a date is passed using the default format, instead of the custom one.
How can I do it?
I will suggest rewrite your code. Here my comments and example:
First, if you try to use this format for DateTime yyyy-MM-dd HH:mm:ss you will get a validation error from web API service when you try to send a request.
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title":"One or more validation errors occurred.",
"status":400,
"traceId":"00-a0c978c054625441b8ddf4c552b0f34c-314723fc8ce3ac4e-00",
"errors":{
"$.DateTime":[
"The JSON value could not be converted to System.DateTime. Path: $.DateTime | LineNumber: 0 | BytePositionInLine: 33."
]
}
}
This means - you use incorrect format for DateTime. You should use this format "yyyy-MM-ddTHH:mm:ss". Read more in question The JSON value could not be converted to System.DateTime.
Don't forget add T because parsers use ISO_8601
Second, I would highly recommend do not use JsonSerializer from RestSharp, because I faced different problems with it. I highly recommend to use System.Text.Json from Microsoft or Newtonsoft.Json. Read more about System.Text.Json or Newtonsoft.Json.
Third, let's code! My example of web api service.
[HttpGet]
public string Test()
{
var client = new RestClient("your url");
var request = new RestRequest(Method.POST);
var body = new SimpleRequestBody
{
DateTime = DateTime.Now
};
var json = JsonConvert.SerializeObject(body,
new JsonSerializerSettings()
{
DateFormatString = "yyyy-MM-ddTHH:mm:ss"
});
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
if (body != null)
request.AddParameter("application/json", json, null, ParameterType.RequestBody);
var response = client.Execute(request);
// response.ErrorException -> Could not handle bad request
// Better to user IsSuccessful property
if (!response.IsSuccessful)
throw new Exception(response.Content);
return response.Content;
}
public class SimpleRequestBody
{
public DateTime DateTime { get; set; }
}
Example of client web api code:
[HttpPost]
public ActionResult Post([FromBody] SimpleResponse response)
{
var result = $"OK result from another web api. DateTime {response.DateTime}";
return Ok(result);
}
public class SimpleResponse
{
public DateTime DateTime { get; set; }
}
}
Results

Storing An Image In SQL Server

I need to create a procedure in SQL server that takes a web URL of an image and converts it to VARBINARY, and after that: store in a column called "personqr_Image" in table "tblPersons".
I created a procedure "getPersonQrCode" that returns a URL of a unique QR code (450x450 image), and using that URL I need to convert it to VARBINARY data type in order to store it in my SQL DB.
Unfortunately I haven't really found a solution online, maybe because I am not very familiar with the subject.
You can't do this purely in TSQL, as it doesn't have any functions for browsing the web and handling http requests and responses. If you have to do this IN SQL Server, you'll need to write a CLR procedure.
Here is a CLR function that will allow you to submit HTTP requests
public class RestClient
{
[SqlFunction(DataAccess = DataAccessKind.Read)]
public static string Submit(string url, string data, string contentType, string
method = "POST",
string httpHeaderCredentials = "")
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var request = (HttpWebRequest) WebRequest.Create(url);
//Add header credentials if required
if (!string.IsNullOrEmpty(httpHeaderCredentials))
{
request.Headers.Add("Authorization: " + httpHeaderCredentials);
}
request.ContentType = contentType;
request.Method = method;
if (request.Method == "PATCH")
{
//http://stackoverflow.com/questions/31043195/rest-api-patch-request
request.ServicePoint.Expect100Continue = false;
}
if (method == "POST" || method == "PATCH")
{
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(data);
streamWriter.Flush();
streamWriter.Close();
}
}
var httpResponse = request.GetResponse();
using (var responseStream = httpResponse.GetResponseStream())
{
if (responseStream != null)
{
using (var reader = new StreamReader(responseStream))
{
return reader.ReadToEnd().Replace("\n", string.Empty);
}
}
}
}
catch (Exception ex)
{
if (SqlContext.Pipe != null)
{
SqlContext.Pipe.Send(ex.Message);
}
}
return "";
}

Get missing auditlog from Management Activity API in Office365

Our application calls out-of-the-box Office 365 Management API to retrieve activities and events on files stored in SharePoint Online. However per our experiment, the application can’t seem to retrieve not enough logs.
Example: We upload 1000 files to document library in Sharepoint Online. We receive 8 subscriptiona. Each subscription, we only get maximum 100 logs. Total call API get logs to retrieve 600 logs. Not enough!
Here my code to get subscription
List<SubscriptionsContent> GetSubscriptionsContents(AuthenticationResult authenticationResult, ManagementAPI m, DateTime startDate, DateTime endDate, bool proxyRequired = false)
{
try
{
string jsonSubscription = string.Empty;
string url = string.Empty;
string logType = "Audit.SharePoint";
if (authenticationResult != null)
{
url = string.Format(UrlFormat, m.TenantId, string.Format("subscriptions/content?contentType={0}&startTime={1}&endTime={2}", logType, startDate.ToUniversalTime().ToString(DateFormat), endDate.ToUniversalTime().ToString(DateFormat)));
jsonSubscription = ExecuteRequest(url, HttpMethod.Get, authenticationResult);
//Log.Info("jsonSubscription:");
//Log.Info(jsonSubscription);
}
var listContent = Common.GetListSubscriptionsContent(jsonSubscription);
Log.Info("Common.GetListSubscriptionsContent(jsonSubscription); Count: " + (listContent != null ? listContent.Count.ToString() : "IS NULL"));
return listContent;
}
catch (Exception ex)
{
Log.Error(ex);
return new List<SubscriptionsContent>();
}
}
Here my code to execute Request
public string ExecuteRequest(string url, HttpMethod method, AuthenticationResult token)
{
var responseStr = "";
try
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(method, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
Log.Info("ExecuteRequest(string url, HttpMethod method, AuthenticationResult token): response.StatusCode: " + response.StatusCode + " ; response.ReasonPhrase: " + response.ReasonPhrase + " ; response.RequestMessage: " + response.RequestMessage);
if (response.IsSuccessStatusCode)
{
responseStr = response.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
Log.Error(ex);
}
return responseStr;
}
Here my code to get audit log from each subscription
List<AuditLog> listAudit = new List<AuditLog>();
foreach (var item in listSubscription)
{
var jsonAudit = ExecuteRequest(item.ContentUri.ToString(), HttpMethod.Get, authenticationResult);
if (string.IsNullOrEmpty(jsonAudit))
continue;
var listAuditLog = Common.GetListAuditLog(jsonAudit);
}
Here my code to parser JsonString
public static List<AuditLog> GetListAuditLog(string jsonString)
{
try
{
return JsonConvert.DeserializeObject<List<AuditLog>>(jsonString);
}
catch (Exception ex)
{
Log.Error("public static List<AuditLog> GetListAuditLog(string jsonString)", ex.InnerException);
return new List<AuditLog>();
}
}
I think that you need to use the pagination header.
If the amount of data is too big, the API will return a header entry named NextPageUrl containing an address to be used to request the next page of results. This link (representing the query) will be available for 24 hours.
Ex.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
NextPageUrl:https://manage.office.com/api/v1/{tenant_id}/activity/feed/subscriptions/content?contentType=Audit.SharePoint&startTime=2015-10-01&endTime=2015-10-02&nextPage=2015101900R022885001761
So, if the response contains this header entry, just use the value of NextPageUrl to request more data.
Repeat the process until this header entry doesn't exists anymore.
You can find more information in the Office 365 Management API reference

HTTP Requests in Glass GDK

I am implementing a GDK application and need to do in my application some HTTP Post requests. Do I send the HTTP requests the same way as on android phone or there is some other way of doing it? (I have tried the code that I am using on my phone and it's not working for glass.)
thanks for your help in advance.
You can make any post request like in smartphones, but ensure you make the requests using an AsyncTask.
For example:
private class SendPostTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Make your request POST here. Example:
myRequestPost();
return null;
}
protected void onPostExecute(Void result) {
// Do something when finished.
}
}
And you can call that asynctask anywhere with:
new SendPostTask().execute();
And example of myRequestPost() may be:
private int myRequestPost() {
int resultCode = 0;
String url = "http://your-url-here";
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
// add headers you want, example:
// post.setHeader("Authorization", "YOUR-TOKEN");
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("id", "111111"));
nameValuePairs.add(new BasicNameValuePair("otherField", "your-other-data"));
try {
post.setEntity(new UrlEncodedFormEntity(urlParameters));
HttpResponse response = client.execute(post);
System.out.println("\nSending 'POST' request to URL : " + url);
System.out.println("Post parameters : " + post.getEntity());
System.out.println("Response Code : " +
response.getStatusLine().getStatusCode());
resultCode = response.getStatusLine().getStatusCode();
BufferedReader rd = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString());
} catch (Exception e) {
Log.e("POST", e.getMessage());
}
return resultCode;
}

Saving data in windows phone received from WCF/web service .

Saving data in windows phone received from WCF/web service .
The response may be received after sometime so how to handle this situation.
Saving data is no problem but How to handel if data is received late
You can use this code (show the code from my project):
public void sendPost(string postData, Action<MyResponse, Exception> callback, CreateResponse creater)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(UrlRequest);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Accept = "application/json";
webRequest.AllowAutoRedirect = true;
webRequest.BeginGetRequestStream(new AsyncCallback(getRequestStreamCallback), new Request()
{
HttpRequest = webRequest,
PostData = postData,
Url = UrlRequest,
CallBack = callback,
Creater = creater
});
}
private void getRequestStreamCallback(IAsyncResult asynchronousResult)
{
var request = (Request)asynchronousResult.AsyncState;
// End the stream request operation
Stream postStream = request.HttpRequest.EndGetRequestStream(asynchronousResult);
byte[] byteArray = Encoding.UTF8.GetBytes(request.PostData);
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
// Start the web request
request.HttpRequest.BeginGetResponse(new AsyncCallback(getResponseCallback), request);
}
private void getResponseCallback(IAsyncResult asynchronousResult)
{
var request = (Request)asynchronousResult.AsyncState;
try
{
HttpWebResponse response;
// End the get response operation
response = (HttpWebResponse)request.HttpRequest.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamReader = new StreamReader(streamResponse);
var myResponse = streamReader.ReadToEnd();
streamResponse.Close();
streamReader.Close();
response.Close();
MyResponse response_obj = request.Creater.CreateResponseObj();
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(myResponse)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response_obj.GetType());
response_obj = (GYResponse)serializer.ReadObject(stream);
if (request.CallBack != null)
{
request.CallBack.Invoke(response_obj, null);
}
}
}
catch (WebException e)
{
if (request.CallBack != null)
{
request.CallBack.Invoke(null, e);
}
}
}
public void getInfo(string uid, Action<MyResponse, Exception> callback)
{
CreateResponse creater = new CreateResponseGetInfo();
string model = "User";
string method = "getInfo";
Params parametrs = new Params();
parametrs.Uid = uid;
//create yor request
string request = getRequestString(model, method, parametrs, Atoken);
sendPost(request, callback, creater);
}
So, you call method, which send request to web service postRequester.getInfo(uid, ResponseHandler) and use delegate for processing result.
private void ResponseHandler(MyResponse result, Exception error)
{
if (error != null)
{
string err = error.Message;
return;
}
else
{
var infoResponse = result as ResponseGetInfo;
if (infoResponse != null)
{
//result processing..
}
}
}
All the web requests you make in a Windows Phone app are Asynchronous. That means, you make a web request from your app and attach a handler to handle the response when it comes. In the response handler, you will have to take care of the response and do whatever you want with it.
Check this link Using WebClient and HttpWebRequest