How can I read the HTTP response headers from a web service response in C#?
After digging through MSDN, all I needed to do was to override the GetWebResponse method, and then I could access the response headers:
public class MyWSProxy : HttpWebClientProtocol
{
protected override WebResponse GetWebResponse(WebRequest request)
{
System.Net.WebResponse wr = base.GetWebResponse(request);
// read a response header
object val = wr.Headers["key"];
return wr;
}
}
If you're getting back an HttpResponse, you can just query the HttpResponse.Headers property.
Can't you just refer to HttpContext.Current.Response.Headers in your webservice?
I'm not sure if that'll work.
Related
I am playing around with an app using HttpWebRequest to dialog with a web server.
I followed standard instructions I found on the web to build my request function that I tried to make as generic as possible (I try to get a unique method regardless of the method: PUT, POST, DELETE, REPORT, ...)
When I submit a "REPORT" request, I get two access logs on my server:
1) I get response 401 after following line is launched in debugger
reqStream.Write(Encoding.UTF8.GetBytes(body), 0, body.Length);
2) I get response 207 (multi-get, which is what I expect) after passing the line calling Request.GetResponse();
Actually, it seems to be the Request.GetRequestStream() line that is querying the server the first time, but the request is only committed once passing the reqStream.Write(...) line...
Same for PUT and DELETE, the Request.GetRequestStream() again generates a 401 access log on my server whereas the Request.GetResponse(); returns code 204.
I don't understand why for a unique request I have two server access logs, especially one that seems to be doing nothing as it always returns code 401... Could anybody explain what is going on? Is it a flaw in my code or a bad design due to my attempt to get a generic code for multiple methods?
Here is my full code:
public static HttpWebResponse getHttpWebRequest(string url, string usrname, string pwd, string method, string contentType,
string[] headers, string body) {
// Variables.
HttpWebRequest Request;
HttpWebResponse Response;
//
string strSrcURI = url.Trim();
string strBody = body.Trim();
try {
// Create the HttpWebRequest object.
Request = (HttpWebRequest)HttpWebRequest.Create(strSrcURI);
// Add the network credentials to the request.
Request.Credentials = new NetworkCredential(usrname.Trim(), pwd);
// Specify the method.
Request.Method = method.Trim();
// request headers
foreach (string s in headers) {
Request.Headers.Add(s);
}
// Set the content type header.
Request.ContentType = contentType.Trim();
// set the body of the request...
Request.ContentLength = body.Length;
using (Stream reqStream = Request.GetRequestStream()) {
// Write the string to the destination as a text file.
reqStream.Write(Encoding.UTF8.GetBytes(body), 0, body.Length);
reqStream.Close();
}
// Send the method request and get the response from the server.
Response = (HttpWebResponse)Request.GetResponse();
// return the response to be handled by calling method...
return Response;
}
catch (Exception e) {
throw new Exception("Web API error: " + e.Message, e);
}
I want to send query to my backend server via a proxy script. But it does not return files correctly.
public class HttpWebRequestRunner : IWebRequestRunner
{
public HttpWebResponse Run(string backendUri)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(backendUri);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
return response;
}
}
My backend server is closed to internet so I send parameters my Asp.Net Mvc application. And it send request to backend server.
Backend server is returning file for this request: http://10.0.2.1/Employee/CV/1445
Inmy mvc controller I use this:
public class PersonController : Controller
{
public ActionResult GetCv(int id)
{
HttpWebResponse response = new HttpWebResponse();
HttpWebResponse webResponse = response.run("http://10.0.2.1/Employee/CV/1445");
context.HttpContext.Response.ContentType = wbResponse.ContentType;
webResponse.GetResponseStream().CopyTo(context.HttpContext.Response.OutputStream);
// write result...
}
}
Now
if I send request to backend from browser this url http://10.0.2.1/Employee/CV/1445 it returns 1445.pdf file
But If I send request via prox app like this http://localhost:22414/Person/GetCv/1445
this returns a file named file but not pdf extension.
File names are in header info. webResponse.Headers["Content-Disposition"]. So you have to use like this:
context.HttpContext.Response.Headers.Set(
"Content-Disposition",
webResponse.Headers.Get("Content-Disposition"));
You need to relay the Content-Disposition HTTP header as well.
In the latest Web API 2, how do I configure it so that it will only return a reply if the Accept header is application/json? This API will only support json, if any other accept header is sent then an error must be thrown. There will be no xml and even no html interface.
If the client asks for xml or html or anything, we need to throw an error to let them know they used the wrong accept type. We must not mask this problem by replying with the correct json when they have requested a type that is not actually supported.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
var response = request.GetResponse();
And the json result is returned successfully. But if there is any other Accept then an error is returned
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml"; // or text/html or text/plain or anything
var response = request.GetResponse();
Returns HTTP 501 Not Implemented or similar http error code.
This question is not a duplicate of How do I get ASP.NET Web API to return JSON instead of XML using Chrome? - that question asks how to also return json. My question is how to only return json, and only if the client asks for json. If the client asks for any other type like xml or html, then an error is returned.
This page shows how to access content negotiation directly. You could conceivably instead pass some filtered subset of this.Configuration.Formatters containing only the desired formatters to IContentNegotiator.negotiate, like so:
ContentNegotiationResult result = negotiator.Negotiate(
typeof(Product), this.Request, this.Configuration.Formatters.Where(/* some expression that excludes all but the json formatter */);
This looks quite clumsy and would be a lot of dull boilerplate, so Javad_Amiry's answer is probably better, but this is another option that might be useful in specific cases.
You can clear all formatters except JSON:
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());
Or you can change the default Web API’s content negotiation mechanism:
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}
// in app_start:
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
See the article.
UPDATE:
Well, if you want to return a HTTP error on non-json request, you can do it by implementing a custom IHttpModule for checking header. But, for self-host apps it won't work. So, it's better to use extend a custom DelegatingHandler. For example, you can use this one:
public class FilterJsonHeaderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken){
if (request.Headers.Accept.All(a => a.MediaType == "application/json")){
// if we have only application/json, so the pipeline continues
return base.SendAsync(request, cancellationToken);
}
// otherwise, do whatever you want:
var response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
var completionSource = new TaskCompletionSource<HttpResponseMessage>();
completionSource.SetResult(response);
return completionSource.Task;
}
}
and register it in app_start:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new FilterJsonHeaderHandler());
// your other settings...
}
}
NOTE: the code is not tested. Please let me know if there is any error.
If I put the URL at the browser, my server responds properly (a XML).
Although, if this same URL pass through the WebClient.DownloadingString() method, something in the URL changes, and my server responds properly, but with an access denied message (XML, too), as if something had changed.
"Error message"
<?xml version="1.0" encoding="ISO-8859-1"?><said:service xmlns:said="http:xxx"><said:codigo_erro>8</said:codigo_erro><said:mensagem_erro>Unable</said:mensagem_erro></said:service>
The URL used on request is like this one:
http://...<parameter1>S<%2Fparameter1>%0D%0A++<parameter2>S<%2Fparameter2>%0D%0A++<parameter3>S<%2Fparameter3>%0D%0A<%2Fqueryservice>%0D%0A%09%09
I have already tried change de Encode to UT8, ISO, etc. No one of them worked.
You have to be sure that you're sending all the necessary data, cookies and request headers that the server is expecting.
I advise you to install Fiddler Web Debugger and monitor successful requests from web browser, after that try to recreate such requests in your application.
Maybe server is redirecting you to some error page because WebClient is not handling cookies. You can create your own version of WebClient and add cookie support. Create a class that inhertis from WebClient and override GetWebRequest method, there you have to add CookieContainer. Following is a simple implementation of WebClient that handles cookies:
public class MyWebClient : WebClient
{
public CookieContainer CookieContainer { get; private set; }
public MyWebClient()
{
this.CookieContainer = new CookieContainer();
}
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = this.CookieContainer;
(request as HttpWebRequest).AllowAutoRedirect = true;
}
return request;
}
}
I am using WebClient to get some info from a page that is sometimes not available [302 Moved Temporarily]. So i want to program to detect whether the page exists
I tried to override the WebClient WebResponse with the following code to only return the page when it's status is OK but it did not worked.
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
if (response is HttpWebResponse)
return (response as HttpWebResponse).StatusCode == HttpStatusCode.OK ? response : null;
return null;
}
when i used my overriden class to get the page (when it is unavailable) it just redirected and did not returned null
Get code
private async Task<string> Get(string uri)
{
return await Handler.DownloadStringTaskAsync(new Uri(uri));
}
[WHAT I WANT TO ACHIEVE] : i want the web client tried to get the page but it was not found so it has been redirected to another page.
WebClient will follow redirects automatically by default (up to a maximum number).
If you override GetWebRequest to modify the returned HttpWebRequest, setting its AllowAutoRedirect property to false, then I believe it will just give you back the 302 directly - although possibly via an exception...
This won't tell you the status but it can be inferred by the fact that you were redirected.
if(reponse.ResponseUri != request.RequestUri) {
// if you really want to know the status
// set AllowAutoRedirect = false;
// and send another request in here.
}