Marco
Marco

Reputation: 23945

Constant BadRequest when posting JSON to endpoint using HttpClient

I am struggeling to consume an API endpoint (http://ogre.adc4gis.com/convertJson) that takes JSON data and returns a zip file. Visiting http://ogre.adc4gis.com shows the parameters the api expects.

It absolutely works using Postman and using Javascript, so I am wondering what I am doing wrong with in my c# code.

This an example JSON string, which can be posted to the API:

{
    "displayFieldName": "NUM_GES2_1",
    "fieldAliases": {
        "NUM_GES2_1": "NUM_GES2_1"
    },
    "geometryType": "esriGeometryPoint",
    "spatialReference": {
        "wkid": 102362,
        "latestWkid": 4647
    },
    "fields": [{
        "name": "NUM_GES2_1",
        "type": "esriFieldTypeString",
        "alias": "NUM_GES2_1",
        "length": 254
    }],
    "features": [{
        "attributes": {
            "NUM_GES2_1": "001-08"
        },
        "geometry": {
            "x": 32674408.2009,
            "y": 5790291.4659000002
        }
    }]
}

For completenes sake, this is the action method, that invokes the call to the bespoke api:

[HttpGet]
[Route("{id:int}/Attributive")]
public async Task<IActionResult> GetFeatureClass(int id)
{
    var client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    //get token from internal api
    var token = await _tokenService.RefreshToken();
    //dummy address that returns a single object for development
    var tokenObjString = await client.GetStringAsync(
        $"http://url.to/MapServer/{id}/query?where=1%3D1&objectIds=4&f=pjson&token={token}"
    );

    var data = await PostDataToOgrService(tokenObjString);

    var response = File(data, "application/octet-stream", "FeatureClass.Zip");
    return response;
}

Here comes the part where the actual call to the api is made. Payload is the escaped json string. The get rid of all the escapes, I've just converted it to an object and then back again.

public async Task<byte[]> PostDataToOgrService(string payload)
{
    var client = new HttpClient();
    var newJson = JsonConvert.DeserializeObject(payload);
    var pairs = new Dictionary<string, object>
    {
        { "json", newJson }
    };
    var json = JsonConvert.SerializeObject(pairs);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await client.PostAsync("http://ogre.adc4gis.com/convertJson", content);

    if (response.IsSuccessStatusCode)
    {
        return await response.Content.ReadAsByteArrayAsync();
    }
    else
    {
        throw new Exception("Something went wrong");
    }
}

Every call returns an Error 400 Bad Request:

{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.NoWriteNoSeekStreamContent, Headers:
{
  Date: Fri, 08 Sep 2017 06:15:01 GMT
  ETag: W/"27-Ag3Jnk3T/v6dECAccJTzg4aO/wA"
  X-Powered-By: Express
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: POST
  Access-Control-Allow-Headers: X-Requested-With
  Access-Control-Expose-Headers: Content-Disposition
  Content-Length: 39
  Content-Type: application/json; charset=utf-8
}}

Things I've tried:

Do not convert the payload to an object and just use it as it is:

var pairs = new Dictionary<string, string>
{
    { "json", payload }
};
var json = JsonConvert.SerializeObject(pairs);
var content = new StringContent(json, Encoding.UTF8, "application/json");

Build the string content raw:

payload = "{ \"json\": " + payload + "}";

Use "application/x-www-url-formencoded" as Content-Type

I am a total loss here. What am I doing wrong?

EDIT As requested, here are the request headers from a successfull postman request:

POST /convertJson HTTP/1.1
cache-control: no-cache
Postman-Token: 2976abca-6725-43ce-873e-907c12a9fdee
Content-Type: multipart/form-data; boundary=--------------------------829328978588990941765750
User-Agent: PostmanRuntime/6.1.6
Accept: */*
Host: ogre.adc4gis.com
accept-encoding: gzip, deflate
content-length: 629
Connection: keep-alive

Upvotes: 1

Views: 6736

Answers (1)

Marco
Marco

Reputation: 23945

In the end I simply rebuild the payload from the website itself using Fiddler to capture it in the first place and it worked.

var contentString = "json=" + System.Web.HttpUtility.UrlEncode(JsonConvert.SerializeObject(newJson));
var content = new StringContent(contentString, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await client.PostAsync("http://ogre.adc4gis.com/convertJson", content);

Upvotes: 3

Related Questions