So, I'm using the Developer's Guide to the WCF REST Starter Kit and having a problem with the RequestInterceptor. I have the exact code the guide is showing but for some reason the method never ends.
This is my code:
public override void ProcessRequest(ref RequestContext requestContext)
{
GenerateErrorResponse(requestContext, HttpStatusCode.Forbidden, "shit happens!");
}
public void GenerateErrorResponse(RequestContext context, HttpStatusCode statusCode, string errorMessage)
{
XElement response = XElement.Load(new StringReader(string.Format(ERROR_HTML, errorMessage)));
Message reply = Message.CreateMessage(MessageVersion.None, "action", response);
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty()
{
StatusCode = statusCode,
//StatusDescription = errorMessage
};
responseProp.Headers[HttpRequestHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
context.Reply(reply);
context = null;
}
My call gets stuck at context.Reply(reply); I have no idea what I'm doing wrong... any heads up?
thanks
Ok, my bad... it was a simple/stupid issue.
First of all there was an Exception taking place that I was not seeing... once I added a try catch and tracing I discovered I was getting the following error: "System.InvalidOperationException: This collection holds request headers and cannot contain the specified response header".
After looking a little closer I noticed I was adding a HttpRequestHeader instead of the HttpResponseHeader... my bad :(
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
Related
I'm trying to configure basic access authorization on my .NET HttpListener but I keep running into the same error. I've tried all the solutions that can be found on this site and many others but with no success.
I need to use admin/admin as username/password for basic authentication. The wikipedi page shows how the header should look, which I followed.
I keep getting the error "The header WWW-Authenticate must be changed with the correct method, parameter:name" there is however no parameter called "name" that must be added, like shown on the wikipedia page. I've ran out of options unfortunately and hope that somebody can help.
My code is as follows
private void WebRequestCallback(IAsyncResult result)
{
if (httpListener == null)
{
return;
}
HttpListenerContext context = httpListener.EndGetContext(result);
if (basicAccessAuth)
{
HttpListenerRequest Request = context.Request;
HttpListenerResponse Response = context.Response;
httpListener.AuthenticationSchemes = AuthenticationSchemes.Basic;
NameValueCollection nvCol = new NameValueCollection();
nvCol.Add("Authorization", "admin:admin");
httpListener.Realm = "Overflow";
Request.Headers.Add(nvCol); // error gets thrown here, missing "name" parameter
Response.Headers.Add("WWW-Authenticate: Basic YWRtaW46YWRtaW4=");
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
MessageBox.Show(identity.Name);
}
httpListener.BeginGetContext(new AsyncCallback(WebRequestCallback), httpListener);
if (ReceiveWebRequest != null)
{
ReceiveWebRequest(context);
}
ProcessRequest(context);
}
I have managed to figure out my issue. I added the header in the wrong way, it should be Response.AddHeader instead of Response.Headers.Add
I have following code in a class library to create a mailjet (https://github.com/mailjet/mailjet-apiv3-dotnet) contact list.
public async Task<ResultMessage> CreateContactList(string contactListName)
{
MailjetResponse response = null;
ResultMessage resultMessage = null;
try
{
var client = new MailjetClient(MjApikeyPublic, MjApikeyPrivate);
MailjetRequest request = new MailjetRequest
{
Resource = Contactslist.Resource,
}
.Property(Contactslist.Name, contactListName);
response = await client.PostAsync(request);
resultMessage = GetResultMessage(response, exception: null);
}
catch (Exception e)
{
resultMessage = GetResultMessage(response: response, exception: e);
}
return resultMessage;
}
This works fine when I use it with a console application, and when debugged it hits the below line and gets http response from mailjet. And when I check it in the mailjet profile I can see the contact list with passed name is created at their end.
resultMessage = GetResultMessage(response, exception: null);
But when I used the same with a ASP.NET MVC application, it does not receive the http response, the application hangs forever. And when debugged it never reaches the above line. But, the list gets created in the mailjet end, so the only issue is it does not capture the http response from the mailjet.
Can someone please help me?
I am writing a nuget package that will consume a REST API I wrote. Im having some difficulty in calling it asynchronously using RestSharp.
I have the following controller method:
[HttpGet("{id}")]
public async Task<IActionResult> GetFeatureByIdAsync(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
ModelState.AddModelError("id-Null", "The id parameter cannot be null.");
return BadRequest(ModelState);
}
var feature = await _director.GetFeatureById(id);
if (feature == null)
return NotFound();
var model = new ViewModel<Feature>(feature);
return Ok(model);
}
I have the follow class library in a seperate solution:
public async Task<bool> IsSwitchEnabledAsync(string name)
{
string fullyFormedUrl = $"{_url}/{name}";
var client = new RestClient(fullyFormedUrl) { Encoding = Encoding.UTF8 };
var request = new RestRequest(Method.GET) { RequestFormat = DataFormat.Json };
var result = await client.ExecuteTaskAsync<FeatureViewModel>(request);
var message = $"Response: {result.StatusCode}, {result.ResponseStatus}, " +
$"\n\nError: {result.ErrorException}, {result.ErrorMessage} " +
$"\n\nData: {result.Content}";
Debug.WriteLine(message);
return result.Data != null;
}
I have the following basic integration test sandbox:
[TestFixture]
public class Sandbox
{
private FeatureSwitch _sut;
[OneTimeSetUp]
public void Setup()
{
const string machineName = "localhost";
const int port = 5000;
_sut = new FeatureSwitch(machineName, port);
}
[Test]
public async Task ShouldReturnTrue()
{
var result = await _sut.IsSwitchEnabledAsync("Release_US501_AddUser");
result.Should().BeTrue();
}
}
I am looking for a little education on the best approach on how to call the API correctly? As my sandbox code is failing with an Internal Server 500 Error. This is because the api method is waiting for the nested async call to complete. So the initial request returns back to the sandbox code before the nested api call has a chance to complete.
The sandbox test code needs to wait for the API to return a result. At the moment it is not doing this.
So what is the correct approach here?
UPDATE: My issue was down to incorrect usage of a Mongo library method.
My issue came from a .chained method call on a Mongo library method:
I was trying to do the following:
Collection.Find(x => x.Name.ToLower().Equals(name.ToLower())
The code compiled but it was throwing an exception that VS studio 17 was not breaking on so i had no idea where the issue was occurring. With code lens turned on, I noticed that the failed requests highlighted in red. This opened an App Insights window which highlighted a break down of 4 InvalidOperationException() that had been thrown. That's how i discovered what the Internal Server Error 500 was about.
Anyways that's how i solved Async Web API problem - it was my first time using it on a web service so I had no idea what the expected default behaviour ought to be.
EDIT: Please note, I know the heart of the problem lies in the service I need to communicate with is not following protocol. This is software which I am not capable of touching and will not be changed anytime soon. Thus, I need help with circumventing the problem and breaking protocol. Development at its finest!
I'm attempting to communicate with an external service. Whomever made it decided to split various calls into not just different folders, but also HTTP request types. The problem here, is that I need to send a GET request that includes content.
Yes, this violates the protocol.
Yes, this works if I formulate the call using Linux commands.
Yes, this works if I manually craft the call in Fiddler (although Fiddler gets angry at the breach of protocol)
When I craft my call, it's wrapped in an async method. Sending it, however, results in an error:
Exception thrown: 'System.Net.ProtocolViolationException' in mscorlib.dll ("Cannot send a content-body with this verb-type.")
Code for the call:
/// <summary>
/// Gets a reading from a sensor
/// </summary>
/// <param name="query">Data query to set data with</param>
/// <returns></returns>
public async Task<string> GetData(string query)
{
var result = string.Empty;
try
{
// Send a GET request with a content containing the query. Don't ask, just accept it
var msg = new HttpRequestMessage(HttpMethod.Get, _dataApiUrl) { Content = new StringContent(query) };
var response = await _httpClient.SendAsync(msg).ConfigureAwait(false);
// Throws exception if baby broke
response.EnsureSuccessStatusCode();
// Convert to something slightly less useless
result = await response.Content.ReadAsStringAsync();
}
catch (Exception exc)
{
// Something broke ¯\_(ツ)_/¯
_logger.ErrorException("Something broke in GetData(). Probably a borked connection.", exc);
}
return result;
}
_httpClient is created in the constructor and is a System.Net.Http.HttpClient.
Does anyone have an idea how to override the regular protocols for the HttpClient and force it to make the call as a GET call, but with a content containing my query for the server?
To me the less devastating way to achieve that is to set ContentBodyNotAllowed field of Get KnownHttpVerb to false using reflection.
You can try with this:
public async Task<string> GetData(string query)
{
var result = string.Empty;
try
{
var KnownHttpVerbType = typeof(System.Net.AuthenticationManager).Assembly.GetTypes().Where(t => t.Name == "KnownHttpVerb").First();
var getVerb = KnownHttpVerbType.GetField("Get", BindingFlags.NonPublic | BindingFlags.Static);
var ContentBodyNotAllowedField = KnownHttpVerbType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance);
ContentBodyNotAllowedField.SetValue(getVerb.GetValue(null), false);
var msg = new HttpRequestMessage(HttpMethod.Get, _dataApiUrl) { Content = new StringContent(query) };
var response = await _httpClient.SendAsync(msg).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
result = await response.Content.ReadAsStringAsync();
}
catch (Exception exc)
{
_logger.ErrorException("Something broke in GetData(). Probably a borked connection.", exc);
}
return result;
}
I'm unable to Post a string to a WebAPI from a Console Application:
Console App:
public void Main()
{
try
{
ProcessData().Wait();
}
catch (Exception e)
{
logger.Log(LogLevel.Info, e);
}
}
private static async Task ProcessData()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(API_BASE_URL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var response = await client.PostAsJsonAsync("api/teste", "test_api");
}
}
catch (Exception e)
{
logger.Log(LogLevel.Info, e);
}
I try to call a WebAPI in an MVC4 Web Application:
namespace Heelp.Api
{
public class TesteController : ApiController
{
private readonly ICommentService _commentService;
public TesteController(ICommentService commentService)
{
_commentService = commentService;
}
public string Post(string comment)
{
var response = "OK";
try
{
_commentService.Create(new Core.DtoModels.CommentDto { CommentType = "Like", Email = "p#p.pt", Message = "comment" });
}
catch(Exception e)
{
response = e.Message;
}
return response;
}
}
}
[EDIT]
After testing with fiddler, I get the error:
{"Message":"An error has occurred.","ExceptionMessage":"Type 'Heelp.Api.TesteController' does not have a default onstructor",
"ExceptionType":"System.ArgumentException","StackTrace":"
at System.Linq.Expressions.Expression.New(Type type)\r\n at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor, Type controllerType)"}
The Route is the Default.
I don't know how to debug it, and why it's not working.
What am I doing wrong here?
Here goes my answer, Web API always had this problem with Simple Types. Read Rick Strahls Blog Post on Web API problems with Simple Data Types
What you can do it, have WEB API code in this way -
public HttpResponseMessage Post(FormDataCollection formData)
{
return null;
}
And let HttpClient request in this way -
HttpClient client = new HttpClient();
var nameValues = new Dictionary<string, string>();
nameValues.Add("Name", "hi");
var Name = new FormUrlEncodedContent(nameValues);
client.PostAsync("http://localhost:23133/api/values", Name).ContinueWith(task =>
{
var responseNew = task.Result;
Console.WriteLine(responseNew.Content.ReadAsStringAsync().Result);
});
that will give the output as follows -
[--- EDIT based on question edit ---]
I checked the latest edit on question and the Error because of no Default Constructor. That error is because you got something wrong with Dependency Injection which you are doing for constructor injection. So please check that area. Also use Unity Web API Dependency Injection nuget to get the work done. Here goes complete tutorial in setting up DI using Unity.
Also please check the autofac if you need different versions of it from MVC and Web Api, at least that is the case with Unity though. I think you need to have Autofac.WebApi 3.1.0 for DI to work with Web API controllers.
First of all, thanks for all the help.
The problem was that in the MVC4 project where I add the Api, I was already using Autofac for Dependency Injection, but I was not aware that I also need DI for the ApiControllers.
So I installed using NuGet Autofac.WebApi 3.1.0 and add to the Autofac Config the lines:
In the Begin:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
In the End:
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
Now everything is working fine ! :)