すべてのリクエストを別のHTTPサービスにルーティングするHTTPプロキシを構築したいと思います。Web-APIでこれを実現する方法について誰か良い提案がありますか?
私の状況は、2つのグループのサービスがあるということです。1つは外部からアクセス可能で、もう1つはアクセスできません。すべてのロジックと構成が存在する内部サービスを呼び出すために、外部サービスからルーターを提供したいと思います。
すべてのリクエストを別のHTTPサービスにルーティングするHTTPプロキシを構築したいと思います。Web-APIでこれを実現する方法について誰か良い提案がありますか?
私の状況は、2つのグループのサービスがあるということです。1つは外部からアクセス可能で、もう1つはアクセスできません。すべてのロジックと構成が存在する内部サービスを呼び出すために、外部サービスからルーターを提供したいと思います。
はい、とても簡単です。DelegatingHandlerから派生するクラスを作成し、SendAsyncをオーバーライドして、構成オブジェクトのMessageHandlersコレクションに追加するだけです。
ここでは、JSONをXMLに、またはその逆に変換する、私が作成したセルフホストのコンソールアプリケーションプロキシを示します。それはあなたにそれがどのように行われるかについての考えを与えるはずです。
class Program
{
static void Main(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8081");
var server = new HttpSelfHostServer(config);
var originServerUri = new Uri("http://example.org/origin-server/");
config.MessageHandlers.Add(new Converter(originServerUri));
server.OpenAsync().Wait();
Console.Read();
server.CloseAsync().Wait();
}
}
public class Converter : DelegatingHandler
{
private HttpClient _HttpClient = new HttpClient();
private Uri _OriginServer;
public Converter(Uri originServer)
{
_OriginServer = originServer;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
Console.WriteLine(new HttpMessageContent(request).ReadAsStringAsync().Result);
var newRequest = CreateNewRequest(request);
var t = _HttpClient.SendAsync(newRequest);
await t;
if (t.IsCompleted)
{
try
{
var response = CreateNewResponse(t.Result);
Console.WriteLine("--->");
Console.WriteLine(new HttpMessageContent(response).ReadAsStringAsync().Result);
return response;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(ex.Message)};
}
}
else
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(t.Exception.Message)};
}
}
private HttpRequestMessage CreateNewRequest(HttpRequestMessage request)
{
var newRequest = new HttpRequestMessage();
newRequest.Headers.Clear();
foreach (var header in request.Headers)
{
newRequest.Headers.Add(header.Key, header.Value);
}
if (request.Content.Headers.ContentLength != 0)
{
newRequest.Content = TranslateContent(request.Content);
}
newRequest.Headers.Host = null;
newRequest.Method = request.Method;
newRequest.RequestUri = new Uri(_OriginServer, request.RequestUri.PathAndQuery);
return newRequest;
}
private HttpResponseMessage CreateNewResponse(HttpResponseMessage response)
{
response.Content = TranslateContent(response.Content);
return response;
}
private HttpContent TranslateContent(HttpContent httpContent)
{
var mediatype = httpContent.Headers.ContentType.MediaType;
if (mediatype.Contains("+xml"))
{
return TranslateXmlToJson(httpContent);
}
else
{
return TranslateJsonToXml(httpContent);
}
}
private HttpContent TranslateJsonToXml(HttpContent content)
{
var mediatype = content.Headers.ContentType.MediaType;
string json = content.ReadAsStringAsync().Result;
XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(json);
return new StringContent(doc.OuterXml, Encoding.UTF8, mediatype.Replace("+json", "+xml"));
}
private HttpContent TranslateXmlToJson(HttpContent content)
{
var mediatype = content.Headers.ContentType.MediaType;
XmlDocument doc = new XmlDocument();
doc.Load(content.ReadAsStreamAsync().Result);
string jsonText = JsonConvert.SerializeXmlNode(doc);
return new StringContent(jsonText, Encoding.UTF8, mediatype.Replace("+xml", "+json"));
}
}
返信ありがとうございます。最終的に、プロキシが必要なサービスが現在 web-api を使用していないため、IHttpHandler を使用することにしました。これは、次のようなことを行うことによって行われました。
public class MyProxy : IHttpHandler
{
private static readonly string CoreServicesUrl = "localhost:1234";
public void ProcessRequest(HttpContext context)
{
string serviceUrl = context.Request.Headers.Get("ServiceUrl");
if (serviceUrl == null)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
string url = string.Format("http://{0}/api/{1}", CoreServicesUrl, serviceUrl);
HttpResponseMessage response = RouteRequest(context, url);
// This will handle both successful and unsuccessful responses
context.Response.StatusCode = (int)response.StatusCode;
var content = response.Content.ReadAsStringAsync().Result;
context.Response.Write(content);
}
private static HttpResponseMessage RouteRequest(HttpContext context, string url)
{
var client = new HttpClient();
if (context.Request.HttpMethod == "GET")
return client.GetAsync(url).Result;
if (context.Request.HttpMethod == "POST")
{
var stream = context.Request.GetBufferlessInputStream();
var sr = new StreamReader(stream);
var body = sr.ReadToEnd();
try
{
return client.PostAsync(url, new StringContent(body, Encoding.UTF8, "application/json")).Result;
}
catch (Exception ex)
{
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError) {ReasonPhrase = ex.Message};
return response;
}
}
// Other HTTP verbs are not supported
return new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
public bool IsReusable
{
get
{
return false;
}
}
}