Encryption/Decryption through DelegatingChannel (MessageHandler) - c#

I am trying to use DelegatingChannel (MessageHandler) to decrypt the incoming message and encrypt the outgoing message. Code looks like this, but with some placeholder i couldn't figure out how to achieve.
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var inEncryptedMessage = request.Content.ReadAsOrDefault<EncryptedMessage>();
Message inMessage = inEncryptedMessage.Decrypt();
var newContent = new StringContent(inMessage.Data, Encoding.UTF8, "text/json");
newContent.Headers.ContentType = request.Content.Headers.ContentType;
request.Content = newContent;
return base.SendAsync(request, cancellationToken).ContinueWith(
task =>
{
HttpResponseMessage response = task.Result;
// need to serialize the data in response.Content as json
var outMessage = new Message {
Data = ... // but don't know how
};
var outEncryptedMessage = outMessage.Encrypt();
response.Content = new ObjectContent(outEncryptedMessage);
return response;
});
}
public class Message
{
public string Data { get; set; }
}
public class EncryptedMessage
{
public byte[] Key { get; set; }
public byte[] Message { get; set; }
}
I want to pass JSON string in the request to the Operation, and therein deserialise the JSON into object. But failed... But object was created, but all the properties are null.
So which stoped me to think about response aspect. But i still find the difficulties in read object from response.Content.
Please give me some suggestion if you have the answer.
Thanks a lot,
M

If I'm not wrong you could try to implement a DecryptOperationHandler : HttpOperationHandler<HttpRequestMessage>, <HttpRequestMessage>
This should decrypt your incoming message before passing it to the resource itself
Encryption could be done using a EncryptOperationHandler : HttpOperationHandler<HttpResponseMessage>, <HttpResponseMessage> which gets executed after your resource method has been executed

Related

Integration Testing multipart/form-data c#

I have trouble trying to create an integration test for my post call that accepts a viewmodel that has amongst other values, an IFormFile, which makes this call from an application/json to a multipart/form-data
My IntegrationSetup class
protected static IFormFile GetFormFile()
{
byte[] bytes = Encoding.UTF8.GetBytes("test;test;");
var file = new FormFile(
baseStream: new MemoryStream(bytes),
baseStreamOffset: 0,
length: bytes.Length,
name: "Data",
fileName: "dummy.csv"
)
{
Headers = new HeaderDictionary(),
ContentType = "text/csv"
};
return file;
}
My Test Method
public async Task CreateAsync_ShouldReturnId()
{
//Arrange
using var content = new MultipartFormDataContent();
var stringContent = new StringContent(
JsonConvert.SerializeObject(new CreateArticleViewmodel
{
Title = "viewModel.Title",
SmallParagraph = "viewModel.SmallParagraph",
Url = "viewModel.Url",
Image = GetFormFile()
}),
Encoding.UTF8,
"application/json");
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
//Act
var response = await httpClient.PostAsync($"{Url}", content);
//Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
int id = int.Parse(await response.Content.ReadAsStringAsync());
id.ShouldBeGreaterThan(0);
}
My Controller Method
[HttpPost]
public async Task<IActionResult> CreateArticleAsync([FromForm] CreateArticleViewmodel viewModel)
{
var id = await _service.CreateAsync(viewModel).ConfigureAwait(false);
if (id > 0)
return Ok(id);
return BadRequest();
}
It throws a BadRequest without getting inside the method.
The way you are posting the request contents to the API, in your code, is not correct.
When the API expects a FileInfo in the request payload, posting JSON content never works. You need to send the payload as MultipartFormData and not as JSON.
Consider following example.
This is a an API endpoint which expects and model with FileInfo in it as payload.
[HttpPost]
public IActionResult Upload([FromForm] MyData myData)
{
if (myData.File != null)
{
return Ok("File received");
}
else
{
return BadRequest("File no provided");
}
}
public class MyData
{
public int Id { get; set; }
public string Title { get; set; }
// Below property is used for getting file from client to the server.
public IFormFile File { get; set; }
}
This is pretty much the same API as yours.
Following is the client code which calls the above API with file and other model properties.
var apiURL = "http://localhost:50492/home/upload";
const string filename = "D:\\samplefile.docx";
HttpClient _client = new HttpClient();
// Instead of JSON body, multipart form data will be sent as request body.
var httpContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(filename));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// Add File property with file content
httpContent.Add(fileContent, "file", filename);
// Add id property with its value
httpContent.Add(new StringContent("789"), "id");
// Add title property with its value.
httpContent.Add(new StringContent("Some title value"), "title");
// send POST request.
var response = await _client.PostAsync(apiURL, httpContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// output the response content to the console.
Console.WriteLine(responseContent);
The client code is running from a Console application. So when I run this, the expectation is to get File received message in the console and I am getting that message.
Following is the screen capture of the model content at the API end while debugging it.
And if I am calling this API from postman, it would look like following.
I hope this will help you solve your issue.

How to convert Object and view it on WebAPI?

Here is my question,
I got a Web API and client(winform), client will send out data with a Serialize Object. My Web API do have received and return a response to client. But I can't view the data on Web API, I do have try using Deserialize Object and convert it into string but not working neither.
Please help me,Thanks!
Here is my code:
Client
private string WebApiPost(string sParam, string sJson)
{
var client = new HttpClient();
var content = new StringContent(sJson, Encoding.UTF8, "application/json");
var response = client.PostAsync(sWebAPI_URL + sParam, content).Result;
var body = response.Content.ReadAsStringAsync().Result;
return body;
}
This is my Web API
public object Post([FromBody]object hL7)
{
//what should I do???
//I've tried set hL7 into string but it wont get any data;
//I've also tried deserialize it but will get 500 internal server error.
return hL7;
}
This is my WebAPI model
public class HL7MID
{
public string LOC { get; set; }
public string COMPANY { get; set; }
}
public class HL7MID_List
{
public string sMSG { get; set; }
public List<HL7MID> data = new List<HL7MID>();
}
Because sJson matches HL7MID, you can use that type in as a paramter of your Post function, and just use that type.
public HL7MID Post([FromBody]HL7MID hL7)
{
//use hL7 here
return hL7;//also since you know the return type, changing that to HL7MID is suggested
}

How to Seriailize an c# object into Json, while using httpClient?

I have a little program which should communicate with "Slack". In an older Version I used "Dictionary<string, string>" and then put them into UrlEncodedContent - which worked fine.
Now I am trying to create a Json-object, using Newtonsoft's Nuget-package and (in my opinion) formatting my object the way they say on their website.
Problem is, when I try to make a simple request, my program just runs to one specific line in the code(var response = await _httpClient.SendAsync(request);) and then it just ends. It doesn't throw an exception or display any kind of message, it simply ends on this line. I went through my code step by step while debugging, that's how I know it ends on exactly this line. And I just don't know why!
Now my code:
First, my object...
namespace BPS.Slack
{
public class JsonObject
{
//generally needed parameters
[JsonProperty("ok")]
public bool ok { get; set; }
[JsonProperty("error")]
public string error { get; set; }
[JsonProperty("channel")]
public string channel { get; set; }
[JsonProperty("token")]
private string token = "xoxp-MyToken";
[JsonProperty("as_user")]
public bool as_user = false;
[JsonProperty("username")]
public string username { get;set; }
//--------------------------------
//only needed for textmessages
[JsonProperty("text")]
public string text { get; set; }
//--------------------------------
//for posting messages with data attached
[JsonProperty("initial_comment")]
public string initial_comment { get; set; }
[JsonProperty("file")]
public string file { get; set; }
[JsonProperty("channels")]
public string channels { get; set; }
//--------------------------------
//for getting the latest message from a channel
[JsonProperty("count")]
public string count = "1";
[JsonProperty("unreads")]
public bool unreads = true;
}
}
now the client:
namespace BPS.Slack
{
public class BpsHttpClient
{
private readonly HttpClient _httpClient = new HttpClient { };
public Uri UriMethod { get; set; }
public BpsHttpClient(string webhookUrl)
{
UriMethod = new Uri(webhookUrl);
}
public async Task<HttpResponseMessage> UploadFileAsync(MultipartFormDataContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
}
}
and the main
namespace TestArea
{
class MainArea
{
public static void Main( string[] args)
{
try
{
Task.WhenAll(SendMessage());
}
catch(Exception ass)
{
Console.WriteLine(ass);
Console.ReadKey();
}
}
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/im.history");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
var Json = JsonConvert.SerializeObject(JO);
var StringJson = new StringContent(Json, Encoding.UTF8);
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(StringJson);
var Response = await client.UploadFileAsync(content);
string AnswerContent = await Response.Content.ReadAsStringAsync();
Console.WriteLine(AnswerContent);
Console.ReadKey();
}
}
}
I had the same problem in my older version, BUT only as I wanted to DEserialize an answer I got from Slack. It had to do with my object I tried do deserialize the answer into. But this time I can not figure out what's wrong. But, as I said, I do not have any experience with using serialized objects as Json-property to send requests... anyone has an idea what is wrong with my code?
EDIT: This problem is kinda solved. But there is a follow up problem.
Okay, I found out that the reason for the abprubt termination was the
Task.WhenAll(SendMessage());
it should be
Task.WaitAll(SendMessage()); Why??? Somebody said I should use WhenAll, but obviously it doesn't work properly in this case...
Now I get a response from Slack, but now a different problem has arisen. When I use this method:
public async Task<HttpResponseMessage> UploadFileAsync(MultipartFormDataContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
I allways get the answer:
{"ok":false,"error":"invalid_form_data"}
so I tried to explicitly tell it the 'mediaType', I tried "application/json" and others, but with all of them I get the same error. Here is the full method that calls the upper mehtod:
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/chat.postMessage");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
JO.text = "This is so much fun :D !";
var Json = JsonConvert.SerializeObject(JO, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var StringJson = new StringContent(Json, Encoding.UTF8, "application/json");
var requestContent = new MultipartFormDataContent();
requestContent.Add(StringJson);
var Response = await client.UploadFileAsync(requestContent);
string AnswerContent = await Response.Content.ReadAsStringAsync();
}
When I use this method:
public async Task<HttpResponseMessage> SendMessageAsync(FormUrlEncodedContent content)
{
var response = await _httpClient.PostAsync(UriMethod, content);
return response;
}
so bascially I am passing "FormUrlEncodedContent" instead of "MultipartFormDataContent" in this, and then I get the response I want and can work wiht it. BUT this i of little use to me since I have to use "MultipartFormDataContent" to be able to send files with my requests.
Anyone have an idea what is failing here? Why does it not like the one content-type but the other one? I'd be gratefull for tipps and ideas!
You are serializing your object to Json and then adding it to a Multipart body, that's quite strange. Unless you're uploading binary data (eg Files), there is no need to use MultipartFormDataContent.
You are can directly post your JsonObject serialized as JSON:
public async Task<HttpResponseMessage> PostJsonAsync(StringContent content)
{
var response = await client.PostAsync(url, content);
return response;
}
var client = new BpsHttpClient("https://slack.com/api/im.history");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
var Json = JsonConvert.SerializeObject(JO);
var StringJson = new StringContent(Json, Encoding.UTF8);
var Response = await client.PostJsonAsync(content);
Also this is should be POST on the UploadFileAsync function.
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, UriMethod);
so I figured out that in the Main() the problem was this:
Task.WhenAll(SendMessage());
I should instead use:
Task.WaitAll(SendMessage());
Anyone who has more knowledge on this, please elaborate why!

What is wrong with this request (Azure management.azure.com API)

I am trying to get this azure API to work:
https://learn.microsoft.com/en-us/rest/api/machinelearning/webservices/createorupdate
The following code runs a GET request which returns successfully (200 OK and return body), but the Put request to the exact same uri at the bottom fails with a 400 "Bad request".
The code inbetween basically just unwraps the json output of the first request and tried to send the same unchanged data in the input format of the PUT on the same uri.
Could anyone help me see why this fails?
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await httpClient.GetAsync($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.MachineLearning/webServices/{serviceName}?api-version=2016-05-01-preview");
var json = await response.Content.ReadAsStringAsync();
var f = JsonConvert.DeserializeObject<WebService>(json);
var requestBody = new RequestBody()
{
location = f.Location,
name = f.Name,
tags = f.Tags,
properties = f.Properties
};
var jsoncontent = JsonConvert.SerializeObject(requestBody, Formatting.None);
var content = new StringContent(jsoncontent, Encoding.UTF8, "application/json"); //tried simpler things like "{\"location\":\"West Europe\"}" with no result
var response2 = await httpClient.PutAsync($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.MachineLearning/webServices/{serviceName}?api-version=2016-05-01-preview", content, new JsonMediaTypeFormatter());
// last line returns a 400 bad request only.
And then I use this custom object;
private class RequestBody
{
public string location { get; set; }
public string name { get; set; }
public WebServiceProperties properties { get; set; }
public IDictionary<string, string> tags { get; set; }
}
It seems it was one of those obvious things.
The ", new JsonMediaTypeFormatter()" snippet at the end ruined it. Removing it fixed the issue and it returns 200 OK without it.

Object null in WebApi method after PostAsJsonAsync

I am posting an object to a WebApi method. I'm using PostAsJsonAsync to do this.
public async Task<HttpResponseMessage> PostAsync(string token, ServiceCall call)
{
var client = new HttpClient();
client.SetBearerToken(token);
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", call);
return response;
}
The object call that I'm passing is not null when I post it.
[HttpPost]
[Route("id/nestedcall")]
public async Task<IHttpActionResult> NestedCall([FromBody]ServiceCall call)
{
// call is null here
}
However it is null in my API method. I can't seem to work out why as all of the examples I've followed use this format.
Why isn't the call object being picked up by the web api?
Edit
Here is the ServiceCall object. It is in a separate class library and a reference is included in both the web application and the API.
public class ServiceCall
{
public ServiceCall(Service service, string grantType)
{
ClientId = service.Id;
ClientSecret = service.Secret;
Uri = service.Uri;
Scope = service.Scope;
GrantType = grantType;
}
public ServiceCall(string clientid, string clientsecret, string uri, string scope, string grantType)
{
ClientId = clientid;
ClientSecret = clientsecret;
Uri = uri;
Scope = scope;
GrantType = grantType;
}
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string Uri { get; set; }
public string Scope { get; set; }
public string GrantType { get; set; }
}
I have seen Object null in WebApi method after PostAsJsonAsync due to serialization.
Better to use PostAsync like below :
var obj = new MyClass()
{
MyProperty = 11
};
using (var client = new HttpClient())
{
string inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
HttpContent inputContent = new StringContent(inputJson, Encoding.UTF8, "application/json");
HttpResponseMessage response1 = client.PostAsync("http://localhost:60909/api/home/Test", inputContent).Result;
if (response1.IsSuccessStatusCode)
{
}
}
Using Prefix Stackify I was able to diagnose that the serialiser was throwing an exception:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Core.Models.ServiceCall. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'ClientId', line 1, position 12.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize
However, very helpfully, rather than tell me that an exception occurred the controller simply gave me a null object.
As hinted by the exception the solution is to add a default constructor (or at least one the serialiser can understand).
public ServiceCall()
{
}
looks like the JSON serialization may be failing. BTW, remove that [FromBody] and try without it like below. PostAsJsonAsync method serializes the ServiceCall object to JSON and then sends the JSON payload in a POST request.
public async Task<IHttpActionResult> NestedCall(ServiceCall call)
{
// your code
}
I run into exactly the same problem and had to do this to solve it:
using (var client = new HttpClient())
{
client.SetBearerToken(token);
var content = new StringContent(JsonConvert.SerializeObject(call), Encoding.UTF8, "application/json");
var response = await client.PostAsJsonAsync(Uri + "id/nestedcall", content);
return response;
}

Categories