I need to create an ASP.NET Core 3 Web API that understand this URL
http://myapp.com/MyASPNetCore3WebApi/myController/myWebMethod?user=A0001
and one zipfile which goes as a content. This is the code that calls the needed API, which I need to create:
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(URI);
httpWebRequest.Timeout = -1;
httpWebRequest.KeepAlive = false;
httpWebRequest.Method = "POST";
httpWebRequest.ProtocolVersion = HttpVersion.Version10;
httpWebRequest.ContentType = "application/octet-stream";
httpWebRequest.Accept = "application/octet-stream";
httpWebRequest.ContentLength = data.Length;
Stream requestStream = httpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
The code above is working fine, it is used everyday, sending data to a java web service, now I am replacing that system for a new one in ASP.NET Core and I can't change the caller's code, that's why I need to create a Web API that understand that URL.
I have wrote this code in my Web API, but I guess I am missing something that I canĀ“t figure it out because I get an error ion the client (code above)
[HttpPost("myWebMethod")]
public FileStreamResult myWebMethod(string user, [FromBody] Stream compress)
{
byte[] zip = ((MemoryStream)compress).ToArray();
byte[] data = ZipHelper.Uncompress(zip);
.....................
}
The error I get in the client is this:-
[System.Net.WebException] {"The remote server returned an error: (415)
Unsupported Media Type."} System.Net.WebException
Thanks in advance for any help
If the goal is to read the raw request content, this can be done using HttpContext controller property. HttpContext has Request property that provides access to the actual HTTP request.
No additional model properties or controller arguments are needed to access raw request stream. It's important to note that FromBody and FromForm binding should not be used in this case.
There are couple notes regarding the code in the example from the original question.
byte[] zip = ((MemoryStream)compress).ToArray();
byte[] data = ZipHelper.Uncompress(zip);
The HttpContext.Request.Body property does not return MemoryStream, it returns its own implementation of a Stream. It means that there is no ToArray method.
When reading the entire content of a request directly into the server's memory, it is better to check the content length, otherwise the client can crash the server by sending a large enough request.
Using *Async methods when reading the content of the request will improve performance.
I am working on (built-in web apis) provided by whatsapp business api. As a newbie in .net core razor pages and web apis. I want to know how can I get access to the body of the post request api. Take an example below for sending a message
Post: {URL}/v1/messages
Request Body:
"to": "",
"message_type:"
"message_text:"
"recipient_type: "individual | group""
How can I make a call to the builtin api and access the body parts of it?
Ofcourse, we as a developer can use postman for checking the working of api. But take this as a client and for the client we have some fields like
To:
Message:
How can take these fields and put it into the api call body and then when the user click on the send, the api call works and shows whatever we want to show the user for example a model with send successfully etc.
You can call the API using HttpClient.
Add the URL in await client.PostAsync() function. If you have authorization use client.DefaultRequestHeaders.Authorization otherwise omit it
string myContent = "";
string myJson = <JsonQuery>;
using (HttpClient client = new HttpClient())
{
// If any authorization available
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenLabel.Text.Trim());
using (HttpResponseMessage response = await client.PostAsync("https:url", new StringContent(myJson, Encoding.UTF8, "application/json")))
{
using (HttpContent content = response.Content)
{
myContent = await content.ReadAsStringAsync();
}
}
}
Update
Content
string myJson = "{\"subject\": }";
URL
using (HttpResponseMessage response = await client.PostAsync("{{URL}}/v1/groups", new StringContent(myJson, Encoding.UTF8, "application/json")))
Header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "");
I'm using the onelogin REST api to log a user in: https://developers.onelogin.com/api-docs/1/samples/login-user-via-api.
I have followed all the steps successfully to generate a session token with no issues.
The documentation then says to post the session token to this url: https://admin.us.onelogin.com/session_via_api_token
However, when do the post to that URL with the session token it simply re-directs me to the onelogin Sign On Page.
Here is the c# code for the post. I have a valid session token in variable: session_token:
string url = "https://admin.us.onelogin.com/session_via_api_token";
StringBuilder postData = new StringBuilder();
postData.Append("session_token=" + HttpUtility.UrlEncode(session_token) + "&");
postData.Append("auth_token=" + HttpUtility.UrlEncode(""));
//ETC for all Form Elements
// Now to Send Data.
StreamWriter writer = null;
request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postData.ToString().Length;
try
{
writer = new StreamWriter(request.GetRequestStream());
writer.Write(postData.ToString());
}
finally
{
if (writer != null)
writer.Close();
}
This appears to be server side code so this will never be able to successfully get a session with the end-user's browser.
In order for this flow to work properly, you need to redirect the end-user's browser to the https://admin.us.onelogin.com/session_via_api_token URL with just the auth_token value as a POST parameter.
All the above code will do is allow your back end server to get a session cookie, which doesn't help your end-user establish a session at all.
More details can be found here: https://developers.onelogin.com/api-docs/1/samples/login-user-via-api
I want to use the Jenkins Remote API, and I am looking for safe solution. I came across Prevent Cross Site Request Forgery exploits and I want to use it, but I read somewhere that you have to make a crumb request.
How do I get a crumb request in order to get the API working?
I found this https://github.com/entagen/jenkins-build-per-branch/pull/20, but still I don't know how to fix it.
My Jenkins version is 1.50.x.
Authenticated remote API request responds with 403 when using POST request
I haven't found this in the documentation either. This code is tested against an older Jenkins (1.466), but should still work.
To issue the crumb use the crumbIssuer
// left out: you need to authenticate with user & password -> sample below
HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
String crumbResponse = toString(httpclient, httpGet);
CrumbJson crumbJson = new Gson().fromJson(crumbResponse, CrumbJson.class);
This will get you a response like this
{"crumb":"fb171d526b9cc9e25afe80b356e12cb7","crumbRequestField":".crumb"}
This contains two pieces of information you need
the field name with which you need to pass the crumb
the crumb itself
If you now want to fetch something from Jenkins, add the crumb as header. In the sample below I fetch the latest build results.
HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);
Here is the sample code as a whole. I am using gson 2.2.4 to parse the response and Apache's httpclient 4.2.3 for the rest.
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.*;
import com.google.gson.Gson;
public class JenkinsMonitor {
public static void main(String[] args) throws Exception {
String protocol = "http";
String host = "your-jenkins-host.com";
int port = 8080;
String usernName = "username";
String password = "passwort";
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(host, port),
new UsernamePasswordCredentials(usernName, password));
String jenkinsUrl = protocol + "://" + host + ":" + port + "/jenkins/";
try {
// get the crumb from Jenkins
// do this only once per HTTP session
// keep the crumb for every coming request
System.out.println("... issue crumb");
HttpGet httpGet = new HttpGet(jenkinsUrl + "crumbIssuer/api/json");
String crumbResponse= toString(httpclient, httpGet);
CrumbJson crumbJson = new Gson()
.fromJson(crumbResponse, CrumbJson.class);
// add the issued crumb to each request header
// the header field name is also contained in the json response
System.out.println("... issue rss of latest builds");
HttpPost httpost = new HttpPost(jenkinsUrl + "rssLatest");
httpost.addHeader(crumbJson.crumbRequestField, crumbJson.crumb);
toString(httpclient, httpost);
} finally {
httpclient.getConnectionManager().shutdown();
}
}
// helper construct to deserialize crumb json into
public static class CrumbJson {
public String crumb;
public String crumbRequestField;
}
private static String toString(DefaultHttpClient client,
HttpRequestBase request) throws Exception {
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = client.execute(request, responseHandler);
System.out.println(responseBody + "\n");
return responseBody;
}
}
Or you can use Python and requests instead
req = requests.get('http://JENKINS_URL/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)', auth=(username, password))
print(req.text)
will give you the name and the crumb:
Jenkins-Crumb:e2e41f670dc128f378b2a010b4fcb493
This Python function gets the crumb, and additionally uses the crumb to post to a Jenkins endpoint. This is tested with Jenkins 2.46.3 with CSRF protection turned on:
import urllib.parse
import requests
def build_jenkins_job(url, username, password):
"""Post to the specified Jenkins URL.
`username` is a valid user, and `password` is the user's password or
(preferably) hex API token.
"""
# Build the Jenkins crumb issuer URL
parsed_url = urllib.parse.urlparse(url)
crumb_issuer_url = urllib.parse.urlunparse((parsed_url.scheme,
parsed_url.netloc,
'crumbIssuer/api/json',
'', '', ''))
# Use the same session for all requests
session = requests.session()
# GET the Jenkins crumb
auth = requests.auth.HTTPBasicAuth(username, password)
r = session.get(crumb_issuer_url, auth=auth)
json = r.json()
crumb = {json['crumbRequestField']: json['crumb']}
# POST to the specified URL
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
headers.update(crumb)
r = session.post(url, headers=headers, auth=auth)
username = 'jenkins'
password = '3905697dd052ad99661d9e9f01d4c045'
url = 'http://jenkins.example.com/job/sample/build'
build_jenkins_job(url, username, password)
Meanwhile you can generate an API token in order to prevent having to include your password in the source code provided by the solutions above:
https://wiki.jenkins.io/display/JENKINS/Authenticating+scripted+clients
Refer - https://support.cloudbees.com/hc/en-us/articles/219257077-CSRF-Protection-Explained
If you authenticate with a username and a user API token then a crumb is not needed from Jenkins 2.96 weekly/2.107 LTS. For more information please refer to CSRF crumb no longer required when authenticating using API token or JENKINS-22474.
User cheffe's answer helped 90%. Thanks for giving us the right direction.
The missing 10% revolved around HTTP username and password authentication.
Since the Codenameone Java API I was using did not have the Authentication Class,
new UsernamePasswordCredentials(usernName, password));
I used:
String apiKey = "yourJenkinsUsername:yourJenkinsPassword";
httpConnection.addRequestHeader("Authorization", "Basic " + Base64.encode(apiKey.getBytes()));
User cheffe's Java snippet worked great for me on Jenkins v2.89.3 (Eclipse.org) and another Jenkins instance I use, at v2.60.3 (once enabled1).
I've added this to a Maven mojo2 I use for pushing locally-edited config.xml changes back to the server.
1 CSRF Protection
2 Hudson job sync plugin
In any of these answers I didn't find an option to use Jenkins API token.
I really tried all of these options but if you're enabling CSRF protection, you should access Jenkins APIs with Jenkins API token instead of normal password.
This token can be generated by each individual user in the user config page.
The token can be used as follows-
JenkinsApi::Client.new(server_url: jenkins_url, username: jenkins_user, password: jenkins_token)
P.S. - This initialization is for a Ruby Jenkins API client
I am stumped on this problem and have come humbled to the experts on advice for my problem.
I have an ASP.NET MVC app that is Siteminder enabled. In addition, this app has a section of URLS that are web services which provide data to another application. Those URLS have been configured for "bypass" Siteminder authentication in the Siteminder setup. I've double checked the bypass to make sure the Siteminder configuration is correct. I can enter those URLs in a browser and the JSON data is displayed "without" Siteminder authentication. However....
The problem is when I use HttpWebResponse, Stream and StreamReader to retrieve the JSON data when siteminder is enabled, I get the Siteminder "login page HTML" as the string when StreamReader.ReadToEnd() is evoked instead of the JSON formatted data???
This is baffling because I another developer here can access the same web service and get the "correct" JSON formatted data in a PYTHON app. Also, I put it in a regular ASP.NET app so it's not an MVC issue. I get the same result.
Is there another class or library I should use? Is there a configuration setting I need to pass to the web service call? Any help would be greatly appreciated.
Here is the code of one of the web service calls.
public static string GetData()
{
string host = (string)System.Configuration.ConfigurationManager.AppSettings["WEBSERVICE_GET"];
string URL = host + "Api/GetData";
var end = string.Empty;
try
{
HttpWebRequest request = WebRequest.Create(URL) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
using (Stream responseStream = response.GetResponseStream())
{
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
{
throw new HttpException((int)response.StatusCode, response.StatusDescription);
}
using (StreamReader reader = new StreamReader(responseStream))
{
end = reader.ReadToEnd();
reader.Close();
}
responseStream.Close();
response.Close();
}
}
catch (Exception ex)
{
EmailNotification.SendErrorEmail("Could not get Data from WEBSERVICE + ex);
}
return end;
}