I have a simple webservice, which I want to access via a http post.
The webservice code is show below:
[WebMethod]
public int Insert(string userDate, string DeviceID)
{
bool output;
DateTime date;
output = DateTime.TryParse(userDate, out date);
if (!output)
{
// Throw an error
return -1;
}
int Device;
output = int.TryParse(DeviceID, out Device);
if (!output)
{
// Throw an Error
return -1;
}
UsersDatesBLL BLL = new UsersDatesBLL();
return BLL.Insert(Device, date);
}
I can access the service fine using internet explorer, the results are inserted to the database perfectly simply by calling: CountDownService.asmx/Insert?userDate=24/04/1980&DeviceID=3435
However when testing on Safari and Firefox the service always returns -1
Does anyone know the cause of this? Does Safari encode strings differently to IE?
Regards
Mick
Users can configure their UI language and culture in their browser. The browser passes this information as the Accept-Language HTTP header in requests to your webservice. That information may be used to set the "current culture" of the ASP.NET session that handles the request. It is available as the static property CultureInfo.CurrentCulture.
DateTime.TryParse will use that "current culture" to figure out which of the many datetime string formats it should expect - unless you use the overload where you explicitly pass a culture as the IFormatProvider. Apparently the browsers you are testing with are configured differently, so ASP.NET expects different datetime formats from each. If you want the datetimes to be parsed independently from the browser settings, then you should use CultureInfo.InvariantCulture as the format provider.
The first thing I would do as a debugging measure (assuming you can't just run a debugger on the server code) would be to make all three return paths return different values. That way you're not left guessing whether it was the DateTime.TryParse() call, the int.TryParse() call, or the BLL.Insert() call that failed.
If BLL.Insert() returns a -1 on failure, then I would change the first -1 to -3 and the second to -2. Like so:
output = DateTime.TryParse(userDate, out date);
if (!output)
{
// Throw an error
return -3; // ***** Changed this line
}
int Device;
output = int.TryParse(DeviceID, out Device);
if (!output)
{
// Throw an Error
return -2; // ***** Changed this line
}
I know it doesn't exactly answer the question, but it would at least help you track down which part is failing.
You are using the current culture to parse your DateTime and int values. The first thing to check is whether your various browsers are all configured to send the same culture to the web server in their HTTP request headers.
As a matter of style, you should avoid using culture-dependent formats for URL parameters. The most appropriate format to use is the fixed XML Schema format, like this:
[WebMethod]
public int Insert(string userDate, string DeviceID) {
DateTime date;
int device;
try {
date = XmlConvert.ToDateTime(userDate, XmlDateTimeSerializationMode.Local);
device = XmlConvert.ToInt32(DeviceID);
} catch (Exception) {
// Throw an error
return -1;
}
UsersDatesBLL BLL = new UsersDatesBLL();
return BLL.Insert(device, date);
}
And call it like this:
CountDownService.asmx/Insert?userDate=1980-04-24&DeviceID=3435
Another alternative is to use strongly-typed parameter values and let ASP.NET do the parsing for you, e.g.
[WebMethod]
public int Insert(DateTime userDate, int DeviceID) {
UsersDatesBLL BLL = new UsersDatesBLL();
return BLL.Insert(DeviceID, userDate);
}
DISCLAIMER - I have not tried this alternative myself, but it's worth a look, because it will make your life easier if you don't have to put parsing code into every web method.
Related
Is there any way in ASP.net C# to treat sub-domain as query string?
I mean if the user typed london.example.com then I can read that he is after london data and run a query based on that. example.com does not currently have any sub-domains.
This is a DNS problem more than an C#/ASP.Net/IIS problem. In theory, you could use a wildcard DNS record. In practice, you run into this problem from the link:
The exact rules for when a wild card will match are specified in RFC 1034, but the rules are neither intuitive nor clearly specified. This has resulted in incompatible implementations and unexpected results when they are used.
So you can try it, but it's not likely to end well. Moreover, you can fiddle with things until it works in your testing environment, but that won't be able to guarantee things go well for the general public. You'll likely do much better choosing a good DNS provider with an API, and writing code to use the API to keep individual DNS entries in sync. You can also set up your own public DNS server, though I strongly recommend using a well-known and reputable commercial DNS host.
An additional problem you can run into is the TLS/SSL certificate (because of course you're gonna use HTTPS. Right? RIGHT!?) You can try a wild card certificate and probably be okay, but depending on what else you do you may find it's not adequate; suddenly you're needing to provision a separate SSL certificate for every city entry in your database, and that can be a real pain, even via the Let's Encrypt service.
If you do try it, IIS is easily capable of mapping the requests to your ASP.Net app based on a wildcard host name, and ASP.Net itself is easily capable of reading and parsing the host name out of the request and returning different results based on that. IIS URL re-writing should be able to help with this, though I'm not sure whether you can do stock MVC routing in C#/ASP.Net based on this attribute.
I have to add to the previous answers, that after you fix the dns, and translate the subdomain to some parameters you can use the RewritePath to move that parameters to your pages.
For example let say that a function PathTranslate(), translate the london.example.com to example.com/default.aspx?Town=1
Then you use the RewritePath to keep the sub-domain and at the same time send your parameters to your page.
string sThePathToReWrite = PathTranslate();
if (sThePathToReWrite != null){
HttpContext.Current.RewritePath(sThePathToReWrite, false);
}
string PathTranslate()
{
string sCurrentPath = HttpContext.Current.Request.Path;
string sCurrentHost = HttpContext.Current.Request.Url.Host;
//... lot of code ...
return strTranslatedUrl
}
A low tech solution can be like this: (reference: https://www.pavey.me/2016/03/aspnet-c-extracting-parts-of-url.html)
public static List<string> SubDomains(this HttpRequest Request)
{
// variables
string[] requestArray = Request.Host().Split(".".ToCharArray());
var subDomains = new List<string>();
// make sure this is not an ip address
if (Request.IsIPAddress())
{
return subDomains;
}
// make sure we have all the parts necessary
if (requestArray == null)
{
return subDomains;
}
// last part is the tld (e.g. .com)
// second to last part is the domain (e.g. mydomain)
// the remaining parts are the sub-domain(s)
if (requestArray.Length > 2)
{
for (int i = 0; i <= requestArray.Length - 3; i++)
{
subDomains.Add(requestArray[i]);
}
}
// return
return subDomains;
}
// e.g. www
public static string SubDomain(this HttpRequest Request)
{
if (Request.SubDomains().Count > 0)
{
// handle cases where multiple sub-domains (e.g. dev.www)
return Request.SubDomains().Last();
}
else
{
// handle cases where no sub-domains
return string.Empty;
}
}
// e.g. azurewebsites.net
public static string Domain(this HttpRequest Request)
{
// variables
string[] requestArray = Request.Host().Split(".".ToCharArray());
// make sure this is not an ip address
if (Request.IsIPAddress())
{
return string.Empty;
}
// special case for localhost
if (Request.IsLocalHost())
{
return Request.Host().ToLower();
}
// make sure we have all the parts necessary
if (requestArray == null)
{
return string.Empty;
}
// make sure we have all the parts necessary
if (requestArray.Length > 1)
{
return $"{requestArray[requestArray.Length - 2]}.{requestArray[requestArray.Length - 1]}";
}
// return empty string
return string.Empty;
}
Following question is similar to yours:
Using the subdomain as a parameter
I've been using Protobuf-net as the serializer for a thick client application that's using Service Stack to communicate over HTTP. Our first customer with a lot of volume has started seeing errors when deserializing. We are sending DateTimeOffset types in some of our models, and so we created a surrogate that serializes the value as a string. From our logs, I can see when the error occurs this is the date value it's attempting to deserialize has an extra six characters at the end where the timezone offset is repeated:
8/9/2016 12:02:37 AM-7:00 -7:00
Here's the code for our surrogate.
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public string DateTimeString { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate { DateTimeString = value.ToString() };
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
try
{
return DateTimeOffset.Parse(value.DateTimeString);
}
catch (Exception ex)
{
throw new Exception("Unable to parse date time value: " + value.DateTimeString, ex);
}
}
}
Once this date error has occurred, it won't correctly serialize/deserialize until the PC has rebooted. We have not been able to reproduce this error in a way that would allow us to debug and look at the rest of the message. Is this a situation that someone is familiar with? We were using version 2.0.0.640, and because of this issue I updated to 2.0.0.668 but the issue remains.
It looks as though the CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern is somehow getting messed up on the client's machine. I can reproduce the problem by adding the "K" format to the LongTimePattern:
var dateTime = DateTimeOffset.Parse(#"8/9/2016 12:02:37 AM-7:00");
var myCI = new CultureInfo("en-US");
myCI.DateTimeFormat.LongTimePattern = myCI.DateTimeFormat.LongTimePattern + " K";
Console.WriteLine(dateTime.ToString(myCI)); // Prints 8/9/2016 12:02:37 AM -07:00 -07:00
The string written is 8/9/2016 12:02:37 AM -07:00 -07:00 which is exactly what you are seeing.
It may be that there is a bug in your application which is setting the LongTimePattern somewhere. I can also reproduce the problem by doing:
Thread.CurrentThread.CurrentCulture = myCI;
Console.WriteLine(dateTime.ToString()); // Prints 8/9/2016 12:02:37 AM -07:00 -07:00
Or it may be that the client is somehow modifying the "Long time:" string in "Region and Language" -> "Additional settings..." dialog, which looks like (Windows 7):
If the client is doing this somehow, and the machine is on a domain, the format may get reset back on reboot which is exactly what you are seeing.
The client may be doing this manually (although, from experimentation, trying to append K manually on Windows 7 in the UI generates an error popup and then fails), or there may be some buggy 3rd party application doing it unbeknownst to you or them via a call to SetLocaleInfo.
You could log the value of LongTimePattern to try to trace the problem, but regardless you should modify your DateTimeOffsetSurrogate so that it serializes the DateTimeOffset in a culture-invariant format, preferably as specified by How to: Round-trip Date and Time Values: To round-trip a DateTimeOffset value:
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public string DateTimeString { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate { DateTimeString = value.ToString("o") };
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
try
{
return DateTimeOffset.Parse(value.DateTimeString, null, DateTimeStyles.RoundtripKind);
}
catch (Exception ex)
{
throw new Exception("Unable to parse date time value: " + value.DateTimeString, ex);
}
}
}
Not only should this fix the bug you are seeing, it will also ensure that protocol buffers generated by your app in one region (say, the UK) can be parsed elsewhere (say, the US) with different cultural formatting for dates and times.
I am using a mono self hosted servicestack application with the ServiceStack.Razor rendering. In the application the user enters into a form a UK date (dd/mm/yyyy) but this is converted to a US date (mm/dd/yyyy) on a HTTP POST.
In a normal MVC application I would do this using model binding as shown here ASP.NET MVC3: Force controller to use date format dd/mm/yyyy
How do you do this in ServiceStack as I could not find anything about it.
You can use custom serializers/deserializers to globally control the serialization and deserialization of DateTime values:
In your AppHost:
using ServiceStack.Text;
JsConfig<DateTime>.SerializeFn = SerializeAsUKDate;
// Also, if you need to support nullable DateTimes:
JsConfig<DateTime?>.SerializeFn = SerializeAsNullableUKDate;
public static string SerializeAsUKDate(DateTime value)
{
// or whatever you prefer to specify the format/culture
return value.ToString("dd/MM/yyyy");
}
public static string SerializeAsNullableUKDate(DateTime? value)
{
return value.HasValue ? SerializeAsUKDate(value.Value) : null;
}
You may or may not need to specify DeSerializeFn to ensure that dates are parsed correctly. The ServiceStack.Text date deserializer is pretty robust.
JsConfig<DateTime>.DeSerializeFn = DeSerializeAsUKDate;
public static DateTime DeSerializeAsUKDate(string value)
{
// date parsing logic here
// ServiceStack.Text.Common.DateTimeSerializer has some helper methods you may want to leverage
}
I write my logs to a text file as JSON. In the file the call obejct LogTime value is
"1378289277591".
*{"LogTime":"Date(1378290565240)"}*
Consider the code below:
Public Class Sync{
public async Task<CallModel> ConvertCallFileToCallObejct(string path)
{
try
{
using (var sr = new StreamReader(path))
{
string callText = await sr.ReadToEndAsync();
var call = new JavaScriptSerializer().Deserialize<CallModel>(callText);
return call;
}
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
}
}
I convert the Call File to Call Object:
var sync = new Sync();
CallModel call = sync.ConvertCallFileToCallObejct(e.FullPath).GetAwaiter().GetResult();
The problem is that Call.LogTime is 9/4/2013 10:29:25 AM but Using Chrome Console and new Date(1378290565240) the result is 9/4/2013 14:59:25 PM
What is the problem?
try below code
// JSON received from server is in string format
var jsonString = '{"date":1251877601000}';
//use JSON2 or some JS library to parse the string
var jsonObject = JSON.parse( jsonString );
//now you have your date!
alert( new Date(jsonObject.date) );
I'm not sure what your Time zone is but I would expect that its UTC datetime.
According to your profile, you live in Iran, where the timezone is UTC+3:30. However, in April, Iran uses daylight saving time so the real timezone is UTC+4:30.
This means that UTC time of 9/4/2013 10:29:25 AM is 9/4/2013 14:59:25 PM local time in Iran.
According to ECMA specification, the time given in your JSON string is treated as UTC time, and it is deserialized as such. You can check the return value of Call.LogTime expression which returns DateTimeKind.Utc. Thus, what you see in your C# code is UTC time.
Now, Chrome also sees this time as UTC time, however it seems to display it as local time, according to your timezone. I am not 100% sure, but I think that Chrome uses your list of preferred languages when choosing how to display date, so try to play with it - I have no idea what exactly it does, but I remember a similar problem when changing the language order affected how time was interpreted. OF course, it depends on what exactly you try to achieve - IMO, both values are correct, as it is the same time.
I have a simple function GetPageName(String PageFileName, String LangCode) defined inside a class file. I call this function from default.aspx.cs file, In this function I am not able to use Response.Redirect("Error.aspx") to show user that error has been generated.
Below is example of Code
public static string GetPageName(String PageFileName, String LangCode)
{
String sLangCode = Request("Language");
String pgName = null;
if ( sLangCode.Length > 6)
{
Reponse.Redirect("Error.aspx?msg=Invalid Input");
}
else
{
try
{
String strSql = "SELECT* FROM Table";
Dataset ds = Dataprovider.Connect_SQL(strSql);
}
catch( Exception ex)
{
response.redirect("Error.aspx?msg="+ex.Message);
}
}
return pgName;
}
I have may function defined in Business and Datalayer where i want to trap the error and redirect user to the Error page.
HttpContext.Current.Response.Redirect("error.aspx");
to use it your assembly should reference System.Web.
For a start, in one place you're trying to use:
response.redirect(...);
which wouldn't work anyway - C# is case-sensitive.
But the bigger problem is that normally Response.Redirect uses the Page.Response property to get at the relevant HttpResponse. That isn't available when you're not in a page, of course.
Options:
Use HttpContext.Current.Response to get at the response for the current response for the executing thread
Pass it into the method as a parameter:
// Note: parameter names changed to follow .NET conventions
public static string GetPageName(String pageFileName, String langCode,
HttpResponse response)
{
...
response.Redirect(...);
}
(EDIT: As noted in comments, you also have a SQL Injection vulnerability. Please use parameterized SQL. Likewise showing exception messages directly to users can be a security vulnerability in itself...)