I created a HttpSelfHostServer service hosting controller for communication in web API
problem is that I want the ability to view images
ex: http://localhost:8080/images/pic.jpg
but the self host doesn't allow me to do this
it use to be IAppBuilder.UseFileServer but it's different with HttpSelfHostServer
here's the server code
using Autofac;
using Autofac.Integration.WebApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace WebAPIServerV2
{
public class WebAPIServer
{
private HttpSelfHostServer m_Server;
private ILifetimeScope m_lifetimeScope;
public WebAPIServer(ILifetimeScope lifetimeScope)
{
m_lifetimeScope = lifetimeScope;
}
public void Start(string url)
{
if (m_Server != null)
{
Stop();
}
var config = new HttpSelfHostConfiguration(url);
config.DependencyResolver = new AutofacWebApiDependencyResolver(m_lifetimeScope);
config.MaxReceivedMessageSize = int.MaxValue;
config.Routes.MapHttpRoute(
name: "ServerAPI",
routeTemplate: "server/{controller}/{action}"
);
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter());
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
m_Server = new HttpSelfHostServer(config);
m_Server.OpenAsync().Wait();
if (Environment.UserInteractive)
Console.WriteLine($"WebAPI server start at:{url}");
}
public void Stop()
{
m_Server?.Dispose();
m_Server = null;
}
}
}
well I found it!
https://social.msdn.microsoft.com/Forums/en-US/ed7e5248-69e2-4644-aa02-8da7d13c9765/hosting-a-file-with-httpselfhostserverhttpselfhostconfiguration?forum=aspwebapi
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace WebAPIServer
{
internal class StaticFileHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri.AbsolutePath.StartsWith("/server/"))
return base.SendAsync(request, cancellationToken);
var path = Environment.CurrentDirectory;
var newUri = new Uri(path);
var newPath = newUri.AbsolutePath + request.RequestUri.AbsolutePath;
newUri = new Uri(newPath);
if (!File.Exists(newUri.LocalPath))
{
return base.SendAsync(request, cancellationToken);
}
else
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = request.CreateResponse();
response.Content = new StreamContent(new FileStream(newUri.LocalPath, FileMode.Open));
return response;
});
}
}
}
}
ofcourse server is the prefix for the API so it was easy
Related
I want to post a request to a url, using content-type and authorization. I am getting error 400 "Bad Request". I have tested with the same data and headers in python and it worked.
So I think there is something wrong with how I set up my headers. But I haven't found a workaround.
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Http.Headers;
using System.Net.Http;
namespace PostRequests
{
//var data={"bio":"cheesy"}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static readonly HttpClient client = new HttpClient();
private void go_Click(object sender, EventArgs e)
{
var values = new Dictionary<string, string>
{
{"bio", "test"}
};
sendPost("https://discord.com/api/v9/users/#me", values);
}
async void sendPost(string url, Dictionary<string, string> vals)
{
string authValue = "mytoken";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authValue);
var content = new FormUrlEncodedContent(vals);
var request = new HttpRequestMessage(new HttpMethod("PATCH"), url) { Content = content };
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.SendAsync(request);
}
}
}
Background: I am using ASP.NET Core 3.1, and integration testing a REST service that requires cookie authentication.
Candidate solution below.
Note:
The reason I use a vanilla Host instead of TestServer is because of the cookie requirement. When using TestServer, it provides an HttpClient for you, but the client does not pass cookies back to the server.
I also attempted to use a custom HttpClient with TestServer. That consistently generated a System.Net.Sockets.SocketException (No connection could be made because the target machine actively refused it.)
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi; // Contains my Startup.cs
namespace WebApiTest
{
[TestClass]
public class UserTest
{
static IHost HttpHost;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
HttpHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.Build();
await HttpHost.StartAsync();
}
[ClassCleanup]
public static async Task ClassCleanup()
{
await HttpHost.StopAsync();
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
public static HttpClient GetCookieHttpClient()
{
SocketsHttpHandler handler = new SocketsHttpHandler
{
AllowAutoRedirect = false,
CookieContainer = new CookieContainer(),
UseCookies = true
};
return new HttpClient(handler);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = GetCookieHttpClient())
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
HttpClient is a thin-client; it doesn't do anything unless you explicitly tell it to. In other words, it will never send the cookie for you; you must add a Cookie header to the request with the cookie value for each request. The test server "client" is just an HttpClient instance set up to proxy requests to the test server. You should use the test server, as prescribed, along with its client, and then add the Cookie header the requests you make with that.
Solutions based on Chris Pratt's suggestions
After some further digging, Microsoft provides a solution for this (WebApplicationFactory):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
[TestClass]
public class Class2
{
static WebApplicationFactory<Startup> Factory;
static WebApplicationFactoryClientOptions ClientOptions;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
Factory = new WebApplicationFactory<Startup>();
ClientOptions = new WebApplicationFactoryClientOptions();
ClientOptions.AllowAutoRedirect = false;
ClientOptions.HandleCookies = true;
ClientOptions.BaseAddress = new Uri("http://localhost:5000");
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = Factory.CreateClient(ClientOptions))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
In case you want to stick with TestServer, here is a manual Cookie-passing implementation:
using Microsoft.AspNetCore.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
public class CookieHttpClient : IDisposable
{
private static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return httpContent;
}
private static IEnumerable<string> GetCookieStrings(CookieCollection collection)
{
List<string> output = new List<string>(collection.Count);
foreach (Cookie cookie in collection)
{
output.Add(cookie.Name + "=" + cookie.Value);
}
return output;
}
private HttpClient client;
private CookieContainer container;
public CookieHttpClient(HttpClient client)
{
this.client = client;
this.container = new CookieContainer();
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri)
{
return await this.SendAsync(method, uri, null);
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri, object data)
{
HttpRequestMessage request = new HttpRequestMessage(method, uri);
// Add data
if (data != null)
{
request.Content = GetHttpContent(data);
}
// Add cookies
CookieCollection collection = this.container.GetCookies(uri);
if (collection.Count > 0)
{
request.Headers.Add("Cookie", GetCookieStrings(collection));
}
HttpResponseMessage response = await this.client.SendAsync(request);
// Remember cookies before returning
if (response.Headers.Contains("Set-Cookie"))
{
foreach (string s in response.Headers.GetValues("Set-Cookie"))
{
this.container.SetCookies(uri, s);
}
}
return response;
}
public void Dispose()
{
this.client.Dispose();
}
}
[TestClass]
public class Class1
{
static TestServer TestServer;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
IWebHostBuilder builder = new WebHostBuilder()
.UseStartup<Startup>();
TestServer = new TestServer(builder);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (CookieHttpClient client = new CookieHttpClient(TestServer.CreateClient()))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.SendAsync(HttpMethod.Post, new Uri("http://localhost:5000/api/auth/login"), credentials);
response = await client.SendAsync(HttpMethod.Get, new Uri("http://localhost:5000/api/users/" + credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
I am currently working on an asp.net Web API project. The project consists of the following files: "S3BucketController", "IS3Service", "S3Service". Basically, I am trying to call the AmazonS3 web service to create and retrieve data. To make my code cleaner, I reference on the following article on dependency injection
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
I am using the Unity.WebApi NuGet package (Unity 5.2.0 and Unity.WebApi 5.3.0) The issue I am facing is that when attempting to run the code, I get the error: Make sure that the controller has a parameterless public constructor. I've research similar issues in StackOverflow but still could not solve my issue.
Update I am still trying to solve this issue, any help is greatly appreciated
Below is my code:
S3BucketController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Task_7.Services;
namespace Task_7.Controllers
{
public class S3BucketController : ApiController
{
private readonly IS3Service _service;
// Initialize at constructor
// injected the IS3Service,
public S3BucketController(IS3Service service)
{
_service = service;
}
[HttpPost]
[Route("api/S3Bucket/CreateBucket")]
public async Task<IHttpActionResult> CreateBucket([FromBody] string bucketName)
{
var response = await _service.createBucketAsync(bucketName);
return Ok(response);
}
[HttpPost]
public async Task<IHttpActionResult> AddFile([FromBody] string bucketName)
{
await _service.createFileAsync(bucketName);
return Ok();
}
}
}
IS3Service
using System.Threading.Tasks;
using Task_7.Models;
namespace Task_7.Services
{
public interface IS3Service
{
Task<S3Response> createBucketAsync(string bucketName);
Task createFileAsync(string bucketName);
}
S3Service
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Amazon.S3.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using Task_7.Models;
namespace Task_7.Services
{
public class S3Service : IS3Service
{
private readonly IAmazonS3 _client;
public S3Service(IAmazonS3 client)
{
_client = client;
}
public async Task<S3Response> createBucketAsync(string bucketName)
{
try
{
if (await AmazonS3Util.DoesS3BucketExistAsync(_client, bucketName) == false)
{
var putBucketRequest = new PutBucketRequest
{
BucketName = bucketName,
UseClientRegion = true
};
var response = await _client.PutBucketAsync(putBucketRequest);
return new S3Response
{
Message = response.ResponseMetadata.RequestId,
Status = response.HttpStatusCode
};
}
}
catch (AmazonS3Exception e)
{
return new S3Response
{
Status = e.StatusCode,
Message = e.Message
};
}
catch (Exception e)
{
return new S3Response
{
Status = HttpStatusCode.InternalServerError,
Message = e.Message
};
}
return new S3Response
{
Status = HttpStatusCode.InternalServerError,
Message = "Something Went Wrong"
};
}
private const string filePath = "C:\\Users\\ randomguy1\\Desktop\\Project\\Background_Images";
public async Task createFileAsync(string bucketName)
{
try
{
var fileTransferUtility = new TransferUtility(_client);
await fileTransferUtility.UploadAsync(filePath, bucketName);
}
catch (AmazonS3Exception e)
{
Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
}
//https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-install-assemblies.html#net-dg-nuget
}
}
}
UnityResolver.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Dependencies;
using Unity;
using Unity.Exceptions;
namespace Task_7.Resolver
{
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
container.Dispose();
}
}
}
WebApiConfig.cs
using Amazon.S3;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Task_7.Resolver;
using Task_7.Services;
using Unity;
using Unity.Lifetime;
namespace Task_7
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IS3Service, S3Service>(new HierarchicalLifetimeManager());
container.RegisterType<IAmazonS3>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
I am trying to post a message on the #general channel and this worked when was doing it through a console app but Now I am using MVC and the message doesn't seem to get posted. Also, earlier I was using the webhook URL and now I am using the access token that I have.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
namespace SlackClient.Controllers
{
public class SlackClient
{
private readonly Uri _uri;
private readonly Encoding _encoding = new UTF8Encoding();
public SlackClient(string urlWithAccessToken)
{
_uri = new Uri(urlWithAccessToken);
}
//Post a message using simple strings
public void PostMessage(string text, string username = null, string channel = null)
{
Payload payload = new Payload()
{
Channel = channel,
Username = username,
Text = text
};
PostMessage(payload);
}
//Post a message using a Payload object
public void PostMessage(Payload payload)
{
string payloadJson = JsonConvert.SerializeObject(payload);
using (WebClient client = new WebClient())
{
NameValueCollection data = new NameValueCollection();
data["payload"] = payloadJson;
var response = client.UploadValues(_uri, "POST", data);
//The response text is usually "ok"
string responseText = _encoding.GetString(response);
}
}
}
//This class serializes into the Json payload required by Slack Incoming WebHooks
public class Payload
{
[JsonProperty("channel")]
public string Channel { get; set; }
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
}
And the other class is SlackClientTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SlackClient.Controllers
{
public class SlackClientTest
{
void TestPostMessage()
{
string urlWithAccessToken = "https://srishti2604.slack.com/services/hooks/incoming-webhook?token=my-tokenHere.";
SlackClient client = new SlackClient(urlWithAccessToken);
client.PostMessage(username: "Mr. Torgue",
text: "THIS IS A TEST MESSAGE! SQUEEDLYBAMBLYFEEDLYMEEDLYMOWWWWWWWW!",
channel: "#general");
}
}
}
Could someone tell me what might me wrong?
My console app looks like this
SlackClient.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace SlackProject1
{
public class SlackCient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient();
public SlackCient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> SendMessageAsync(string message,
string channel = null, string username = null)
{
var payload = new
{
text = message,
channel,
username,
};
var serializedPayload = JsonConvert.SerializeObject(payload);
var response = await _httpClient.PostAsync(_webhookUrl,
new StringContent(serializedPayload, Encoding.UTF8, "application/json"));
return response;
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SlackProject1
{
class Program
{
static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri("https://hooks.slack.com/services/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");
var slackClient = new SlackCient(webhookUrl);
while (true)
{
Console.Write("Type a message: ");
var message = Console.ReadLine();
var response = await slackClient.SendMessageAsync(message);
var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
Console.WriteLine($"Received {isValid} response.");
}
}
}
}
The requirement
The organization have a proxy and it requires authentication.
A third party software supports proxy, but does not support proxy authentication.
So we want to write a small proxy program that delegates the requests from the third party software to the organization proxy.
(We use OWIN self hosting for this service. the project is a console application)
The class ProxyHandler
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
...
public class ProxyHandler : DelegatingHandler
{
public static IWebProxy Proxy { get; set; }
public ProxyHandler()
{
}
protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage req, CancellationToken ct)
{
var fwd = new UriBuilder(req.RequestUri);
fwd.Port = 443; //The software uses https only;
//in the test below this is not even called so does not matter
req.RequestUri = fwd.Uri;
var h = new HttpClientHandler();
h.UseCookies = true;
h.AllowAutoRedirect = true;
h.Proxy = Proxy;
var c = new HttpClient(h);
var resp = await c.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
return resp;
}
}
The class Startup
using Owin;
using System.Net.Http;
using System.Web.Http;
...
class Startup
{
public void Configuration(IAppBuilder b)
{
var cfg = new HttpConfiguration();
cfg.Routes.MapHttpRoute(
"Proxy",
"{*path}",
new {path=RouteParameter.Optional},
null,
HttpClientFactory.CreatePipeline(
new HttpClientHandler(),
new DelegatingHandler[] { }
));
b.UseWebApi(cfg);
}
}
The code that starts the proxy service and test with a download
using Microsoft.Owin.Hosting;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
...
var p = new WebProxy(proxy_uri, true)
{
Credentials = new NetworkCredential(domain_user, password)
};
ProxyHandler.Proxy = p;
var app = WebApp.Start<Startup>("http://localhost:8181/");
using (app)
{
var wc = new WebClient();
wc.Proxy = new WebProxy("localhost:8181", true);
var downloaded = await wc.DownloadStringTaskAsync
(new Uri("http://example.com/));
Console.WriteLine(downloaded);
}
The result
The call to wc.DownloadStringTaskAsync throws HTTP 400 error. The SendAsync method was not called at all.
The question
How can I make it work?