I am new to Web API and HTTP.
I am using the MVC 6 (beta version). I have a proxy service (Web API) which has a POST method to get response from another service with XML content returned. I need to return the response content to the client since the client can't call the service directly.
// In my proxy service
public HttpResponseMessage Post(String content)
{
using ( HttpClient client = new HttpClient() ) {
.......
HttpResponseMessage response = client.PostAsync(uri, content).Result;
// I get everything I need in the "response".
// How to return the response or it body to the client.
// return response;
}
}
II need to return the "response" to the client with no or minimum changes. I tried "return response", or create a new HttpResponseMessage, but I only got something like
{"Headers":[{"Key":"Content-Type","Value":["text/xml"]}]}
in the body.
So is there a simple way to pass the response back to the client? Thanks.
The ASP.NET team is currently working on a "proxy middleware" that does exactly what you're looking for: https://github.com/aspnet/Proxy
Here's how it works internally:
public async Task Invoke(HttpContext context)
{
var requestMessage = new HttpRequestMessage();
if (string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
}
// Copy the request headers
foreach (var header in context.Request.Headers)
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
requestMessage.Headers.Host = _options.Host + ":" + _options.Port;
var uriString = $"{_options.Scheme}://{_options.Host}:{_options.Port}{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}";
requestMessage.RequestUri = new Uri(uriString);
requestMessage.Method = new HttpMethod(context.Request.Method);
using (var responseMessage = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
context.Response.Headers.SetValues(header.Key, header.Value.ToArray());
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers.SetValues(header.Key, header.Value.ToArray());
}
// SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
context.Response.Headers.Remove("transfer-encoding");
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
}
https://github.com/aspnet/Proxy/blob/dev/src/Microsoft.AspNet.Proxy/ProxyMiddleware.cs
Related
I am trying to insert a new entry in my database using web api. I have two web projects: one is a UI project where all the user interaction will occur and the other is a services project which will handle all interactions with my database.
Below is my post method that will take in form data for creating a new team.
// POST: Api/Team/Create
[HttpPost]
public ActionResult Create(Team team)
{
try
{
if (ModelState.IsValid)
{
HttpEndPointContext httpEndPoint = new HttpEndPointContext()
{
AuthenticationMethod = HttpAuthenticationMethods.None,
Ssl = false,
HttpMethod = HttpMethod.Post,
Path = "localhost:32173/api/team/",
QueryStrings = null,
PayloadData = SerializationHelper.Current.Serialize(team.ToString(), SerializationTypes.Xml)
};
IProcessResult result = HttpConnectionManager.Current.SendMessage(httpEndPoint);
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And this is my method for dealing with my PayloadStream/PayloadData attribute in the above method:
private void StreamPayload(HttpWebRequest webRequest, HttpEndPointContext httpEndPointContext)
{
if (httpEndPointContext.HttpMethod == new HttpMethod("GET"))
return;
//TODO: FIX MAYBE .... sometimes we want to post body with GET.
//Stream vs string
if (httpEndPointContext.PayloadStream == null)
{
//Wrap with SOAP Envelope and method if defined in SoapDefinition
string data = httpEndPointContext.PayloadData ?? String.Empty;
if (httpEndPointContext.SoapDefinition != null)
{
//If parameters is set, clear existing payload data.
data = String.Empty;
if (httpEndPointContext.SoapDefinition.Parameters != null)
foreach (var parameter in httpEndPointContext.SoapDefinition.Parameters)
{
data += String.Format("<{0}>{1}</{0}>", parameter.Key, parameter.Value);
}
data = String.Format("<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>" +
"<s:Body><{0} xmlns='{2}'>" +
"{1}</{0}></s:Body></s:Envelope>",
httpEndPointContext.SoapDefinition.SoapMethod, data,httpEndPointContext.SoapDefinition.SoapGlobalKey);
}
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(data);
httpEndPointContext.PayloadStream = new MemoryStream(byteArray);
}
using (Stream requestStream = webRequest.GetRequestStream())
{
StreamHelper.Current.CopyStreams(httpEndPointContext.PayloadStream, requestStream);
requestStream.Close();
}
}
And the code for getting the server response. I'm currently getting an Internal Server (500) Error. Not sure why.
public IProcessResult SendMessage(HttpEndPointContext httpEndPointContext)
{
HttpWebRequest webRequest = CreateWebRequest(httpEndPointContext);
StreamPayload(webRequest, httpEndPointContext);
IProcessResult result = GetWebResponse(webRequest, httpEndPointContext);
return result;
}
private IProcessResult GetWebResponse(HttpWebRequest webRequest, HttpEndPointContext httpEndPointContext)
{
//Get Response
WebResponse response;
IProcessResult result = new ProcessResult(Statuses.Success);
try
{
response = webRequest.GetResponse();
}
catch (System.Net.WebException ex)
{
//Do exception handling. Still get the response for 500s etc.
result.Error.Exception = ex;
result.Status = Constants.Statuses.FailedUnknown;
result.ResponseCodeDescription = ex.Status.ToString();
result.ResponseCode = ex.Status.ToString();
result.Error.ErrorCode = ex.Status.ToString();
response = ex.Response;
//The error did not have any response, such as DNS lookup.
if (response == null)
return result;
}
try
{
//Get the response stream.
Stream responseData = response.GetResponseStream();
if (responseData == null)
throw new CoreException("No Response Data in GetWebResponse.",
"No Response Data in GetWebResponse. EndPoint:{0}", httpEndPointContext.ToString());
// Open the stream using a StreamReader for easy access.
var reader = new StreamReader(responseData);
// Read the content.
result.ResponseData = reader.ReadToEnd();
}
finally
{
response.Close();
}
result.ResponseCode = ((int)((HttpWebResponse)response).StatusCode).ToString();
result.ResponseCodeDescription = ((HttpWebResponse) response).StatusDescription;
return result;
}
And finally, my method for inserting to the database, found in my services project:
//POST api/controller/5
public IProcessResult Insert(Team team)
{
return TeamBusinessManager.Current.Insert(SecurityManager.Current.ConnectionContext, new Team());
}
I'm confused as to why I'm getting the 500 error. I'm not sure if it's the PayloadData attribute in my POST method or is it something wrong with my method in my services project.
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 have created a ASP.Net Web API project and using this link. NServiceBus is integrated with web api. Here is my configuration at web api as a client.
Configure.Serialization.Xml();
Configure.Transactions.Enable();
Configure.With()
.DefineEndpointName(Constants.ClientName)
.DefaultBuilder()
.ForWebApi()
.Log4Net()
.UseTransport<Msmq>()
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus()
.Start();
This is how I'm sending message to Server
var response = await Bus.Send(Constants.ServerName, request)
.Register<ResponseModel>((NServiceBus.CompletionResult completionResult) =>
{
ResponseModel responseMessage = null;
if (completionResult != null && completionResult.Messages.Length > 0)
{
var status = completionResult.Messages[0] as RequestStatus?;
if (status == RequestStatus.Successful)
{
responseMessage = TransactionManager.TransactionDictionary[request.RequestId].ResponseModel;
}
}
return responseMessage;
});
This is how I'm sending response from Server. I have commented some lines to show what I have already tried.
public void Handle(RequestModel message)
{
ProcessRequest(message).RunSynchronously();
}
private async Task ProcessRequest(RequestModel message)
{
....
ResponseModel response = new ResponseModel();
response.RequestId = message.RequestId;
response.Result = await responseMessage.Content.ReadAsStringAsync();
//Bus.Send(Util.Constants.ClientName, response);
//Bus.Reply(response);
//Bus.Reply<ResponseModel>((ResponseModel response) =>
//{
// response = Bus.CreateInstance<ResponseModel>(r =>
// {
// r.RequestId = message.RequestId;
// r.Result = responseMessage.Content.ReadAsStringAsync().Result;
// });
//});
await Bus.Send(Util.Constants.ClientName, response).Register((NServiceBus.CompletionResult completionResult) =>
{
if (completionResult != null && completionResult.Messages.Length > 0)
{
var msg = completionResult.Messages[0];
if (msg != null)
{
var status = (RequestStatus)msg;
return status;
}
}
return RequestStatus.Error;
});
....
}
From any of the above response methods ultimately all messages end up in error queue.
Previously I was getting 'Could not enlist message' error. Now it is not throwing that error. But Server could not send message to Client.
I could not get what I'm doing wrong. Please also suggest if you see any scope for improvements.
I'm not sure if TransactionScope work correctly with async/await in C#. According to this question (Get TransactionScope to work with async / await) in .NET 4.5.1 there was introduced option for TransactionScope that enable mixing it with async/await. Unfortunately NServiceBus doesn't support .NET 4.5/4.5.1 so try just remove async/await.
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 have a problem with sending POST http request. It stops on (HttpWebResponse)request.GetResponse() and after timeout throws timeout expired exception, but if i send the same request via GET all works fine.
Does any body know what it can be?
try
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
if (content != null)
request.GetRequestStream().Write(content, 0, content.Length);
using (var response = (HttpWebResponse)request.GetResponse())
{
return new Response(response);
}
}
catch (WebException exception)
{
return new Response(exception);
}
Most likely, this is due to the code on the server not exposing this method as a POST. If the server doesn't explicitly set anyhting, it defaults to GET only.
Fixed problem with this code:
using (var requestStream = request.GetRequestStream())
{
if (content != null)
{
requestStream.Write(content, 0, content.Length);
}
requestStream.Close();
using (var response = (HttpWebResponse)request.GetResponse())
{
return new Response(response);
}
}