I created a Rest Webservice with ASP.NET MVC 4 Web Application. If I want to consume the service and send a request including a requestbody the input parameter CoreMessage item is always null. I watched some sample tutorials and some sample code but I couldn't fix the bug.
Some code of my Rest Client
CoreMessage message = new CoreMessage() { Source = "Dummy", Date = DateTime.Now, Data = "abcd" };
var url = "http://localhost:12294/api/message";
var method = "POST";
string reponseAsString = "";
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method;
using (Stream requestStream = request.GetRequestStream())
using (StreamWriter writer = new StreamWriter(requestStream))
{
writer.Write(message);
}
}
catch (Exception ex)
{
reponseAsString += "ERROR: " + ex.Message;
}
My Controller with the Post function look like this
public MessageRepository repository = new MessageRepository();
public HttpResponseMessage PostMessage(CoreMessage item)
{
bool status = repository.TransmitMessage(item);
var response = Request.CreateResponse<bool>(HttpStatusCode.Created, status);
return response;
}
My Model
public class CoreMessage
{
public string Source { get; set; }
public DateTime Date { get; set; }
public string Data { get; set; }
}
There are few things to try
First, put [FromBody] attribute:
public HttpResponseMessage PostMessage([FromBody] CoreMessage item)
Then try to add Content-Type header to your request
request.ContentType = "application/json; chatset=utf-8"
And you have to make it JSON before writing to stream, you cannot write the object out like that. The method depends on what framework you use for JSON serialisation. One of the most popular one is JSON.NET, which is dead easy to install through NuGet package manager. Once you got it installed you can serialise your CoreMessage to string like:
string messageString = JSONConvert.SerializeObject(message);
Then writing it in UTF-8 encoding:
using (StreamWriter writer = new StreamWriter(requestStream, Encoding.UTF8))
{
writer.Write(messageString);
}
After all, if it's still not working, you can try Dev HTTP Client in Chrome. The tool will allow you to craft raw HTTP request. Your request should have Content-Type as application/json; charset=utf-8 and POST data as JSON string.
Related
I am trying to upload a file in to an Azure Storage Container using REST API, without using any SDKs.
Reason for not using the Azure.Storage.Blobs SDK is, it has a dependency on .netstandard2.0 and some of the DLLs referred by the SDK is not compatible when the project runs .NET Framework 4.6.1. I tried assembly binding redirect in the config and didn't workout well (System.ValueTuple.dll reference issue).
Hence i am trying to re-write my Upload code using C# and REST API without the SDK.
Here is code that i am trying
public class AzureRestApi
{
private string _x_ms_client_request_id = string.Empty;
private string _x_ms_date = string.Empty;
public string AzureStorageAccountName { get; set; }
public string AzureStorageAccessKey { get; set; }
public string BlobSaveFolder
{
get; set;
}
public string X_MS_DATE
{
get
{
return _x_ms_date;
}
}
public string X_MS_VERSION
{
get
{
return "2019-12-12";
}
}
public string BaseURI
{
get
{
return string.Format("https://{0}.blob.core.windows.net/", AzureStorageAccountName);
}
}
public AzureRestApi()
{
//Date and GUID should be same that is used to create the key signing string and the one that is being sent in the
//header of REST api requests.
_x_ms_date = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
_x_ms_client_request_id = System.Guid.NewGuid().ToString();
}
public bool UploadBlobToContainer(string filePath, string containerName)
{
bool isUploaded = false;
try
{
FileInfo fileInfo = new FileInfo(filePath);
long contentLength = fileInfo.Length;
long range = contentLength - 1;
string method = "PUT";
string contentType = "application/pdf";
string blobName = fileInfo.Name;
string blobURI = BaseURI + containerName + "/" + blobName;
string xmsHeader = $"x-ms-blob-type:BlockBlob\nx-ms-date:{X_MS_DATE};\nx-ms-version:{X_MS_VERSION}";
string resHeader = $"/{AzureStorageAccountName}/{containerName}/{blobName}";
if (WebRequest.Create(blobURI) is HttpWebRequest request)
{
request.Method = method;
request.ContentType = contentType;
request.ContentLength = contentLength;
request.Headers.Add("x-ms-blob-type", "BlockBlob");
request.Headers.Add("x-ms-date", X_MS_DATE);
request.Headers.Add("x-ms-version", X_MS_VERSION);
request.Headers.Add("Authorization", GetAuthorizationHeader(method, xmsHeader, resHeader, request));
using (Stream requestStream = request.GetRequestStream())
{
byte[] fileContents = null;
using (FileStream fs = fileInfo.OpenRead())
{
fileContents = new byte[fs.Length];
fs.Read(fileContents, 0, fileContents.Length);
fs.Close();
}
requestStream.Write(fileContents, 0, fileContents.Length);
}
if (request.GetResponse() is HttpWebResponse response)
{
if (response.StatusCode == HttpStatusCode.OK)
isUploaded = true;
else
isUploaded = false;
}
}
}
catch (Exception ex)
{
Log.LogException(ex);
}
return isUploaded;
}
private string GetAuthorizationHeader(string method, string xmsHeader, string resHeader, HttpWebRequest request)
{
string strToSign = $"{method}\n\n\n{request.ContentLength}\n\n{request.ContentType}\n\n\n\n\n\n\n{xmsHeader}\n{resHeader}";
string signatureString = GetHashedString(strToSign, AzureStorageAccessKey);
string authorizationHeader = string.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
AzureStorageAccountName,
signatureString);
return authorizationHeader;
}
}
Code to invoke the Upload function
string storageName = "MyDefaultStorage";
string containerName = "MyDefaultContainer";
AzureRestApi restApi = new AzureRestApi()
{
AzureStorageAccountName = storageName,
AzureStorageAccessKey = "MY_STORAGE_ACCOUNT_ACCESS_KEY",
};
restApi.UploadBlobToContainer("E:\\UploadFiles\\UploadDocument1.pdf", containerName);
Exception is thrown while executing this line
if (request.GetResponse() is HttpWebResponse response)
Exception Message: The remote server returned an error: (403) Forbidden.
I tried to simulate this request using PostMan and got this error message
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
The MAC signature found in the HTTP request 'RW1ACnL1aYqtSnJeHhV2694Ek95Olnf+V3Eup4Jt5ng=' is not the same as any computed signature. Server used following string to sign: 'PUT
461804
application/pdf
x-ms-blob-type:BlockBlob
x-ms-date:Wed, 12 Aug 2020 19:30:20 GMT
x-ms-version:2019-12-12
/MyDefaultStorage/MyDefaultContainer/UploadDocument1.pdf'
I tried the answers given in these links and it works if the file is a text file, but fails when tried with a pdf
Azure rest api put blob
REST Api to Azure blob storage using Access key
Any help would be greatly appreciated.
Thanks
Raghunathan S
you added an extra semicolon in this code:
string xmsHeader = $"x-ms-blob-type:BlockBlob\nx-ms-date:{X_MS_DATE};\nx-ms-version:{X_MS_VERSION}";
please change to this:
string xmsHeader = $"x-ms-blob-type:BlockBlob\nx-ms-date:{X_MS_DATE}\nx-ms-version:{X_MS_VERSION}";
Hope this can help you.
I'm trying to create a version in JIRA for a specific project.
I'm able to do the process via Postman by building my requests manually, but it fails with a 404 when creating the version record via .NET.
I'm assuming .NET adds pesky parameters to the request that Postman doesn't do.
The weird thing is that the authentication call works, but the the version creation fails.
Here's the helper I wrote:
public class JIRA
{
private string AuthToken { get; set; }
private const string c_JIRAUrl = "https://org.atlassian.net";
private const string c_LoginUrl = c_JIRAUrl + "/rest/auth/1/session";
private const string c_CreateVersionUrl = c_JIRAUrl + "/rest/api/2/version";
public JIRA()
{
//this works...
var authResponse = ExecuteRequest(c_LoginUrl, "POST", new
{
username = "login",
password = "password"
});
AuthToken = authResponse["session"]["value"].ToString();
}
public void CreateVersion(string name, string projectKey, ProjectEnvironment environment)
{
//lets hardcode the same data I use in Postman for testing purposes...
var createVersionResponse = ExecuteRequest(c_CreateVersionUrl, "POST", new
{
description = "An excellent version",
name = "1.1.2",
archived = false,
released = false,
project = "TEST"
});
}
private JObject ExecuteRequest(string url, string method, object data)
{
HttpWebResponse response;
var jsonDataString = JsonConvert.SerializeObject(data);
byte[] dataBytes = Encoding.Default.GetBytes(jsonDataString);
var responseText = string.Empty;
var wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "application/json";
if (!string.IsNullOrEmpty(AuthToken))
wr.Headers.Add(HttpRequestHeader.Authorization, $"Bearer {AuthToken}");
wr.Method = method;
wr.ContentLength = dataBytes.Length;
wr.Accept = "application/json";
using (var webStream = wr.GetRequestStream())
{
webStream.Write(dataBytes, 0, dataBytes.Length);
response = (HttpWebResponse)wr.GetResponse();
}
using (var sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return JObject.Parse(responseText);
}
}
The CreateVersion method always fails with a 404.
As I've said, doing the same (retrieving the token, creating the version) all works in Postman.
Any ideas what's going on ?
Thanks.
Apparently, when retrieving the token (/rest/auth/1/session) the response contains cookies that POSTMAN was sending back in the 2nd request (creating the version). I had to fire up Fiddler to find out it was doing so because its UI was not saying so.
My .NET client was not doing so. When making it do so, it works.
I'm a little miffed that a REST service expects cookies...
i'm new on creating Rest API using PHP. I have create one, my codes works but i'm not sure about it, here is the code :
// connect to the mysql database
$link = mysql_connect('localhost', 'root', 'pass');
mysql_select_db('database',$link);
// retrieve the table and key from the path
$nama_tujuan = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
$nama_tabel = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
//Menu Login
if($nama_tujuan=="login")
{
$username = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
$password = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
if($method=='GET')
{
$sql = "select * from $nama_tabel where username_reg='$username' and pass_reg='$password'";
$result = mysql_query($sql,$link);
if (!$result) {
http_response_code(404);
die(mysql_error());
}
$cek_user=mysql_num_rows($result);
//cek user
if($cek_user==1)
{
//Kirim data yang dperlukan
for ($i=0;$i<mysql_num_rows($result);$i++)
{
echo ($i>0?',':'').json_encode(mysql_fetch_object($result));
}
}
}
}
for example i try it with:
http://localhost/api.php/login/member/neo/qw
and it show all of the fields like this :
{"nik":"46464646457349187","username_reg":"neo","nm_dpn_reg":"neo","nm_blg_reg":"alit","email_reg":"neo#neo.com","pass_reg":"qw","jns_kel":"P","pin_reg":"111111","tmp_lahir_reg":"lumajang","tgl_lahir_reg":"1-1-1945","alamat_reg":"jolotundo","kota_reg":"surabaya","kd_pos":"60131","propinsi":"Aceh","alamat_kirim":"","kota_kirim":"","kdpos_kirim":"","propinsi_kirim":"","wilayah":"jawa","tlp_hp_reg":"08964507555","tgl_daf":"2016-04-27 16:32:00","ikut_resel":"","bonus":"0"}
Is this called Rest API? And my second question, i want to get JSON data on my Android app, i'm using Visual Studio 2015 and Xamarin. I already create some code, but i don't know the rest, because i'm new on developing android app. Here is my activity code :
button.Click += async (sender, e) => {
string url = "http://localhost/api.php/login/login_mbr/" +
txtUsername.Text +
"/" +
txtPassword.Text;
string m = JsonConvert.DeserializeObject<string>(await FetchWeatherAsync(url));
// ParseAndDisplay (json);
};
}
private async Task<string> FetchWeatherAsync(string url)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "GET";
}
Thanks in advance :)
You can get the json string by this:
private async Task<string> FetchWeatherAsync (string url)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create (new Uri (url));
request.ContentType = "application/json";
request.Method = "GET";
// Send the request to the server and wait for the response:
using (WebResponse response = await request.GetResponseAsync ())
{
// Get a stream representation of the HTTP web response:
using (Stream stream = response.GetResponseStream ())
{
string strContent = sr.ReadToEnd();
return strContent;
}
}
}
now this already returns you the JSON.
If you want to deserialize the JSON into the object you should create the object with the specified properties like:
public class WeatherResult ()
{
public string nik {get; set;}
public string username_reg {get; set;}
...
...
...
}
and then you can get it by
WeatherObject result = JsonConvert.DeserializeObject<WeatherResult>(await FetchWeatherAsync(url));
If you don't want to create new object you can use dynamic object
dynamic result = JObject.Parse(await FetchWeatherAsync(url));
Note: you need Newtonsoft library for JSON.
Having a simple C# unit test:
[TestMethod]
public void JsonPostTest()
{
string testUri1 = "http://localhost:1293/Test/StreamDebug";
string testUri2 = "http://localhost:1293/Test/StreamDebug2?someParameter=abc";
string sampleJson = #"
{
""ID"": 663941764,
""MessageID"": ""067eb623-7580-4d82-bb5c-f5d7dfa69b1e""
}";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(testUri1);
EmailConfig config = GetTestConfigLive();
// Add postmark headers
request.Accept = "application/json";
request.ContentType = "application/json";
request.Method = "POST";
using (var outStream = new StreamWriter(request.GetRequestStream()))
{
outStream.Write(sampleJson);
}
// Get response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string resultText = "";
using (var reader = new StreamReader(response.GetResponseStream()))
{
resultText = reader.ReadToEnd();
}
Assert.Inconclusive();
}
And a simple set of MVC actions to consume and echo posted data back to the unit test (Notice that the code in both actions is identical):
[HttpPost]
[ValidateInput(false)]
public ActionResult StreamDebug()
{
string postbody = "";
using (StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8))
{
postbody = reader.ReadToEnd();
}
return this.Content(postbody);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult StreamDebug2(string someParameter)
{
string postbody = "";
using (StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8))
{
postbody = reader.ReadToEnd();
}
return this.Content(postbody);
}
If I post to the first action I get a string containing the posted json, if I post to the second action I get an empty string.
To make matters more interesting, if I change the content type in the unit test to "text/plain", both actions return the expected values.
Can anyone shed any light on why this might be happening?
Its also worth noting that the request length on the both actions under both sets of circumstances seems to be of the right length.
Further environmental information:
Unit test is in a separate MS test project.
Actions are in a empty MVC 4.0 project (Net 4.0).
It is possible that somewhere in the request pipeline Request.InputStream was already read. In this case its position is already at the end, and of course ReadToEnd reads nothing and returns empty string. This is the root of the problem in our case. Resetting the position fixes the problem:
[HttpPost]
[ValidateInput(false)]
public ActionResult StreamDebug2(string someParameter)
{
string postbody = "";
Request.InputStream.Position = 0;
using (StreamReader reader = new StreamReader(Request.InputStream, Encoding.UTF8))
{
postbody = reader.ReadToEnd();
}
return this.Content(postbody);
}
Update. After a little bit of digging into sources I also found why the position was shifted. It turns out that Request.InputStream is used in JsonValueProviderFactory in the following manner:
// System.Web.Mvc.JsonValueProviderFactory
private static object GetDeserializedObject(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
return null;
}
StreamReader streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string text = streamReader.ReadToEnd();
if (string.IsNullOrEmpty(text))
{
return null;
}
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
return javaScriptSerializer.DeserializeObject(text);
}
This method is called by ControllerActionInvoker to retrieve values from request and bind them to action parameters. Note that this is the only place where Request.InputStream is used through all the MVC.
Therefore if content type of the request is json, the method above is called, input stream gets shifted and attempt to read it yet again without resetting the position fails. However when content type is a plain text, MVC does not try to read the request using json deserialization, input stream is not read before the call in controller and everything works as expected.
This question is already asked elsewhere but those things are not the solutions for my issue.
This is my service
[WebInvoke(UriTemplate = "", Method = "POST")]
public SampleItem Create(SampleItem instance)
{
// TODO: Add the new instance of SampleItem to the collection
// throw new NotImplementedException();
return new SampleItem();
}
I have this code to call the above service
XElement data = new XElement("SampleItem",
new XElement("Id", "2"),
new XElement("StringValue", "sdddsdssd")
);
System.IO.MemoryStream dataSream1 = new MemoryStream();
data.Save(dataSream1);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:2517/Service1/Create");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// You need to know length and it has to be set before you access request stream
request.ContentLength = dataSream1.Length;
using (Stream requestStream = request.GetRequestStream())
{
dataSream1.CopyTo(requestStream);
byte[] bytes = dataSream1.ToArray();
requestStream.Write(bytes, 0, Convert.ToInt16(dataSream1.Length));
requestStream.Close();
}
WebResponse response = request.GetResponse();
I get an exception at the last line:
The remote server returned an error: (405) Method Not Allowed. Not sure why this is happening i tried changing the host from VS Server to IIS also but no change in result. Let me know if u need more information
First thing is to know the exact URL for your REST Service. Since you have specified http://localhost:2517/Service1/Create now just try to open the same URL from IE and you should get method not allowed as your Create Method is defined for WebInvoke and IE does a WebGet.
Now make sure that you have the SampleItem in your client app defined in the same namespace on your server or make sure that the xml string you are building has the appropriate namespace for the service to identify that the xml string of sample object can be deserialized back to the object on server.
I have the SampleItem defined on my server as shown below:
namespace SampleApp
{
public class SampleItem
{
public int Id { get; set; }
public string StringValue { get; set; }
}
}
The xml string corresponding to my SampleItem is as below:
<SampleItem xmlns="http://schemas.datacontract.org/2004/07/SampleApp" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Id>6</Id><StringValue>from client testing</StringValue></SampleItem>
Now i use the below method to perform a POST to the REST service :
private string UseHttpWebApproach<T>(string serviceUrl, string resourceUrl, string method, T requestBody)
{
string responseMessage = null;
var request = WebRequest.Create(string.Concat(serviceUrl, resourceUrl)) as HttpWebRequest;
if (request != null)
{
request.ContentType = "application/xml";
request.Method = method;
}
//var objContent = HttpContentExtensions.CreateDataContract(requestBody);
if(method == "POST" && requestBody != null)
{
byte[] requestBodyBytes = ToByteArrayUsingDataContractSer(requestBody);
request.ContentLength = requestBodyBytes.Length;
using (Stream postStream = request.GetRequestStream())
postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);
}
if (request != null)
{
var response = request.GetResponse() as HttpWebResponse;
if(response.StatusCode == HttpStatusCode.OK)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
var reader = new StreamReader(responseStream);
responseMessage = reader.ReadToEnd();
}
}
else
{
responseMessage = response.StatusDescription;
}
}
return responseMessage;
}
private static byte[] ToByteArrayUsingDataContractSer<T>(T requestBody)
{
byte[] bytes = null;
var serializer1 = new DataContractSerializer(typeof(T));
var ms1 = new MemoryStream();
serializer1.WriteObject(ms1, requestBody);
ms1.Position = 0;
var reader = new StreamReader(ms1);
bytes = ms1.ToArray();
return bytes;
}
Now i call the above method as shown:
SampleItem objSample = new SampleItem();
objSample.Id = 7;
objSample.StringValue = "from client testing";
string serviceBaseUrl = "http://localhost:2517/Service1";
string resourceUrl = "/Create";
string method="POST";
UseHttpWebApproach<SampleItem>(serviceBaseUrl, resourceUrl, method, objSample);
I have the SampleItem object defined in the client side as well. If you want to build the xml string on the client and pass then you can use the below method:
private string UseHttpWebApproach(string serviceUrl, string resourceUrl, string method, string xmlRequestBody)
{
string responseMessage = null;
var request = WebRequest.Create(string.Concat(serviceUrl, resourceUrl)) as HttpWebRequest;
if (request != null)
{
request.ContentType = "application/xml";
request.Method = method;
}
//var objContent = HttpContentExtensions.CreateDataContract(requestBody);
if(method == "POST" && requestBody != null)
{
byte[] requestBodyBytes = ASCIIEncoding.UTF8.GetBytes(xmlRequestBody.ToString());
request.ContentLength = requestBodyBytes.Length;
using (Stream postStream = request.GetRequestStream())
postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);
}
if (request != null)
{
var response = request.GetResponse() as HttpWebResponse;
if(response.StatusCode == HttpStatusCode.OK)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
var reader = new StreamReader(responseStream);
responseMessage = reader.ReadToEnd();
}
}
else
{
responseMessage = response.StatusDescription;
}
}
return responseMessage;
}
And the call to the above method would be as shown below:
string sample = "<SampleItem xmlns=\"http://schemas.datacontract.org/2004/07/XmlRestService\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><Id>6</Id><StringValue>from client testing</StringValue></SampleItem>";
string serviceBaseUrl = "http://localhost:2517/Service1";
string resourceUrl = "/Create";
string method="POST";
UseHttpWebApproach<string>(serviceBaseUrl, resourceUrl, method, sample);
NOTE: Just make sure that your URL is correct
Are you running WCF application for the first time?
run below command to register wcf.
"%WINDIR%\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe" -r
After spending 2 days on this, using VS 2010 .NET 4.0, IIS 7.5 WCF and REST with JSON ResponseWrapped, I finally cracked it by reading from "When investigating further..." here https://sites.google.com/site/wcfpandu/useful-links
The Web Service Client code generated file Reference.cs doesn't attribute the GET methods with [WebGet()], so attempts to POST them instead, hence the InvalidProtocol, 405 Method Not Allowed. Problem is though, this file is regenerated when ever you refresh the service reference, and you also need a dll reference to System.ServiceModel.Web, for the WebGet attribute.
So I've decided to manually edit the Reference.cs file, and keep a copy. Next time I refresh it, I'll merge my WebGet()s back in.
The way I see it, it's a bug with svcutil.exe not recognising that some of the service methods are GET and not just POST, even though the WSDL and HELP that the WCF IIS web service publishes, does understand which methods are POST and GET??? I've logged this issue with Microsoft Connect.
When it happened to me, I just simply added the word post
to the function name, and it solved my problem. maybe it will help some of you too.
In the case I came up against, there was yet another cause: the underlying code was attempting to do a WebDAV PUT. (This particular application was configurable to enable this feature if required; the feature was, unbeknownst to me, enabled, but the necessary web server environment was not set up.
Hopefully this may help someone else.
The issue I have fixed, because your service is secured by login credential with user name and password, try set up the user name and password on the request, it will be working. Good luck!