How to send post request from Android to a C# web API - c#

As my question states, I have a c# web API (MVC.NET web API 2) with a controller having a POST method that receives the post request (Json String) and simply writes it to a log file (for simplicity just making sure I have received it from android app). On the other hand I have an Android app that uses Volley to send a post string request to a mentioned API. I have used a couple of approaches such as using Stringrequest, JsonObject request etc but none of which seemed to work (I get 400 error code). I have tested the API in postman and everything is okay...I'm receiving the posted string in the API post method. Please help me guys or my job is hanging in the balance if I fail to get this task accomplished. Thanks in advance. My code is attached herewith below:
Web API controller
public class TestController : ApiController
{
// POST: api/test
[HttpPost]
[Route("api/test")]
public void Post()
{
string param = Request.Content.ReadAsStringAsync().Result;
EventLogger.writeErrorLog("Posted payload --> " + param)
}
}
Android code to send post
private void postDummy() {
String url = "http://10.0.2.2:1106/api/test";
RequestQueue requestQueue = Volley.newRequestQueue(this);
JSONObject jsonBodyObj = new JSONObject();
try{
jsonBodyObj.put("payload", "XYZ");
}catch (JSONException e){
e.printStackTrace();
}
final String requestBody = jsonBodyObj.toString();
Log.d("Json :--> ", requestBody);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
url, null, new Response.Listener<JSONObject>(){
#Override public void onResponse(JSONObject response) {
Log.i("Response",String.valueOf(response));
}
}, new Response.ErrorListener() {
#Override public void onErrorResponse(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
}
}){
#Override
public String getBodyContentType() {
return "application/json";
}
#Override
public byte[] getBody() {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
requestBody, "utf-8");
return null;
}
}
};
requestQueue.add(jsonObjectRequest);
}
Error Log from Android Studio
D/JsonĀ :-->: {"payload":"XYZ"}
E/Volley: [561] BasicNetwork.performRequest: Unexpected response code 400 for http://10.0.2.2:1106/api/test
E/Volley: 1 6.onErrorResponse: Error:
Postman test result
A screenshot for Web API test result

My case was very similar, C# web API return 400 when i call from android volley (whit custom headers). The solution (in my case) was simple, just i removed "Content-Type" "application/json" and let volley do whatever it do.
My code working:
ANDROID:
try{
JSONObject postparams = new JSONObject();
postparams.put("param1", "1");
postparams.put("param2", "2");
String URL=sesion.urlAire + "api/adjunto/getArchivo";
RequestQueue requestQueue= Volley.newRequestQueue(context);
JsonObjectRequest objectRequest=new JsonObjectRequest(
Request.Method.POST,
URL,
postparams,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//do something
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//do something
}
}
)
{
#Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
//headers.put("Content-Type", "application/json");
headers.put("authorization", sesion.token);
return headers;
}
};
requestQueue.add(objectRequest);
}catch (Exception e){}
C#:
[HttpPost]
public CustomObject getArchivo(PostData postData) {
return new CustomObject{ param1="1" };
}

Related

How to Decrypt the payload on server during authorization and map to model in web api

I've been trying to follow this answer trying to decrypt the Encrypted payload during the Authorization before it gets model mapped to the controller.
From the client only the Payload will be encrypted and on the server side I'm trying to decrypt. Thing is the entire Response.content cannot be decrypted as only the payload needs to decrypted.
Inside the content we're receiving the payload in Result and when I'm trying to change that it is showing that it is read only and I couldn't see any other options. In the image above the result is not encrypted yet, I was testing to see if we can change that.
I've done it in another way where I'll be passing the entire encrypted string to the controller and then decrypting it and mapping to model inside the controller like this:
[Route("api/xxxxxx")]
[HttpPost]
public HttpResponseMessage PostTest(string encryptedValue)
{
//creating an instance of class
HttpResponseMessage response = new HttpResponseMessage();
try
{
string decryptJson = AES.DecryptString(encryptedValue);
Model list = JsonConvert.DeserializeObject<Model>(decryptJson);
//rest of the operation
}
//to catch exceptions if any
catch (Exception ex)
{
output.Success = false;
output.Message = Literals.GetErrorMessage(ex.Message);
}
//creating response
response = Request.CreateResponse(HttpStatusCode.OK, JObject.FromObject(output));
//returning response
return response;
}
This is working as expected but I'm trying if at all it's possible to do this at Authorization instead of doing it individually to every controller.
Any advice is appreciated.
Using new StringContent() to add the decrypted string to the Response.Content:
public class LogAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
try
{
var resoponseContent = actionContext.Request.Content.ReadAsStringAsync();
var result = resoponseContent.Result;
var decryptedString = AESEncryptDecrypt.DecryptStringAES(result);
actionContext.Request.Content = new StringContent(decryptedString, Encoding.UTF8, "application/json");
var checkingDecryptedResponseContent = actionContext.Request.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
}
}
}
After updating the new content the Model will be auto mapped to the controller.
[LogAttribute]
[Route("api/xxxxxx")]
[HttpPost]
public HttpResponseMessage PostTest(Model data)
{
//creating an instance of class
HttpResponseMessage response = new HttpResponseMessage();
try
{
//rest of the operation
}
//to catch exceptions if any
catch (Exception ex)
{
output.Success = false;
output.Message = Literals.GetErrorMessage(ex.Message);
}
//creating response
response = Request.CreateResponse(HttpStatusCode.OK, JObject.FromObject(output));
//returning response
return response;
}

API POST call from Console Application

How to do the REST API POST Call from the console Application ?
I want to pass the class from the Console application to the REST API. My below code is working if I have to do the GET call but not for the POST. It is hitting the API but in the Parameter it is not passing anything.
API
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
//public void Post([FromBody]string value)
//{
//}
public void Post([FromBody]Student value)
{
}
}
Console Application
static async Task CallWebAPIAsync()
{
var student = new Student() { Id = 1, Name = "Steve" };
using (var client = new HttpClient())
{
//Send HTTP requests from here.
client.BaseAddress = new Uri("http://localhost:58847/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", student);
if (response.IsSuccessStatusCode)
{
}
else
{
Console.WriteLine("Internal server Error");
}
}
}
The Same is working if I call from fiddler.
User-Agent: Fiddler
Content-Length: 31
Host: localhost:58847
Content-Type: application/json
Request Body:
{
"Id":"1",
"Name":"Rohit"
}
This is working for me.
public async Task CallWebAPIAsync()
{
var student = "{'Id':'1','Name':'Steve'}";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:58847/");
var response = await client.PostAsync("api/values", new StringContent(student, Encoding.UTF8, "application/json"));
if (response != null)
{
Console.WriteLine(response.ToString());
}
}
You are not serializing the student object.
You can try to send a StringContent
StringContent sc = new StringContent(Student)
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", sc);
if this doesn't work (a long time I used StringContent).
Use NewtonSoft sterilizer
string output = JsonConvert.SerializeObject(product);
HttpResponseMessage response = await client.PostAsJsonAsync("api/values", output);
To be honest I don't know. It seems like your StringContent did not sterilize it to UTF8 which your restful API is trying to do by default. However, your console application should also do that by default.
The issue seemed to be that the restful API could not bind the byte data and therefor not assign the data to your class Student in the restful API.
What you can try to do is add following code before you make your post to API:
var encoding = System.Text.Encoding.Default;
It will tell you what is your default encoding type. It could be that UTF8 is not the default encoding for some reason.

Call MVC web API controller method from client

I am trying to consume/call an MVC Web API controller method, which will be used to upload a file. I am struggling to call it from my MVC controller.
Here's my code for the API Controller
public class ImportController : ApiController
{
[HttpPost]
public bool PutImportFile(byte[] fileToBeImported, string nameOfTheFileToBeImported)
{
// I am doing file saving stuff here
}
}
I have tested the file saving part by changing the method to HttpGet and its working when I called it directly from the browser. I removed the parameters for that.
However, I am not able to figure out how to call it from a client.
I have tried below.
public class ImportFileModel
{
public byte[] FileToBeImported { get; set; }
public string NameOfTheFileToBeImported { get; set; }
}
The below code will accept a file from the browser uploaded by user and post it to the API controller to save the file.
[HttpPost]
public async Task<JsonResult> Upload()
{
byte[] file;
string fileName = string.Empty;
if (Request.Files.Count > 0)
{
try
{
fileName = Request.Files[0].FileName;
using (MemoryStream ms = new MemoryStream())
{
Request.Files[0].InputStream.CopyTo(ms);
file = ms.ToArray();
}
//To do: get url from configuration
string url = "http://localhost:(port)/api/Import/PutImportFile";
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/bson"));
ImportFileModel request = new ImportFileModel
{
FileToBeImported = file,
NameOfTheFileToBeImported = fileName
};
MediaTypeFormatter bsonFormatter = new BsonMediaTypeFormatter();
var result = await client.PostAsync(url, request, bsonFormatter);
HttpResponseMessage response = result.EnsureSuccessStatusCode();
}
}
catch (Exception ex)
{
// exception handling here
}
}
return Json(true, JsonRequestBehavior.AllowGet);
}
It ends up in an exception at the last line.
HttpResponseMessage response = result.EnsureSuccessStatusCode();
Throwing 404 not found error.
I have also tried the same from a console application using HttpWebRequest. It also throws the same error.
Your Web API method PutImportFile is setup to receive two values, not a single model; hence, your HttpClient call is not recognized (no matching route found). Change your Web API method to receive a model:
public class ImportController : ApiController
{
[HttpPost]
public bool PutImportFile(ImportFileModel fileInfo)
{
//Your code to save the file...
}
}

Pass parameters to C# Http client post

I am learning to create a RESTful API with a client, but am struggling with passing user input to the post. My controller is fine as I can send data to db (tested with Swagger) but on the client side the debugger is giving me an error on my PostAsJsonAsync. I think it probably has to do with the routing. Here is my post code from my client:
static async Task AddAsync(ForumPost fp)
{
try
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:7656/");
client.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
ForumPost thePost = new ForumPost() {
Subject = fp.Subject,
Message = fp.Message};
HttpResponseMessage response = await client.PostAsJsonAsync("post", thePost);
if (response.IsSuccessStatusCode)
{
Uri uri = response.Headers.Location;
Console.WriteLine("URI for new resource: " + uri.ToString());
}
else
{
Console.WriteLine(response.StatusCode + " " + response.ReasonPhrase);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.ReadLine();
}
}
and the relevant bit of the controller
[HttpPost]
// POST: api/Forum
[Route("post")]
public void PostNewMessage (string subject, string message)
{
if (ModelState.IsValid)
{
ForumPost p = new ForumPost(subject, message);
db.ForumPosts.Add(p);
db.SaveChanges();
}
}
I have looked around at various different but similar questions here on SO but struggling to understand. I have tried putting placeholders in the route but maybe I implemented it incorrectly? (that's if that is even the correct way to be thinking!) If anyone could help me out on this I would appreciate it.
When your Web API action parameters are simple types like strings, the parameter binding mechanism assumes they are coming from the query string. To infer that the values should come from the request body, just use your ForumPost class directly as your parameter instead of the individual string values:
[HttpPost]
// POST: api/Forum
[Route("post")]
public void PostNewMessage(ForumPost p)
{
if (ModelState.IsValid)
{
db.ForumPosts.Add(p);
db.SaveChanges();
}
}
Also note that ForumPost needs a parameterless constructor in order for the framework to know how to create an instance. Define it like this and you should be good:
public class ForumPost
{
public string Subject { get; set; }
public string Message { get; set; }
}

MVC4 WebApi adding ETag in Response Header

We have a REST Service created in Mvc4
I am trying to add ETag Header in the Response from my WebApi method. It is added in the Header collection without any error but when I check the response header in the Fiddler it is not there.
Here is the method that I used to write header in the response:
internal static HttpResponseMessage<T> GetResponse<T>(Tuple<T, Dictionary<string, string>> response)
{
HttpResponseMessage<T> httpResponse = new HttpResponseMessage<T>(response.Item1, HttpStatusCode.OK);
if (response.Item2 != null)
{
foreach (var responseHeader in response.Item2)
{
if (string.Compare(responseHeader.Key, "ETAG", StringComparison.OrdinalIgnoreCase) == 0)
{
httpResponse.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue("\"" + responseHeader.Value + "\"");
}
else
{
httpResponse.Headers.Add(responseHeader.Key, responseHeader.Value);
}
}
}
return httpResponse;
}
You can do it 2 ways, you can either set the ETag in an ActionFilter.OnActionExecuted method like this:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
actionExecutedContext.ActionContext.Response.Headers.ETag = new EntityTagHeaderValue(...);
}
But there's no way to easily pass the desired value from your controller to the ActionFilter. The second way is to change your WebAPI Action. Instead of returning a model type, return an HttpResponseMessage:
[HttpGet]
public HttpResponseMessage MyActionMethod() {
var result = // response data
var response = Request.CreateResponse<MyType>(HttpStatusCode.OK, result);
response.Headers.Add("Last Modified", result.Modified.ToString("R"));
response.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue(CreateEtag(result));
return response;
}

Categories