Im having a problem with a WebMethod MVC
This is the method in the controller
[HttpPost]
public IHttpActionResult GetData(int Company,
int FromYear,int ToYear, int language ,
int DATA_SERIES_TYPE, bool PERIOD_Q1)
{
var ds = new DataSet();
try
{
ds = Actions.GetValues(Company, FromYear, ToYear,DATA_SERIES_TYPE);
return Ok(ds);
}
catch(Exception ex)
{
return InternalServerError(ex);
}
}
I have this method in an API, when i call the api from another project using webrequest
Dim Request As WebRequest = WebRequest.Create("http://localhost/PBWebApi/api/GetData")
With strJson
.Append("{")
.Append($"Company:{_dynaSolver.CompanyCode.ToString()}" & ",")
.Append($"FromYear:{_dynaSolver.FromYear.ToString}" & ",")
.Append($"ToYear:{_dynaSolver.ToYear.ToString}" & ",")
.Append($"language:{CType(_dynaSolver.DataLanguage, Integer).ToString}" & ",")
.Append($"DATA_SERIES_TYPE:{ CType(_dynaSolver.Fundamentals, Integer).ToString}")
.Append("}")
End With
Dim data = Encoding.UTF8.GetBytes(strJson.ToString)
With Request
.Method = "POST"
.ContentType = "application/json; charset=utf-8"
.ContentLength = data.Length
End With
Dim stream = Request.GetRequestStream()
stream.Write(data, 0, data.Length)
stream.Close()
Dim response = Request.GetResponse().GetResponseStream
Dim reader As StreamReader = New StreamReader(response)
Dim res = reader.ReadToEnd()
dsRes = JsonConvert.DeserializeObject(Of DataSet)(res)
reader.Close()
response.Close()
When i execute this, the response gives this error.
No HTTP resource was found that matches the request URI
I tried using just one parameter in another function but i got the same result.
I tried using fiddler but i got the same error.
I fixed the problem creating and object called id and adding all the properties to that object.
After that i send the object as json in the content and it works.
Regards
Related
Currently i'm using the Microsoft OData connected Service to control a WebAPI with HMAC. So far i managed to get the GET methods working. However when i POST a message i need to hash the raw HTTP body and add it to the header. (due to the HMAC signature)
So far i got:
Private WebCon As New Container(New Uri("http://domain/odata/v1"))
WebCon.Configurations.RequestPipeline.OnMessageCreating = Function(args)
Dim request As New HttpWebRequestMessage(args)
'Todo hash content if there is any
Dim contentMd5Hash As String = ""
If args.Method = "POST" Then
'Todo, retrieve raw (JSON) content from the HttpWebRequestMessage so i can do make a MD5 hash of it.
End If
'rest of code thath creates the headers.
End function
At the moment, I do not know the right way to do this. Unless to somehow receive the body of the request before the call, and put it into container.Configurations.RequestPipeline.OnMessageCreating:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// POST api/values/5
[HttpPost("{id}")]
public async Task PostAction(int id, [FromBody] string value)
{
var container = new DefaultContainer(new Uri("https://services.odata.org/V4/(S(qc322lduoxrqt13nhydbdcvx))/TripPinServiceRW/"));
// You need to get request body for HMAC
var postData = new
{
Id = id,
Value = value
};
byte[] requestBody = await new ObjectContent(typeof(object), postData, new JsonMediaTypeFormatter()).ReadAsByteArrayAsync();
container.Configurations.RequestPipeline.OnMessageCreating = (args) =>
{
var request = new HttpWebRequestMessage(args);
// Get the Request URI
string requestUri = HttpUtility.UrlEncode(request.Url.AbsoluteUri.ToLower());
// Calculate UNIX time
var epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
var timeSpan = DateTime.UtcNow - epochStart;
var requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
// Create the random nonce for each request
var nonce = Guid.NewGuid().ToString("N");
// Get request body for not GET requests (with Microsoft.AspNetCore.Http.HttpRequest Request.Body)
var requestContentBase64String = string.Empty;
if (!request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase) && requestBody != null && requestBody.Length != 0)
{
var md5 = MD5.Create();
var requestContentHash = md5.ComputeHash(requestBody);
requestContentBase64String = Convert.ToBase64String(requestContentHash);
}
// Creating the raw signature string by combinging
// APPId, request Http Method, request Uri, request TimeStamp, nonce
var signatureRawData = string.Format("{0}{1}{2}{3}{4}{5}", APPId, request.Method, requestUri, requestTimeStamp, nonce, requestContentBase64String);
// Converting the APIKey into byte array
var secretKeyByteArray = Convert.FromBase64String(APIKey);
// Converting the signatureRawData into byte array
var signature = Encoding.UTF8.GetBytes(signatureRawData);
// Generate the hmac signature and set it in the Authorization header
using (var hmac = new HMACSHA256(secretKeyByteArray))
{
var signatureBytes = hmac.ComputeHash(signature);
var requestSignatureBase64String = Convert.ToBase64String(signatureBytes);
//Setting the values in the Authorization header using custom scheme (hmacauth)
request.SetHeader("Authorization", string.Format("hmacauth {0}:{1}:{2}:{3}", APPId, requestSignatureBase64String, nonce, requestTimeStamp));
// You can add more haeder you need there
}
return request;
};
// Call some OData method with postData
var result = container.CallSomeMethod(postData);
// Add more business logic there
}
}
Can you change server logic to avoid request body in the HMAC authorization header?
Then you can use the HMAC without the request body in the client side.
I am developing an app for iOS using Xamarin iOS & MonoGame. I want to use Parse's push notifications through their REST API, so first I must create an installation object:
var bundle = new Dictionary<string, object>();
bundle.Add("channels", "");
bundle.Add("deviceType", "ios");
bundle.Add("deviceToken", _deviceToken);
string urlpath = "https://api.parse.com/1/installations";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(urlpath);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("X-Parse-Application-Id", _parseAppID);
httpWebRequest.Headers.Add("X-Parse-REST-API-KEY", _parseRestAPIKey);
httpWebRequest.Method = "POST";
string bundleString = bundle.ToJson();
byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(bundleString);
string result = Convert.ToBase64String(buffer);
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(result, 0, result.Length);
requestWriter.Close();
WebResponse httpResponse = await httpWebRequest.GetResponseAsync();
Stream stream = httpResponse.GetResponseStream();
string json = string.Empty;
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
json += reader.ReadLine();
}
}
JsonObject jsonObject = JsonObject.Parse(json);
_varStorage.Save("ObjectId", jsonObject.Get<string>("objectId"));
The bundleString value is:
"{\"channels\":\"\",\"deviceType\":\"ios\",\"deviceToken\":\"46becd0a165be042eeab5a1ec96b8858065cbea7311479da16c0fd1c9428e2eb\"}"
This code raises a System.Net.WebExceptionStatus.ProtocolError error 400 "Bad Request", and I can't see why.
Channels is supposed to be an array of strings according to the documentation, https://www.parse.com/docs/rest#installations
bundle.Add("channels", new [] { "" });
After more trail and error, I found that replacing this
byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(bundleString);
string result = Convert.ToBase64String(buffer);
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(result, 0, result.Length);
requestWriter.Flush();
requestWriter.Close();
with this
httpWebRequest.ContentLength = bundleString.Length;
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(bundleString);
requestWriter.Flush();
requestWriter.Close();
fixed the problem, I don't know exactly why though.
should you not be calling flush before closing your stream ?
requestWriter.Write(result, 0, result.Length);
requestWriter.Close();
How to ensure UploadStringCompletedEventHandler event has been executed successfully ? in following code you can see i am calling function UploadMyPOST with my lastreads parameter having some data. Now you can see i am saving a variable named response into the MyClassXYZ varialbe. in the extreme last you can see there is a event which invoked by the method UploadMyPost() is filling the server response into the response variable. Now here issue is UploadMyPost(lastreads) executes successfully but its invoked event does not executes. Even cursor do not go on that event by which i am not able to fill server response into the response variable. So Anyone know any approach by which i can wait until that event successfully execute and i could able to save server response ?
private async void MyMethod(MyClassXYZ lastreads)
{
await UploadMyPOST(lastreads);
MyClassXYZ serverResponse = response;
if (serverResponse.Book == null)
{
//Do Something.
}
}
private void UploadMyPOST(MyClassXYZ lastreads)
{
apiData = new MyClassXYZApi()
{
AccessToken = thisApp.currentUser.AccessToken,
Book = lastreads.Book,
Page = lastreads.Page,
Device = lastreads.Device
};
//jsondata is my global variable of MyClassXYZ class.
jsondata = Newtonsoft.Json.JsonConvert.SerializeObject(apiData);
MyClassXYZ responsedData = new MyClassXYZ();
Uri lastread_url = new Uri(string.Format("{0}lastread", url_rootPath));
WebClient wc = new WebClient();
wc.Headers["Content-Type"] = "application/json;charset=utf-8";
wc.UploadStringCompleted += new UploadStringCompletedEventHandler(MyUploadStringCompleted);
wc.UploadStringAsync(lastread_url, "POST", jsondata);
}
private void MyUploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
try
{
if (e.Error == null)
{
string resutls = e.Result;
DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(MyClassXYZ));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(resutls));
response = (MyClassXYZ)json.ReadObject(ms);
}
else
{
string sx = e.Error.ToString();
}
}
catch(Exception exe)
{
}
}
//After Stephen suggession i used the HttpClient so i have written new code with the help of HttpClient. Code is building successfully but at run time cursor goes out from this method to the parent method where from its calling.
private async Task<string> UploadMyPOST(MyClassXYZ lastreads)
{
string value = "";
try
{
apiData = new LastReadAPI()
{
AccessToken = thisApp.currentUser.AccessToken,
Book = lastreads.Book,
Page = lastreads.Page,
Device = lastreads.Device
};
jsondata = Newtonsoft.Json.JsonConvert.SerializeObject(apiData);
LastRead responsedData = new LastRead();
Uri lastread_url = new Uri(string.Format("{0}lastread", url_rootPath));
HttpClient hc = new HttpClient();
//After following line cursor go back to main Method.
var res = await hc.PostAsync(lastread_url, new StringContent(jsondata));
res.EnsureSuccessStatusCode();
Stream content = await res.Content.ReadAsStreamAsync();
return await Task.Run(() => Newtonsoft.Json.JsonConvert.SerializeObject(content));
value = "kd";
}
catch
{ }
return value;
}
I recommend that you use HttpClient or wrap the UploadStringAsync/UploadStringCompleted pair into a Task-based method. Then you can use await like you want to in MyMethod.
Thank you Stephen Clear you leaded me in a right direction and i did POST my request successfully using HttpClient.
HttpClient hc = new HttpClient();
hc.BaseAddress = new Uri(annotation_url.ToString());
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, myUrl);
HttpContent myContent = req.Content = new StringContent(myJsonData, Encoding.UTF8, "application/json");
var response = await hc.PostAsync(myUrl, myContent);
//Following line for pull out the value of content key value which has the actual resposne.
string resutlContetnt = response.Content.ReadAsStringAsync().Result;
DataContractJsonSerializer deserializer_Json = new DataContractJsonSerializer(typeof(MyWrapperClass));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(resutlContetnt.ToString()));
AnnotateResponse = deserializer_Json.ReadObject(ms) as Annotation;
I've successfully received data from my WebAPI project ("GET"), but my attempt to Post is not working. Here is the relevant server/WebAPI code:
public Department Add(Department item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
departments.Add(item);
return item;
}
...which fails on the "departments.Add(item);" line, when this code from the client is invoked:
const string uri = "http://localhost:48614/api/departments";
var dept = new Department();
dept.Id = 8;
dept.AccountId = "99";
dept.DeptName = "Something exceedingly funky";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.Method = "POST";
var deptSerialized = JsonConvert.SerializeObject(dept); // <-- This is JSON.NET; it works (deptSerialized has the JSONized versiono of the Department object created above)
using (StreamWriter sw = new StreamWriter(webRequest.GetRequestStream()))
{
sw.Write(deptSerialized);
}
HttpWebResponse httpWebResponse = webRequest.GetResponse() as HttpWebResponse;
using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream()))
{
if (httpWebResponse.StatusCode != HttpStatusCode.OK)
{
string message = String.Format("POST failed. Received HTTP {0}", httpWebResponse.StatusCode);
throw new ApplicationException(message);
}
MessageBox.Show(sr.ReadToEnd());
}
...which fails on the "HttpWebResponse httpWebResponse = webRequest.GetResponse() as HttpWebResponse;" line.
The err msg on the server is that departments is null; deptSerialized is being populated with the JSON "record" so...what is missing here?
UPDATE
Specifying the ContentType did, indeed, solve the dilemma. Also, the StatusCode is "Created", making the code above throw an exception, so I changed it to:
using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream()))
{
MessageBox.Show(String.Format("StatusCode == {0}", httpWebResponse.StatusCode));
MessageBox.Show(sr.ReadToEnd());
}
...which shows "StatusCode == Created" followed by the JSON "record" (array member? term.?) I created.
You forgot to set the proper Content-Type request header:
webRequest.ContentType = "application/json";
You wrote some JSON payload in the body of your POST request but how do you expect the Web API server to know that you sent JSON payload and not XML or something else? You need to set the proper Content-Type request header for that matter.
I am trying to figure out how to use the AWS .NET SDK to confirm a subscription to a SNS Topic.
The subscription is via HTTP
The endpoint will be in a .net mvc website.
I can't find any .net examples anywhere?
A working example would be fantastic.
I'm trying something like this
Dim snsclient As New Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(ConfigurationSettings.AppSettings("AWSAccessKey"), ConfigurationSettings.AppSettings("AWSSecretKey"))
Dim TopicArn As String = "arn:aws:sns:us-east-1:991924819628:post-delivery"
If Request.Headers("x-amz-sns-message-type") = "SubscriptionConfirmation" Then
Request.InputStream.Seek(0, 0)
Dim reader As New System.IO.StreamReader(Request.InputStream)
Dim inputString As String = reader.ReadToEnd()
Dim jsSerializer As New System.Web.Script.Serialization.JavaScriptSerializer
Dim message As Dictionary(Of String, String) = jsSerializer.Deserialize(Of Dictionary(Of String, String))(inputString)
snsclient.ConfirmSubscription(New Amazon.SimpleNotificationService.Model.ConfirmSubscriptionRequest With {.AuthenticateOnUnsubscribe = False, .Token = message("Token"), .TopicArn = TopicArn})
End If
Here is a working example using MVC WebApi 2 and the latest AWS .NET SDK.
var jsonData = Request.Content.ReadAsStringAsync().Result;
var snsMessage = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
//verify the signaure using AWS method
if(!snsMessage.IsMessageSignatureValid())
throw new Exception("Invalid signature");
if(snsMessage.Type == Amazon.SimpleNotificationService.Util.Message.MESSAGE_TYPE_SUBSCRIPTION_CONFIRMATION)
{
var subscribeUrl = snsMessage.SubscribeURL;
var webClient = new WebClient();
webClient.DownloadString(subscribeUrl);
return "Successfully subscribed to: " + subscribeUrl;
}
Building on #Craig's answer above (which helped me greatly), the below is an ASP.NET MVC WebAPI controller for consuming and auto-subscribing to SNS topics. #WebHooksFTW
using RestSharp;
using System;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Description;
namespace sb.web.Controllers.api {
[System.Web.Mvc.HandleError]
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class SnsController : ApiController {
private static string className = MethodBase.GetCurrentMethod().DeclaringType.Name;
[HttpPost]
public HttpResponseMessage Post(string id = "") {
try {
var jsonData = Request.Content.ReadAsStringAsync().Result;
var sm = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
//LogIt.D(jsonData);
//LogIt.D(sm);
if (!string.IsNullOrEmpty(sm.SubscribeURL)) {
var uri = new Uri(sm.SubscribeURL);
var baseUrl = uri.GetLeftPart(System.UriPartial.Authority);
var resource = sm.SubscribeURL.Replace(baseUrl, "");
var response = new RestClient {
BaseUrl = new Uri(baseUrl),
}.Execute(new RestRequest {
Resource = resource,
Method = Method.GET,
RequestFormat = RestSharp.DataFormat.Xml
});
if (response.StatusCode != System.Net.HttpStatusCode.OK) {
//LogIt.W(response.StatusCode);
} else {
//LogIt.I(response.Content);
}
}
//read for topic: sm.TopicArn
//read for data: dynamic json = JObject.Parse(sm.MessageText);
//extract value: var s3OrigUrlSnippet = json.input.key.Value as string;
//do stuff
return Request.CreateResponse(HttpStatusCode.OK, new { });
} catch (Exception ex) {
//LogIt.E(ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError, new { status = "unexpected error" });
}
}
}
}
I don't know how recently this has changed, but I've found that AWS SNS now provides a very simply method for subscribing that doesn't involve extracting urls or building requests using RESTSharp.....Here's the simplified WebApi POST method:
[HttpPost]
public HttpResponseMessage Post(string id = "")
{
try
{
var jsonData = Request.Content.ReadAsStringAsync().Result;
var sm = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
if (sm.IsSubscriptionType)
{
sm.SubscribeToTopic(); // CONFIRM THE SUBSCRIPTION
}
if (sm.IsNotificationType) // PROCESS NOTIFICATIONS
{
//read for topic: sm.TopicArn
//read for data: dynamic json = JObject.Parse(sm.MessageText);
//extract value: var s3OrigUrlSnippet = json.input.key.Value as string;
}
//do stuff
return Request.CreateResponse(HttpStatusCode.OK, new { });
}
catch (Exception ex)
{
//LogIt.E(ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError, new { status = "unexpected error" });
}
}
The following example helped me work with SNS. It goes through all the steps to work with Topics. The subscribe request in this case is an email address, however that can be changed to HTTP.
Pavel's SNS Example
Documentation
I ended up getting it working using the code shown. I was having trouble capturing the exception on the development server which turned out was telling me the server's time didn't match the timestamp in the SNS message.
Once the server's time was fixed up (an Amazon server BTW), the confirmation worked.