How to call PUT method from Web Api using HttpClient? - c#-4.0

I want to call Api function (1st) . from 2nd Api function using HttpClient. But I always get 404 Error.
1st Api Function (EndPoint : http : // localhost : xxxxx /api/Test/)
public HttpResponseMessage Put(int id, int accountId, byte[] content)
[...]
2nd Api function
public HttpResponseMessage Put(int id, int aid, byte[] filecontent)
{
WebRequestHandler handler = new WebRequestHandler()
{
AllowAutoRedirect = false,
UseProxy = false
};
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri("http://localhost:xxxxx/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var param = new object[6];
param[0] = id;
param[1] = "/";
param[2] = "?aid=";
param[3] = aid;
param[4] = "&content=";
param[5] = filecontent;
using (HttpResponseMessage response = client.PutAsJsonAsync("api/Test/", param).Result)
{
return response.EnsureSuccessStatusCode();
}
}
}
So My question is that. Can I post Method Parameter as an object array from HttpClient as I did ? I don't want to Pass model as method parameter.
What is the wrong in my code ?
Unable to get any response , after change code to
return client.PutAsJsonAsync(uri, filecontent)
.ContinueWith<HttpResponseMessage>
(
task => task.Result.EnsureSuccessStatusCode()
);
OR
return client.PutAsJsonAsync(uri, filecontent)
.ContinueWith
(
task => task.Result.EnsureSuccessStatusCode()
);

As you probably found out, no you can't. When you call PostAsJsonAsync, the code will convert the parameter to JSON and send it in the request body. Your parameter is a JSON array which will look something like the array below:
[1,"/","?aid",345,"&content=","aGVsbG8gd29ybGQ="]
Which isn't what the first function is expecting (at least that's what I imagine, since you haven't showed the route info). There are a couple of problems here:
By default, parameters of type byte[] (reference types) are passed in the body of the request, not in the URI (unless you explicitly tag the parameter with the [FromUri] attribute).
The other parameters (again, based on my guess about your route) need to be part of the URI, not the body.
The code would look something like this:
var uri = "api/Test/" + id + "/?aid=" + aid;
using (HttpResponseMessage response = client.PutAsJsonAsync(uri, filecontent).Result)
{
return response.EnsureSuccessStatusCode();
}
Now, there's another potential issue with the code above. It's waiting on the network response (that's what happens when you access the .Result property in the Task<HttpResponseMessage> returned by PostAsJsonAsync. Depending on the environment, the worse that can happen is that it may deadlock (waiting on a thread in which the network response will arrive). In the best case this thread will be blocked for the duration of the network call, which is also bad. Consider using the asynchronous mode (awaiting the result, returning a Task<T> in your action) instead, like in the example below
public async Task<HttpResponseMessage> Put(int id, int aid, byte[] filecontent)
{
// ...
var uri = "api/Test/" + id + "/?aid=" + aid;
HttpResponseMessage response = await client.PutAsJsonAsync(uri, filecontent);
return response.EnsureSuccessStatusCode();
}
Or without the async / await keywords:
public Task<HttpResponseMessage> Put(int id, int aid, byte[] filecontent)
{
// ...
var uri = "api/Test/" + id + "/?aid=" + aid;
return client.PutAsJsonAsync(uri, filecontent).ContinueWith<HttpResponseMessage>(
task => task.Result.EnsureSuccessStatusCode());
}

Related

how to use basic authentication on azure functions ISOLATED?

how do i get the user and password informed in basic authentication in azure functions ISOLATED?
exemple, using the app SOAPUI to make a call to the function:
[https://i.imgur.com/3u7eymT.png]
how do i get in the function this USER and his password ?
[Function("Function1")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_logger.LogInformation("C# HTTP trigger function processed a request.");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString("Welcome to Azure Functions!");
return response;
}
i also have a middleware how i get this info in him too?
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
i tried to search in the header, or the identities but i can't find this info/login and password
For Basic Authentication we need to change the open API security property values as below
[OpenApiSecurity("basic_auth", SecuritySchemeType.Http, Scheme = OpenApiSecuritySchemeType.Basic)]
Below is the screenshot of SoapUI
Authorization header need to be added in SoapUI as shown below
Code In Function
var headers = req.Headers["Authorization"];
if (ValidateToken(headers))
{
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name) ? "Pass a name in the query string" : $"{name}";
return new OkObjectResult(responseMessage);
}
Validation Code part
string encode_Credentials = header.Substring("Basic ".Length).Trim();
Encoding encode = Encoding.GetEncoding("iso-8859-1");
string credentials = encode.GetString(Convert.FromBase64String(encode_Credentials));
int seperatorIndex = credentials.IndexOf(':');
var username = credentials.Substring(0, seperatorIndex);
var password = credentials.Substring(seperatorIndex + 1);
if (username is "Rajesh" && password is "1142") return true;
else return false;

Trigger notification after Computer Vision OCR extraction is complete

I am exploring Microsoft Computer Vision's Read API (asyncBatchAnalyze) for extracting text from images. I found some sample code on Microsoft site to extract text from images asynchronously.It works in following way:
1) Submit image to asyncBatchAnalyze API.
2) This API accepts the request and returns a URI.
3) We need to poll this URI to get the extracted data.
Is there any way in which we can trigger some notification (like publishing an notification in AWS SQS or similar service) when asyncBatchAnalyze is done with image analysis?
public class MicrosoftOCRAsyncReadText {
private static final String SUBSCRIPTION_KEY = “key”;
private static final String ENDPOINT = "https://computervision.cognitiveservices.azure.com";
private static final String URI_BASE = ENDPOINT + "/vision/v2.1/read/core/asyncBatchAnalyze";
public static void main(String[] args) {
CloseableHttpClient httpTextClient = HttpClientBuilder.create().build();
CloseableHttpClient httpResultClient = HttpClientBuilder.create().build();;
try {
URIBuilder builder = new URIBuilder(URI_BASE);
URI uri = builder.build();
HttpPost request = new HttpPost(uri);
request.setHeader("Content-Type", "application/octet-stream");
request.setHeader("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY);
String image = "/Users/xxxxx/Documents/img1.jpg";
File file = new File(image);
FileEntity reqEntity = new FileEntity(file);
request.setEntity(reqEntity);
HttpResponse response = httpTextClient.execute(request);
if (response.getStatusLine().getStatusCode() != 202) {
HttpEntity entity = response.getEntity();
String jsonString = EntityUtils.toString(entity);
JSONObject json = new JSONObject(jsonString);
System.out.println("Error:\n");
System.out.println(json.toString(2));
return;
}
String operationLocation = null;
Header[] responseHeaders = response.getAllHeaders();
for (Header header : responseHeaders) {
if (header.getName().equals("Operation-Location")) {
operationLocation = header.getValue();
break;
}
}
if (operationLocation == null) {
System.out.println("\nError retrieving Operation-Location.\nExiting.");
System.exit(1);
}
/* Wait for asyncBatchAnalyze to complete. In place of this wait, can we trigger any notification from Computer Vision when the extract text operation is complete?
*/
Thread.sleep(5000);
// Call the second REST API method and get the response.
HttpGet resultRequest = new HttpGet(operationLocation);
resultRequest.setHeader("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY);
HttpResponse resultResponse = httpResultClient.execute(resultRequest);
HttpEntity responseEntity = resultResponse.getEntity();
if (responseEntity != null) {
String jsonString = EntityUtils.toString(responseEntity);
JSONObject json = new JSONObject(jsonString);
System.out.println(json.toString(2));
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
There is no notification / webhook mechanism on those asynchronous operations.
The only thing that I can see right know is to change the implementation you mentioned by using a while condition which is checking regularly if the result is there or not (and a mechanism to cancel waiting - based on maximum waiting time or number of retries).
See sample in Microsoft docs here, especially this part:
// If the first REST API method completes successfully, the second
// REST API method retrieves the text written in the image.
//
// Note: The response may not be immediately available. Text
// recognition is an asynchronous operation that can take a variable
// amount of time depending on the length of the text.
// You may need to wait or retry this operation.
//
// This example checks once per second for ten seconds.
string contentString;
int i = 0;
do
{
System.Threading.Thread.Sleep(1000);
response = await client.GetAsync(operationLocation);
contentString = await response.Content.ReadAsStringAsync();
++i;
}
while (i < 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1);
if (i == 10 && contentString.IndexOf("\"status\":\"Succeeded\"") == -1)
{
Console.WriteLine("\nTimeout error.\n");
return;
}
// Display the JSON response.
Console.WriteLine("\nResponse:\n\n{0}\n",
JToken.Parse(contentString).ToString());

SHA256 webhook signature from WooCommerce never verifies

I am receiving webhooks from a woocommerce site into a nodejs/express application. I am trying to verify the webhook's signature to prove authenticity, yet the hash I compute never corresponds to the signature that woocommerce reports in the hook's signature header.
Here is the code I am using to verify the authenticity:
function verifySignature(signature, payload, key){
var computedSignature = crypto.createHmac("sha256", key).update(payload).digest('base64');
debug('computed signature: %s', computedSignature);
return computedSignature === signature;
}
This function is being called with the following parameters:
var signature = req.headers['x-wc-webhook-signature'];
verifySignature(signature, JSON.stringify(req.body), config.wooCommence.accounts.api[config.env].webhookSecret)
The webhook's signature headers reports the signature as BewIV/zZMbmuJkHaUwaQxjX8yR6jRktPZQN9j2+67Oo=. The result of the above operation, however, is S34YqftH1R8F4uH4Ya2BSM1rn0H9NiqEA2Nr7W1CWZs=
I have manually configured the secret on the webhook, and as you see in the code above, this same secret is also hardcoded in the express application. So either I am taking the wrong payload to compute the signature, or there is something else fishy that prevents me from verifying these signature.
Would appreciate any pointers to help me solve this issue.
For people using node, this should do the trick.
var processWebHookSignature = function (secret, body, signature) {
signatureComputed = crypto.createHmac('SHA256', secret).update(
new Buffer(JSON.stringify(body), 'utf8')).digest('base64');
return ( signatureComputed === signature ) ? true : false;
}
Since this is the top Google result for this question and there isn't a complete answer out there, here's a Python version using Flask that validates the WooCommerce webhook signature. It took a bit of trial and error, hope it helps someone out there:
import json
import base64
import hmac
import hashlib
from flask import Flask, request, Response
app = Flask(__name__)
# The WooCommerce webhook secret
WEBHOOK_SECRET = 'abc123456'
# Function that compares the computed signature to the one in the request
def verify_woocommerce_signature(body, signature, secret):
digest = hmac.new(bytes(secret, 'utf-8'), body, hashlib.sha256).digest()
encoded = base64.b64encode(digest).decode()
return encoded == signature
# WooCommerce Order Creation Event
#app.route('/webhooks/woocommerce/order_created', methods=['POST'])
def webhooks_woocommerce_order_created():
# Get raw request body
body = request.get_data()
# Get request signature
signature = request.headers['X-WC-WEBHOOK-SIGNATURE']
# Verify webhook signature and handle mismatch
if verify_woocommerce_signature(body, signature, WEBHOOK_SECRET) is False:
msg = {"success": False}
return Response(json.dumps(msg), status=400, mimetype='application/json')
# Signatures match, process the payload
Old question but maybe it helps some poor soul out there.
The signature needs to be checked against the body and not the JSON it contains. i.e. the raw bytes of the body.
pseudo:
byte[] body = request.Body;
string signature = request.Header["X-WC-Webhook-Signature"];
byte[] secretUtf8 = GetUtf8Bytes("yoursecrethere");
byte[] hash = HMAC_SHA256.ComputeHash(body, secretUtf8);
string hashBase64 = ToBase64String(hash);
bool isValid = hashBase64 == signature;
I stumbled upon this while searching for a solution to have an Asp.NET application check signature of the Woocommerce web hook. My answer is based on the pseudo code Johannes provided which worked great. I implemented a custom controller attribute to intercept the request and check the signature before it hits the API controller method:
public class HmacSignatureFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var requestContent = actionContext.Request.Content;
var jsonContent = requestContent.ReadAsStringAsync().Result;
var byteContent = requestContent.ReadAsByteArrayAsync().Result;
//if the request contains this, it's the verification request from Woocommerce
//when the webhook is created so let it pass through so it can be verified
if (!jsonContent.Contains("webhook_id"))
{
var requestSignature = actionContext.Request.Headers;
var bodyHash = HashHMAC("test", byteContent); //this is the shared key between Woo and custom API. should be from config or database table.
var signature = actionContext.Request.Headers.GetValues("x-wc-webhook-signature");
if (bodyHash != signature.FirstOrDefault())
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
}
base.OnActionExecuting(actionContext);
}
private static string HashHMAC(string key, byte[] message)
{
var keyBytes = Encoding.UTF8.GetBytes(key);
var hash = new HMACSHA256(keyBytes);
var computedHash = hash.ComputeHash(message);
return Convert.ToBase64String(computedHash);
}
}
Then to use the filter in your Api controller:
[RoutePrefix("api/woo")]
public class WooController : ApiController
{
public SomeService _service;
public WooController()
{
this._service = new SomeService();
}
// POST api/values
[Route("orderCreated")]
[HttpPost]
[HmacSignatureFilter]
public string Post()
{
var requestContent = Request.Content;
var jsonContent = requestContent.ReadAsStringAsync().Result;
//this is the test request from Woocommerce. Don't do anything but
//respond so it can verify the endpoint
if (jsonContent.Contains("webhook_id"))
{
return "Webhook Test Success";
}
var wooOrder = JsonConvert.DeserializeObject<WooOrderModel>(jsonContent);
//call a service to use the order data provided by WooCommerce
_service.AddOrder(wooOrder);
return "Success";
}
}
Note: Hashing code was referenced from this SO post.
SOLVED in TypeScript. I added this in the server.ts:
this.app.use(bodyParser.json({
verify: function(req, res, buf) {
(req as any).rawBody = buf;
}
}));
and than:
const computedSignature = crypto.createHmac("sha256", process.env.WOOCOMMERCE_SECRET).update((req as any).rawBody).digest("base64");
Hash must be calculated over the 'raw body'. When used in an 'express application' and using JSON bodyParser middleware 'raw body' is lost, see How to access the raw body of the request before bodyparser? to hold-on to the 'raw body'.
For example:
// 'misuse' verify option
app.use(bodyParser.json({
verify: function(req,res,buf) {
req.rawBody=buf;
}
}));
var wcSignature = req.get('X-Wc-Webhook-Signature');
debug('wc signature: %s', wcSignature);
var calculatedSignature = crypto.createHmac('SHA256', secret)
.update(req.rawBody, 'utf8')
.digest('base64');
debug('calculated signature: %s', calculatedSignature);
Hope to save someone time, below works for me.
// Make sure to add a WISTIA_SECRET_KEY in your Environment Variables
// See https://docs.pipedream.com/environment-variables/
const secret = process.env.SELF_AUTOMATE_KEY;
const signature = event.headers["x-wc-webhook-signature"];
const body = steps.trigger.raw_event["body_b64"];
const clean_Body = body.replace("body_b64: ", "");
//const body = steps.trigger.raw_event;
console.log(event.headers["x-wc-webhook-signature"]);
console.log("Print Body", clean_Body);
if (process.env.SELF_AUTOMATE_KEY === undefined) {
$end("No WISTIA_SECRET_KEY environment variable defined. Exiting.")
}
if (!("x-wc-webhook-signature" in event.headers)) {
$end("No x-wc-webhook-signature header present in the request. Exiting.")
}
// Once we've confirmed we have a signature, we want to
// validate it by generating an HMAC SHA-256 hexdigest
const crypto = require('crypto');
const hash = crypto.createHmac('sha256',
secret).update(JSON.stringify(clean_Body), 'base64').digest('base64');
console.log(hash);
// $end() ends the execution of a pipeline, presenting a nice message in the "Messages"
// column in the inspector above. See https://docs.pipedream.com/notebook/code/#end
if (hash !== signature) {
$end("The correct secret key was not passed in the event. Exiting!")
}
SOLVED IN C# API. For people using it as an Attribute in a C# API for a controller, I made a good solution with multiple threads on StackOverflow:
[AttributeUsage(AttributeTargets.Method)]
public class ShaKeyAttribute : Attribute, IAuthorizationFilter
{
private readonly ShaAuthOptions _options;
private readonly ILogger<ShaKeyAttribute> _log;
public ShaKeyAttribute(IOptions<ShaAuthOptions> options, ILogger<ShaKeyAttribute> log)
{
this._options = options.Value;
this._log = log;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
this._log.LogInformation("Entering in Sha Auth...");
if (!context.HttpContext.Request.Headers.TryGetValue("x-wc-webhook-signature", out var extractedSignature))
{
this._log.LogError("Sha Auth failed. Signature was not provided");
context.Result = new ContentResult()
{
StatusCode = 401,
Content = "Signature was not provided",
};
return;
}
// This logic for body rewind comes from: https://stackoverflow.com/a/40994711/14010438
string bodyString;
var req = context.HttpContext.Request;
// Allows using several time the stream in ASP.Net Core
req.EnableBuffering();
// Arguments: Stream, Encoding, detect encoding, buffer size
// AND, the most important: keep stream opened
using (StreamReader reader
= new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
{
bodyString = reader.ReadToEnd();
}
// Rewind, so the core is not lost when it looks the body for the request
req.Body.Position = 0;
// From this point, DON'T TOUCH THE REQUEST BODY. Instead, use bodyString
// https://stackoverflow.com/a/62032738/14010438
byte[] requestData = Encoding.UTF8.GetBytes(bodyString);
var encoding = new UTF8Encoding();
var key = this._options.Secret;
var keyBytes = encoding.GetBytes(key);
var hash = new HMACSHA256(keyBytes);
var computedHash = hash.ComputeHash(requestData);
var computedHashString = Convert.ToBase64String(computedHash);
if (extractedSignature != computedHashString)
{
this._log.LogError("Sha Auth failed. Signature is not valid");
context.Result = new ContentResult()
{
StatusCode = 401,
Content = "Signature is not valid",
};
}
this._log.LogInformation("Successfully passed Sha Auth.");
}
}
You also need to add this in your startup.cs:
// https://stackoverflow.com/questions/47735133/asp-net-core-synchronous-operations-are-disallowed-call-writeasync-or-set-all
// Needed for ShaAuth
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
Here is my controller:
[AllowAnonymous]
[HttpPost]
[ServiceFilter(typeof(ShaKeyAttribute))]
public async Task<IActionResult> Create([FromBody] WooCommerceRequest request)
{
this._log.LogInformation("Executing Create of Subscription controller...");
var response = await this._mediator.Send(new AddSubscriptionCommand() { OrderId = request.OrderId });
this._log.LogInformation("Finished executing Create of Subscription controller.");
return this.Ok();
}
Hope this help somebody.

ServiceStack: httpReq.GetRawBody() is empty

I have a global requestfilter from where i want to log all http traffic using log4net - company restriction. Problem is that the InputStream is always lenght = 0. The soap envelope is desezerialized correctly and execution of service is succesfull, but inputstream is unavailable after first serialization. Is this wrong approach, if i want to log all ingoing and outgoing http traffic? What should i do to accomplish this? I do not want to log the deserialized requestDto.
this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
LogManager.LogFactory.GetLogger(this.GetType()).Info(httpReq.GetRawBody());
});
Error seems to occur in type ServiceStack.WebHost.Endpoints.Support.SoapHandler in method, where using statement closes stream without buffering it:
protected static Message GetRequestMessage(Stream inputStream, MessageVersion msgVersion)
{
using (var sr = new StreamReader(inputStream))
{
var requestXml = sr.ReadToEnd();
var doc = new XmlDocument();
doc.LoadXml(requestXml);
var msg = Message.CreateMessage(new XmlNodeReader(doc), int.MaxValue,
msgVersion);
return msg;
}
}
When i try to access GetRawBody on type ServiceStack.WebHost.Endpoints.Extensions the following logic is executed:
public string GetRawBody()
{
if (bufferedStream != null)
{
return bufferedStream.ToArray().FromUtf8Bytes();
}
using (var reader = new StreamReader(InputStream))
{
return reader.ReadToEnd();
}
}
Here I would expect the inpustream to be buffered, since it Inputstreaem is no longer available (lenght = 0).
Isn't this a bug?
Have you seen Request Logger or IRequiresRequestStream. These might help provide some insight on logging requests. Also, I don't believe InputStream would have a length on GET requests.

How can I use WebHttpRelayBinding with application/json requests?

When I try to use a generic message handler, I run into errors when the accept, or content-type is html/xml/json if I use my own type such as text/x-json everything works as expected the message is dispatched to my handlers and the stream returns the data to the webclient. I have stepped through this with a debugger and my code successfully creates the message but something in the servicebus binding chokes and causes the server not to respond. Is there a setting I need to change to allow application/json and make the service bus send raw data rather then trying to reserialize it?
[WebGet( UriTemplate = "*" )]
[OperationContract( AsyncPattern = true )]
public IAsyncResult BeginGet( AsyncCallback callback, object state )
{
var context = WebOperationContext.Current;
return DispatchToHttpServer( context.IncomingRequest, null, context.OutgoingResponse, _config.BufferRequestContent, callback, state );
}
public Message EndGet( IAsyncResult ar )
{
var t = ar as Task<Stream>;
var stream = t.Result;
return StreamMessageHelper.CreateMessage( MessageVersion.None, "GETRESPONSE", stream ?? new MemoryStream() );
}
Instead of using: StreamMessageHelper.CreateMessage, you can use the following one after you change :
WebOperationContext.Current.OutgoingResponse.ContentTYpe = "application/json"
public Message CreateJsonMessage(MessageVersion version, string action, Stream jsonStream)
{
var bodyWriter = new JsonStreamBodyWriter(jsonStream);
var message = Message.CreateMessage(version, action, bodyWriter);
message.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json));
return message;
}
class JsonStreamBodyWriter : BodyWriter
{
Stream jsonStream;
public JsonStreamBodyWriter(Stream jsonStream)
: base(false)
{
this.jsonStream = jsonStream;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteNode(JsonReaderWriterFactory.CreateJsonReader(this.jsonStream, XmlDictionaryReaderQuotas.Max), false);
writer.Flush();
}
}

Resources