REST webservice "Bad request" - c#

I'm writing a REST webservice having a method like this:
[WebGet(
UriTemplate = "/Test/{p1}/{p2}",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml)]
public string Test(string p1, string p2)
{
// Do something here
}
So if I call basurl/Test/prova/test my method Test is invoked with p1="prova" and p2="test" and everything works fine.
Problem comes when I try to use a param having (for example) % char: even translating it in URL code, when I try to call basurl/Test/prova/te%25st I get a
Errore HTTP 400 - Bad Request.
If I use
[WebGet(
UriTemplate = "/Test/{p1}?p2={p2}",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml)]
public string Test(string p1, string p2)
{
// Do something here
}
and call basurl/Test/prova?p2=te%25st it works.
Why? What can I do to let first syntax work?
UPDATE:
Look at my answer with a possible solution.
If someone finds a better one, please post it!!
Thanks

Googling around I've just found this link:
http://weblogs.asp.net/imranbaloch/archive/2010/04/23/understanding-400-bad-request-exception.aspx
where they say:
ASP.NET Restrictions:
After passing the restrictions enforced by the kernel mode http.sys then the request is handed off to IIS and then to ASP.NET
engine and then again request has to pass some restriction from
ASP.NET in order to complete it successfully.
ASP.NET only allows URL path lengths to 260 characters(only paths, for example http://a/b/c/d, here path is from a to d). This
means that if you have long paths containing 261 characters then you
will get the Bad Request exception. This is due to NTFS file-path
limit.
Another restriction is that which characters can be used in URL path portion.You can use any characters except some characters
because they are called invalid characters in path. Here are some of
these invalid character in the path portion of a URL, <,>,*,%,&,:,\,?.
For confirming this just right click on your Solution Explorer and Add
New Folder and name this File to any of the above character, you will
get the message. Files or folders cannot be empty strings nor they
contain only '.' or have any of the following characters.....
For checking the above situation i have created a Web Application and put Default.aspx inside A%A folder (created from
windows explorer), then navigate to,
http://localhost:1234/A%25A/Default.aspx, what i get response from
server is the Bad Request exception. The reason is that %25 is the %
character which is invalid URL path character in ASP.NET. However you
can use these characters in query string.
The reason for these restrictions are due to security, for example with the help of % you can double encode the URL path portion
and : is used to get some specific resource from server.
So I'm starting to think that my problem is impossible to solve.
I'm sure that this problem is not present in some REST webservice written in PHP and hosted with Apache, so I think it's just a IIS/ASP "security" restriction I can't find a workaround for...
UPDATE WITH FINAL SOLUTION:
I found a solution here: read the article to understand everything.
You should know that it could be risky, so think well before using it.
<system.web>
<httpRuntime requestPathInvalidCharacters="" />
<pages validateRequest="false" />
</system.web>

Related

URI encodes control signs into absolute path

We are sending data to an API with several endpoints. For this we put the base URL into a variable and appending the route info to it. But the request cannot be resolved to a service, as the URI-object is putting not printable characters into the path.
The code for create the URI object:
var uri = new Uri(_url + "/api/v1/create");
The result is:
https://localhost%E2%80%8B/api%E2%80%8B/v1%E2%80%8B/create
We are using .Net Framework 4.7.2.
Does anyone know, whats happening?
As canton7 pointed out, the problem had been the zero-length space here
We retyped the whole method calls - not only the URLs - and now the system is running. Thanks.

Line breaks with WCF, JSON and a non-Windows client

I have a .NET WCF service using WebMessageFormat.Json as both its ResponseFormat and its RequestFormat. The service runs on a Windows server, the client is an Android tablet.
As it turns out, strings sent from the client to the server contain LF linebreaks ("\n") instead of CRLF("\r\n"). Since Android is Linux-based, this is not surprising. However, the data is stored in a Windows database and read by Windows clients, so I need CRLF line breaks.
Is there a more elegant way to solve this issue than to manually s = s.Replace("\n", "\r\n"); every string received via WCF? Since WCF has so many options and features, I figured that there might be some hidden AutoTranslateNewlines option that I have missed...
Additional information: My service is declared roughly like this:
[OperationContract()]
[WebInvoke(ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "MyService?UserId={myUserId}")]
public Reply MyService(String myUserId, Request someRequest) { ... }
where Request is a custom class with a few String fields, some of which can contain multiple lines of text.
You mention that you are using
s = s.Replace("\n","\r\n");
But an issue could arise where you have some "\r\n" sequences in your string. These would become "\r\r\n", which would be an issue. To solve this problem, simply do the following instead.
s = s.Replace("\r\n", "\n");
s = s.Replace("\r", "\n");
s = s.Replace("\n", "\r\n");
which should handle all situations, though perhaps not the most performant. For better performance, perhaps you can use a regular expression.

WCF HTTP GET parameter causes bad request

I have a WCF service with the following operation contract:
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/VerifyKeys.json/{customerKey}/{userKey}")]
[return: MessageParameter(Name = "MyDetail")]
MyDetail VerifyKeys(string customerKey, string userKey);
My method is like this:
public MyDetail VerifyKeys(string customerKey, string userKey)
{
...
return _myDetail;
}
My request is like this:
http://mydomain.com/MyService.svc/web/VerifyKeys.json/FE3D0F1D-5B8B-4677-B332-70B7ABA80A97/08F4349A-30E5-457D-F2BD-70A23CE17F41?deviceId=66345ec6-a5fe-4b5f-8cf2-1b0d8c344dc2&deviceToken=AgTGERCBaS3d8n2QWxF9EtwcLktIoygoXpc8Y42ObZWja3RSjN%2bFBeshaY4ASainj3MusBbVopXbUFQrrgXUOSkAbOA7tChNKOFNKQ2gB8sEfCe5Du9BZufW4bAP5312MKRqV8g%3d&deviceType=Pink24
I have different versions of my application calling this method. Rather than create a new method, I have used a query string at the end. By parsing the url, I can get the additional parameters I need. ie. deviceToken, deviceId, & deviceType
My request worked fine while the deviceToken parameter was smaller. Now the company providing me with my device token has made an excessively huge one. And now my request returns Bad Request 400.
AgTGERCBaS3d8n2QWxF9EtwcLktIoygoXpc8Y42ObZWja3RSjN%2bFBeshaY4ASainj3MusBbVopXbUFQrrgXUOSkAbOA7tChNKOFNKQ2gB8sEfCe5Du9BZufW4bAP5312MKRqV8g%3d
If I remove these characters from the end of my query string, the request goes through successfully. "Q2gB8sEfCe5Du9BZufW4bAP5312MKRqV8g%3d"
I have done some research and discovered that the max for a parameter is 255 characters. My device token is only 140.
To add to my confusion, if I change the order of the deviceId and deviceToken parameters, then I must shorten the deviceId parameter to send successfully. Another point of interest is that if I try to shorten any of the other parameters, then my request still fails. I must always shorten the second parameter.
Has any one else had similar problems and found a solution?
How can I send my looong device token via a query string at the end of a path?
It's possible it's the length of your query string itself, not the parameter.
Try adding
<httpRuntime maxQueryStringLength="2500"
maxUrlLength="2500" maxRequestLength="2500" />
to your config and see if the error persists
I found the solution after posting this question. Such is often the way.
The device token was getting saved to a field that was NVARCHAR(100). Previously this was enough. The device token can now be at least 140 characters. I changed the field to NVARCHAR(255). The problem is fixed. No more Bad Request 400.

how to pass a decimal number to a rest web service

I am wanting to pass a decimal number (1.23) to my WCF-REST web service.
I keep getting a 'resource cannot be found' error. I expect that I'm encountering some security feature of IIS where urls that contain a dot are a resource. Does anyone have a suggestion on how to pass a decimal number to my webservice?
Sample url... http://localhost/restdataservice.svc/echo/2.2
Operation Contract
[OperationContract]
[WebGet(UriTemplate = "echo/{number}")]
string Echo(string number);
And implementation
public string Foo(string number)
{
return number;
}
You should look at IIS log to see the problem. One thing that can cause such problem is UrlScan. It has UrlScan.ini config file, where you can find AllowDotInPath parameter. If it is set to 0, requests such as above would be rejected. Just change it to 1 (but don't forget to ensure, that you don't allow directory traversals by rejecting urls with ..).
You may want to consider sending that number to your resource in a request representation using a POST request.
This should work fine out of the box, I cannot reproduce this problem with a similar basic setup - are you sure the default route for your echo method is set correctly?
I had a similar problem calling a method on a WCF OData Server.
The problem was that a decimal parameter requires an 'm' at the end.
http://localhost/restdataservice.svc/echo/2.2m

Why is this method not allowed for WebInvoke, but OK for WebGet?

Can someone explain the reason behind this or how it works? IF I do a WebInvoke on the following, it fails (says method not allowed, but if I do a WebGet, it passes). I just want to understand why?
[OperationContract]
[WebGet(UriTemplate = "login/{username}/{password}", ResponseFormat =
WebMessageFormat.Json)]
string Login(string username, string password);
The above code, just returns a hard coded string. (No conditional logic)
EDIT: Rewritten somewhat now I've reread the question...
WebInvoke allows you to specify which verb will be allowed, defaulting to POST. WebGet requires the client to be using a GET request. In either case, if the wrong verb is used, you'll get "method is not allowed". You were using the browser, so it was making a GET request, so a normal POST-only WebInvoke would reject it, whereas a WebGet would allow it. You could specify Method="GET" in the WebInvoke attribute declaration to allow GET, of course.

Categories