C# JSON POST connect (Method not allowed) - c#

I have problem with connect to JSON server.
In user-manual:
The Interface is implemented as a standard HTTP Service. Using the service requires an authentication through the “Login” method. A Session Id is returned on success which has to be passed on every function call unless otherwise stated.
The expected data format when sending or receiving data is JSON.
All data must be passed using POST.
The session Id is of type Guid
Example:
Login
Description: Used to authenticate a user.
Url: /Login
Signature: Guid Login(string id, string username, string password)
END OF MANUAL
I wrote this code:
var webAddr = "https://xxx/Login";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"Login\":[{"
+ "\"id\" : 1213213,"
+ "\"username\" : asdasdasd,"
+ "\"password\" : \"adasdsadasd\","
+ "}]}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
MessageBox.Show(result.ToString());
}
And message is: Method not allowed.
How can I send and recive data from this serwer?

You are formatting your JSON incorrectly. The JSON should look like this:
{"id":"1213213","username":"asdasdasd","password":"adasdsadasd"}
Notice: quotes around each name and value. And "Login" should not be part of the JSON.
However, the problem is really that you are doing this all manually. Instead, let .NET format the JSON for you and handle the HTTP request. To do this, create a structure for the arguments:
class Login
{
public string id { get; set; }
public string username { get; set; }
public string password { get; set; }
}
Use NuGet to add references to "Json.Net" and "Microsoft ASP.NET Web API Client Libraries." Now you can write this:
static async Task Login()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://www.censored.de/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(30);
Login l = new Login() { id = "12345", password = "abcde", username = "safsdfasdf" };
var sTemp = JsonConvert.SerializeObject(l); // DEBUG: Just so I can see the JSON
var response = await client.PostAsJsonAsync("/censored/Service.svc/Login", l);
Guid g;
if (response.IsSuccessStatusCode)
{
g = await response.Content.ReadAsAsync<Guid>(); // This gives you the GUID
}
//DEBUG:
// var rawResponse = await response.Content.ReadAsStringAsync();
// Console.WriteLine(response);
}
}
Notice that I used "async" and "await" keywords. If you are not familiar with calling an async function, you can change the "await" line to this temporarily:
var response = await client.PostAsJsonAsync("/censored/Service.svc/Login", l).Result;
This results in a 400 Bad Request with this message.
{"ErrorMessage":"Die Anmeldedaten sind ungültig.","StackTrace":null}
Which Google tells me means that the credentials are wrong. I assume that is the response one would expect with this user/password combination.

Related

C# REST API File Upload from Client Code gets 400 Bad Request

I have a Rest API written in .Net core that accepts a File as input as Multipart/Form-data. The API works absolutely fine when I run it from Swagger/Postman.
Here is the API endpoint.
[HttpPost("CreateStudy")]
public ActionResult CreateStudy([FromForm] APIRequest request)
{
// rest of the code
Also here is the APIRequest object. it has only one property which is IFormFile Type.
public class APIRequest
{
public IFormFile XMLFile { get; set; }
}
So far it works well. The problem is that I am trying to write a client side code that will call this API and pass the File from C# code.
But I am always getting a 400-Bad request in the client code.
This is the client code I am trying with.
public string CallServiceWithFileAsync(string EndPointURL, string FilePath)
{
string ResultStatusCode;
Uri uri = new Uri(EndPointURL);
var Client = new HttpClient();
Client.DefaultRequestHeaders.Clear();
//Prepare Message
HttpRequestMessage Message = new HttpRequestMessage();
Message.Method = HttpMethod.Post;
Message.Headers.Add("Accept", "application/octet-stream");
Message.RequestUri = new Uri(EndPointURL);
using (Stream fileStream = File.OpenRead(FilePath))
{
var content = new StreamContent(fileStream);
var response = Client.PostAsync(uri, content);
ResultStatusCode = response.Result.StatusCode.ToString();
}
return ResultStatusCode;
}
What am I doing wrong here? What is the correct way of sending a file into REST endpoint ?
[FromForm] expects an accept header with application/x-www-url-formencoded. If this is not the case, check your output-logs to see why the request is not processed.

Error converting JSON response from backend to type with JsonConvert.DeserializeObject

So I am having this weird problem with deserializing a response from my BackEnd, The request works fine and the BackEnd succesfully responds with a result.
This is the error I get:
'Error converting value "{"Succes":true,"AuthKey":"$2a$13..."}" to type 'FrontEnd.LoginUserResponse'. Path '', line 1, position 96.'
The code I am using to make the HTTP call and deserialize the string:
public async Task<bool> loginUser(LoginUserData login)
{
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
string response2 = responseMessage.Content.ReadAsStringAsync().Result;
LoginUserResponse response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
if (response.Succes)
{
return true;
}
else { return false; }
}
I tried making a response2 to check the value and I have noticed it does something weird with 3 backslashes. This might be the reason why this is occuring.
This is response2 that visual studio shows when I click the magnifying glass:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
When I hover on visual studio:
https://imgur.com/a/jUyLz6d
This is the Class that it is converting to
public class LoginUserResponse
{
[JsonProperty("succes")]
public bool succes { get; set; }
[JsonProperty("authkey")]
public string authkey { get; set; }
}
The Backend code:
[HttpPost]
[Route("login")]
public string Login([FromBody]LogInData logInData)
{
IReadUser.LogInRequest request = new IReadUser.LogInRequest(logInData);
IReadUser.LogInResponse backResponse = readUser.logIn(request);
LogInResponse response = new LogInResponse();
response.succes = backResponse.Succes;
response.authkey = backResponse.AuthKey;
return JsonConvert.SerializeObject(response);
}
EDIT // SOLUTION
Ok so, the front-end was fine, it was my backend code sending a double serialised string. I used
return JsonConvert.SerializeObject(response);
When I also could have used
return response;
So if you every get an error like this, it's probably the backend doubling up on the serialization.
Thanks for all the help!
Couple of things:
1. you should await responseMessage.Content.ReadAsStringAsync():
public async Task<bool> loginUser(LoginUserData login)
{
var httpContent = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var responseMessage = await httpClient.PostAsync("http://ip/webBackEnd/api/user/login", httpContent);
var response2 = await responseMessage.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<LoginUserResponse>(response2);
return response.Succes
}
And 2. Based on your image it looks like the response from your backing service is being serialized twice.
1:
"{\"Succes\":true,\"AuthKey\":\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\"}"
2:
"\"{\\\"Succes\\\":true,\\\"AuthKey\\\":\\\"$2a$11$tQCw4zGGd2J2fXAxAN68Ruu3xheTuMKq4EHbeLtc9DAa2rgzJe8bS\\\"}\""
now to deserialize you have to do it twice
var s = JsonConvert.DeserializeObject<string>(response2);
var response = JsonConvert.DeserializeObject<LoginUserResponse>(s);
Probably best to fix the service if that's actually what is happening

Custom header in HttpClient POST request not working

I am trying to send a POST request when using HttpClient. When I run the code I am getting an unauthorized response. But I am able to get it to work in PostMan. Below is my current code snippet and pictures of what I am trying to perform. I'd like to add I am trying to send a json string in my body.
using (HttpClient client = new HttpClient())
{
var connectionUrl = "https://api.accusoft.com/prizmdoc/ViewingSession";
var content = new Dictionary<string, string> { { "type", "upload" }, { "displayName", "testdoc" } };
// Serialize our concrete class into a JSON String
var stringPayload = JsonConvert.SerializeObject(content);
// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
//client.DefaultRequestHeaders.Add("Acs-Api-Key", "aPsmKCmvkZHf9VakCmfHB8COmzRxXY5FDhj8F1FU1IGmQlOkfjiKESKxfm38lhey");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Acs-Api-Key", "aPsmKCmvkZHf9VakCmfHB8COmzRxXY5FDhj8F1FU1IGmQlOkfjiKESKxfm38lhey");
// Do the actual request and await the response
var httpResponse = httpClient.PostAsync(connectionUrl, httpContent).Result;
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
// Do something with response. Example get content:
var connectionContent = httpResponse.Content.ReadAsStringAsync().Result;
}
else
{
// Handle a bad response
return;
}
}
}
You're using two HttpClients when you only need to use one.
using (HttpClient client = new HttpClient())
and
using (var httpClient = new HttpClient())
The second one (httpClient) is doing the post but the authentication header has been added to client. Just remove the second one (httpClient) and make sure you use client.PostAsync(...) to send the request.
I'd also consider using await, rather than .Result (see why here) when sending the request:
var httpResponse = await client.PostAsync(connectionUrl, httpContent);
In addition to haldo's answer,
In your code, you are adding your Acs-Api-Key header as and Authorization header, meaning it ends up looking like Authorization: Acs-Api-Key (key) rather than Acs-Api-Key: (key) which is what you have in PostMan.
Instead of adding it as an Authorization header, just add it as a regular header.
client.DefaultRequestHeaders.Add("Acs-Api-Key","(key)");
Also something else that may cause issues is that you aren't wrapping your content in the "source" object like you are in PostMan. There are a couple ways of doing this
The first would be to simply wrap it in it's string format:
stringPayload = $"\"source\":{{{stringPayload}}}"
Or you can do it before you serialize by making your own object instead of having a Dictionary
var content = new PayloadObject(new Source("upload", "testdoc"));
var stringPayload = JsonConvert.SerializeObject(content);
// Send the request
class PayloadObject{
Source source {get; set;}
PayloadObject(Source source){
this.source = source;
}
}
class Source{
string type {get; set;}
string displayName {get; set;}
Source(string type, string displayName){
this.type = type;
this.displayName = displayName;
}
}

Passing a custom object to a REST endpoint with C#

I have a rest endpoint that accepts a single custom object parameter containing two properties.
Let's call the param InfoParam
public class InfoParam
{
public long LongVar { get; set; }
public string StringVar { get; set; }
}
My code I have is as follows:
infoParam.LongVar = 12345678;
infoParam.StringVar = "abc"
var myRequest = (HttpWebRequest)WebRequest.Create(url);
myRequest.Method = "POST";
var content = string.Empty;
using (var theResponse = (HttpWebResponse)MyRequest.GetResponse())
{
using (var stream = theResponse.GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
content = sr.ReadToEnd();
}
}
}
So I have the InfoParam variable, with the two values, but I can't figure out where to pass it in to the REST endpoint.
You need to turn the object into a stream of bytes that can be added to the Request stream - which will in turn be sent as the HTTP POST body. The format of these bytes needs to match what the server expects. REST endpoints usually expect these bytes to resemble JSON.
// assuming you have added Newtonsoft.JSON package and added the correct using statements
using (StreamWriter writer = new StreamWriter(myRequest.GetRequestStream()) {
string json = JsonConvert.SerializeObject(infoParam);
writer.WriteLine(json);
writer.Flush();
}
You'll probably want to set various other request parameters, like the Content-Type header.
You have to write it int the `Content (and set content-type). Check out How to: Send data by using the WebRequest class
The recommendation is to use System.Net.Http.HttpClient instead.
Please note that you should know what content the server expects ('application/x-www-form-urlencoded`, json, etc.)
The following snippet is from POST JSON data over HTTP
// Construct the HttpClient and Uri. This endpoint is for test purposes only.
HttpClient httpClient = new HttpClient();
Uri uri = new Uri("https://www.contoso.com/post");
// Construct the JSON to post.
HttpStringContent content = new HttpStringContent(
"{ \"firstName\": \"Eliot\" }",
UnicodeEncoding.Utf8,
"application/json");
// Post the JSON and wait for a response.
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(
uri,
content);
// Make sure the post succeeded, and write out the response.
httpResponseMessage.EnsureSuccessStatusCode();
var httpResponseBody = await httpResponseMessage.Content.ReadAsStringAsync();
Debug.WriteLine(httpResponseBody);
In your case the content would be something like this
HttpStringContent content = new HttpStringContent(
JsonConvert.SerializeObject(infoParam), // using Json.Net;
UnicodeEncoding.Utf8,
"application/json");

Why I can't correctly call this API passing an object into the request body?

I am pretty new in .NET and C# (I came from Java and Spring framework) and I am finding the following difficulties calling an API in the correct way.
I will try to explain my problem in details.
I have this API (defined into a project deployed into IIS. Note, this project contains also other APIs that I am calling without problem):
[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo(MailBuffer mailBuffer, string siglaIDUor)
{
..........................................................................
DO SOMETHING
..........................................................................
}
As you can see it take 2 input parameters:
MailBuffer mailBuffer that should be into the request body.
siglaIDUor that is into the URI.
I have some problem trying to pass the first parameter.
NOTE: I can't change the code of this API because was made by someone else and it can have impact on other thing.
Into another project deployed elsewhere I am trying to call the previous API (from a controller method) in this way:
[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public IHttpActionResult InviaMailAlProtocollo(string siglaIdUor)
{
Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);
// Ignore self signed certificate of the called API:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// Create the byte array[]
UTF8Encoding encoding = new UTF8Encoding();
byte[] mailContent = encoding.GetBytes("TEST");
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
string jsonRequest = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/ABC123";
// Setting my credentials:
credCache.Add(new Uri(jsonRequest), "NTLM", myCreds);
HttpWebRequest spRequest = (HttpWebRequest)HttpWebRequest.Create(jsonRequest);
spRequest.Credentials = credCache;
spRequest.UserAgent = "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0";
spRequest.Method = "POST";
spRequest.Accept = "application/json;odata=verbose";
spRequest.ContentType = "application/json; charset=UTF-8";
// Create and set the stream:
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
newStream.Close();
// Obtain the response from the API:
HttpWebResponse endpointResponse = (HttpWebResponse)spRequest.GetResponse();
string sResult;
JArray jarray;
// Parse the response:
using (StreamReader sr = new StreamReader(endpointResponse.GetResponseStream()))
{
sResult = sr.ReadToEnd();
jarray = JArray.Parse(sResult);
//JObject jobj = JObject.Parse(sResult);
}
Console.WriteLine(jarray);
return Ok(jarray);
}
The problem is that when this method call my API the received MailBuffer mailBuffer input parameter is null (I see it debuggin my API and calling it).
I suspect that the problem could be related to this code section of my call:
// Create and set the stream:
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
newStream.Close();
Probably I am trying to attach the wrong thing into the body of my request (the byte[] mailContent instead of the entire MailBuffer content object).
NOTE: To perform this call I have to use HttpWebRequest.
So, what is wrong? What am I missing? How can I fix this issue putting the entire MailBuffer content object into the body request and allowing my called API to retrieve it as input parameter?
The other project should make sure that the request is made with properly formatted data expected by the other API.
Right now you are sending just the raw bytes of the test email in the body of the request
//...
// Create the byte array[]
UTF8Encoding encoding = new UTF8Encoding();
byte[] mailContent = encoding.GetBytes("TEST");
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
//...
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length); //<---HERE ONLY SENDING encoding.GetBytes("TEST")
while the other endpoint is expecting data that can be deserialized to MailBuffer
Here is the portion of code that should be refactored to send the correct data
//...
UTF8Encoding encoding = new UTF8Encoding();
// Create the MailBuffer object that have to be passed to the API into the request body:
var content = new MailBuffer() {
Nome = "blablabla",
Buffer = encoding.GetBytes("TEST")
};
//convert model to JSON using Json.Net
var jsonPayload = JsonConvert.SerializeObject(content);
byte[] mailContent = encoding.GetBytes(jsonPayload); //<---CORRECT CONTENT NOW
spRequest.ContentLength = mailContent.Length;
Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);
//...
Finally I would suggest using the simpler API of HttpClient to make the request. That however would be entirely up to your preference.
Here is an example of same call using HttpClient
[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public async Task<IHttpActionResult> InviaMailAlProtocollo(string siglaIdUor) {
Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);
// Ignore self signed certificate of the called API:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
string requestUri = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/" + siglaIdUor;
// Setting my credentials:
credCache.Add(new Uri(requestUri), "NTLM", myCreds);
var handler = new HttpClientHandler {
UseDefaultCredentials = true,
Credentials = credCache
};
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("UserAgent", "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json;odata=verbose"));
// Create the MailBuffer object that have to be passed to the API into the request body:
var buffer = new MailBuffer() {
Nome = "blablabla",
Buffer = Encoding.UTF8.GetBytes("TEST")
};
//convert model to JSON using Json.Net
var jsonPayload = JsonConvert.SerializeObject(buffer);
var mailContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
// Obtain the response from the API:
var response = await client.PostAsync(requestUri, mailContent);
if (response.IsSuccessStatusCode) {
var jarray = await response.Content.ReadAsAsync<JArray>();
Console.WriteLine(jarray);
return Ok(jArray);
}
return BadRequest();
}
Use [FromBody] parameter.
[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo([FromBody]MailBuffer mailBuffer, string siglaIDUor)
{
..........................................................................
DO SOMETHING
..........................................................................
}
Also try passing MailBuffer as JSON object, it will be automaticly converted to MailBuffer object when you pass this from body.
If this won't work switch MailBuffer object in method with similar object and then map this object to MailBuffer.
You can give a try with HttpClient (using System.Net.Http)
private static readonly HttpClient client = new HttpClient();
// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;
var values = new Dictionary<string, object>
{
{ "mailBuffer", content },
{ "siglaIDUor", siglaIdUor }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("/api/XXX/InviaAlProtocollo/ABC123", content);
var responseString = await response.Content.ReadAsStringAsync();

Categories

Resources