Why does my API return {} - c#

I've created a .NET API, but when I try to use it I'm getting strange results.
If I go to the URL of an API call in Chrome I get the result I would expect, in XML format, but if I try it in IE it downloads a file and when I open it it just says {} or sometimes [{},{},{},{}] if I try a call that returns an array.
I've also tried using a webclient.
WebClient web = new WebClient();
var data = web.DownloadString("http://myAPI.example.com/api/MyAPI/APIMethod?parameter1=hiImAParameter");
This also returns empty {}.
I've tried searching online, but I don't see any mentions of this problem anywhere. I'm sure I must be missing something somewhere, but I've tried looking over how I set up my API, and it all looks fine to me.
Edit:
Here's the response I get in Chrome.
<ArrayOfRoute xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/TimePointModel">
<Route>
<RouteId>11235</RouteId>
<RouteName>Fibonacci</RouteName>
<Status i:nil="true"/>
<Width>0</Width>
</Route>
</ArrayOfRoute>
It returns XML on Chrome because of Chrome's accept headers. It's supposed to return JSON on IE, but for some reason the JSON is empty.
This is in my api controller:
[AcceptVerbs("GET")]
public IEnumerable<Route> APIMethod(double parameter)
{
return new Manager(parameter).GetRoutes();
}
This is in my Global.asax.cs:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "APIMethod",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "APIMethod", id = System.Web.Http.RouteParameter.Optional }
);
}
Edit:
This works great when I do an API call which doesn't require parameters.
WebClient web = new WebClient();
var data = web.DownloadString("http://myAPI.example.com/api/MyAPI/SimpleAPIMethod");
I've been doing research, and I tried adding parameters like this:
NameValueCollection myQueryStringCollection = new NameValueCollection();
string myParameter = "hiImAParameter";
myQueryStringCollection.Add("parameter1", myParameter);
web.QueryString = myQueryStringCollection;
var data = web.DownloadString("http://myAPI.example.com/api/MyAPI/APIMethod");
I've noticed that the number of empty {} in my array matches the number of items in the array if I put the full url with the querystring into chrome. It just empties them out for my webclient.
I also tried adding
web.Headers[HttpRequestHeader.ContentType] = "application/json";
before making the call, but there's still no change in the result.
And I tried to follow that tutorial, but it's written for a console application and they're using HttpClient. I can't do that because I can't do Asynchronous calls. This is to be used by a website. That's why I'm using WebClient. I also tried using StreamReader with HttpWebRequest and HttpWebResponse, but it had the same problem as I've been encountered with WebClient.

Without more information, it's a bit hard to diagnose your issue. However, I would say that it is likely your web API is interpreting the expected response type and providing an empty result as it does not support responses of that type, such as happens with ASP .NET Web API websites.
In that sense, the DownloadString is indicating it is expecting a text/html response. You should probably download the Microsoft ASP .NET Web API Client Libraries with NuGet. This library will give you HttpClient which has support for making the queries you want to make with responses such as application/json and application/xml.
You can view a tutorial on how to do the calls right here.
If you want it to work from your web browser, you need to ensure the Accept header field is correct, as you mentioned. Ensure it is being communicated with IE by using something like Fiddler.

I figured out what the problem was. I needed to add [DataMember] attributes to the attributes of the items in the list. I didn't realize it was left out of the return type of that call.

Related

Why is postman sending form data in an HTTP GET?

I received a Postman json collection from an API vendor that works perfectly, but has something mystifying to me: The request is in a GET format, yet there is an x-www-form-urlencoded body.
URL: https://login.microsoftonline.com/d1e<secret>9563/oauth2/token
And when I look at the postman-generated c# code, the mystery continues:
var client = new RestClient("https://login.microsoftonline.com/d1e...d3/oauth2/token");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "c06bb...79");
request.AddParameter("client_secret", "7~u...D");
request.AddParameter("resource", "https://vault.azure.net");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Note the AddParameter constructions for a GET call. To me, this must be a slight-of-hand for merely adding those values to the querystring. But when I look at the postman console I see:
In the postman console I would have expected to see those params appended to the url as a querystring, and then everything would have made sense. But you can see that it's a bonafide Request Body.
When I make GET calls in my c# code I like to use the simple yet solid WebClient object to call the DownloadString() method. But this method is only for GETs and there's no way to send a form-post style body, understandably.
Is postman truly sending a GET with all those values being appended to the url as a querystring? And should I do the same in my DownloadString() call? Or is there something else going on here? Should I instead, in my c#, be calling the UploadString() method and sending a form post BODY as a GET??
Http protocol supports adding a body to a request, but the WebClient class you use doesn't. Presumably because it isn't considered the norm.
I'm sure there's good reasons for Microsoft using it in the OAuth flow though. Those guys normally do things right!
HTTP GET with request body
API is just an abstraction , you can send what ever you want to the API . It depends on the implementation , how the server handles these data.
Some services considers only what it requires and ignores other information
some services considers the entire requests and validates that it has only the allowed data. what should be allowed depends on the service
Postman is just a client that sends data to server , its upto you to decide what all information it should send . If you dont need any body then keep it as none. if you need some thing then add it.

Autodesk Forge Error trying to access the API online

I have a problem loading a 3D model on an online server, the error shown is related to accessing the Forge API, locally works smoothly however when mounted on the server or a website is made marks the following error "Failed to load resource: the server responded with a status of 404 (Not Found)", then "onDocumentLoadFailure() - errorCode:7".
As I comment, what I find stranger is that, locally, it works. Attached the segment of the code where it displays the error.
function getAccessToken() {
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", '/api/forge/toke', false); //Address not found
xmlHttp.send(null);
return xmlHttp.responseText;
}
Thank you very much in advance.
Are you sure the code you're running locally and the code you've deployed are really the same?
The getAccessToken function doesn't seem to be correct, for several reasons:
First of all, there seems to be a typo in the URL - shouldn't it be /api/forge/token instead of /api/forge/toke?
More importantly, the HTTP request is asynchronous, meaning that it cannot return the response immediately after calling xmlHttp.send(). You can find more details about the usage of XMLHttpRequest in https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest.
And finally, assuming that the function is passed to Autodesk.Viewing.Initializer options, it should return the token using a callback parameter passed to it (as shown in https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/viewer_basics/initialization/#example).
With that, your getAccessToken should probably look more like this (using the more modern fetch and async/await):
async function getAccessToken(callback) {
const resp = await fetch('/api/forge/token');
const json = await resp.json();
callback(json.access_token, json.expires_in);
}
I've already found the issue. When I make the deploy I have to change the url where the request is made for the public or the name of the domain. For example: mywebsite.com/aplication-name/api/forge/token.

How to send GET request from internet explorer to Asp.Net Core API?

I am developing Asp.Net Core 3.1 API, Everything working as expected when I send a GET request from google chrome, Edge, Postman. But when I send GET request from internet explorer it starts to download a file default.json with the content as the response of GET request.
Defualt Action method:
public IEnumerable<string> Get()
{
return new string[] { "Welcome" };
}
default.json content:
[
"Welcome"
]
I search on the internet but could not find anything useful.
FVI, I have the same observation when I run the API using visual studio or the deployed API on the server using IIS.
IE Version: 11.900.18362.0
So I have to questions.
Does IE not support this, Is this default behavior of IE?
If Yes then how can it be fixed?
This is IE default behavior, and comes down to it simply doesn't know how to treat content with mime types like */json, hence suggest a download.
Assuming this is for users in general, and you simply want to display the json data in a browser, you could convert the content server side to text.
public ContentResult Get()
{
var jsondata = new string[] { "Welcome" };
return Content(JsonSerializer.Serialize(jsondata));
}
If you are going to do something with the actual json data, which one usually does when consuming an api, you will use some kind of client side script (e.g. Ajax as in below sample, or similar) to get the content, and in those cases there won't be any problem, like the one you encountered.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/your-method', true);
xhr.onload = function (e) {
if (this.status == 200) {
var jsonstring = this.responseText;
// do something with the json string, e.g. JSON.parse(jsonstring)
}
};
xhr.send();
Here's a couple of posts that suggests to change the registry, though they won't be viable unless it is for a local computer of your own (and if it is, picking a browser that works out-of-the-box must be easier).
Display JSON in IE as HTML Without Download
How can I convince IE to simply display application/json rather than offer to download it?
Edit
As suggested in a comment, yet another option would be to change the mime type explicit:
Json response download in IE(7~10)

Is there a way to retrieve the String the way it is actually uploaded to the server (as a whole)?

I am currently working on a OAuth2 implementation. However I am stuck on an Error 401. It seems like there is something wrong with my post request that is supposed to retrieve the access token from the Company the User logged in to. This is my code:
internal void RequestAccessToken(string code)
{
string requestBody = "grant_type="+ WebUtility.UrlEncode(GRANTTYPE)+ "&code=" + WebUtility.UrlEncode(code)+"&redirect_uri="+ WebUtility.UrlEncode(REDIRECT_URI);
WebClient client = new WebClient();
client.Headers.Add("Authorization",HeaderBase64Encode(CLIENT_ID, SECRETKEY));
var response = client.UploadString("https://thewebsiteiamcallingto.com/some/api", requestBody);
var responseString = client.OpenRead("https://thewebsiteiamcallingto.com/some/api");
}
My Questions are:
Is there anything wrong with the way I try to make the POST request ?
Is there a way to retrieve the whole string that is posted to the URI using UploadString?
P.S. I have seen this post regarding the POST creation. However I find the async part to be too complicated for my case.
Since we dont know the api documentation, I would suggest you to make a postman request and view the actual request sent and response received, and secondly make a request using your method and capture using a utility like wireshark and compare the difference.

How to clean up existing response in webapi?

There is a authentication library that I have to use that helpfully does things like
Response.Redirect(url, false);
inside of it's method calls. I can't change this libraries code and it's fine for MVC style apps but in angular SPA -> WebApi apps this is just awful.
I really need a 401 otherwise I get into trouble with CORS when my angular scripts, using $http, try to call out to the auth server on another domain in response to the 302, that's if it even could as the Response.Redirect also sends down the object moved html and the angle brackets cause an error to be thrown.
Since I have to make the call to the auth library first the Response.Redirect is already in the response pipeline and so I need to clean it up to remove the body content and convert the 302 into a 401. I thought I could just:
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent("data");
}
but this just gets appended to the response and doesn't replace it plus I also need the Location: header which I can't seem to access via WebApi methods.
So instead I've had to do this in my ApiController:
var ctxw = this.Request.Properties["MS_HtpContext"] as HttpContextWrapper;
var ctx = ctxw.ApplicationInstance.Context;
var url = ctx.Response.RedirectLocation;
ctx.Response.ClearContent();
return new HttpWebResponse(StatusCode.UnAuthorized){
Content = new StringContent(url);
}
But this seems terrible and counter to webapi "feel". Plus I'm tied to the controller in doing this. I can't get the wrapper in a MessageHandler for example.
What I'd like to do is monitor the response for a given route in a message handler or in an AuthorizationFilterAttribute, if its a 302, I want to read it's headers, take what I want, wipe it and replace it with my own "fresh" response as a 401. How can I do this?
You might want to write your own ActionFilter and override its OnActionExecuted method where you can access HttpActionExecutedContext. From there, you can check response code, for example, and overwrite response with whatever you want.
Ref: https://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute.onactionexecuted%28v=vs.118%29.aspx#M:System.Web.Http.Filters.ActionFilterAttribute.OnActionExecuted%28System.Web.Http.Filters.HttpActionExecutedContext%29

Categories