Ответ 1
Лучший способ, я думаю, не включает WebOperationContext
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);
Как мне получить доступ к телу запроса HTTP POST в службе WCF REST?
Вот определение службы:
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
MyData GetData();
}
Вот реализация:
public MyData GetData()
{
return new MyData();
}
Я использую следующий код для доступа к HTTP-запросу:
IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;
Но IncomingWebRequestContext дает доступ только к заголовкам, а не к телу.
Спасибо.
Лучший способ, я думаю, не включает WebOperationContext
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);
Используйте
OperationContext.Current.RequestContext.RequestMessage
Извините за поздний ответ, но я подумал, что добавлю, что работает с параметрами UriTemplate, чтобы получить тело запроса.
[ServiceContract]
public class Service
{
[OperationContract]
[WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")]
public Stream TestPost(string param0, string param1)
{
string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
return ...;
}
}
body
назначается строка из необработанных байтов тела сообщения.
Похоже, что поскольку WCF предназначен для транспортного протокола-агностик, метод службы не предоставляет доступ к HTTP-специфической информации по умолчанию. Тем не менее, я просто столкнулся с хорошей статьей, описывающей "Режим совместимости ASP.Net", который по сути позволяет вам указать, что ваша служба действительно предназначена для просмотра через HTTP.
http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx
Добавление конфигурации aspNetCompatibilityEnabled
в Web.config
, в сочетании с атрибутом AspNetCompatibilityRequirements
к требуемым операциям обслуживания, должно сделать трюк. Я собираюсь попробовать это сам.
Haw-бен
Вышеупомянутые ответы помогли мне придумать это решение. Я получаю json с парами имя/значение. { "p1": 7514, "p2": 3412, "p3": "joe smith"...}
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json
)]
public Stream getJsonRequest()
{
// Get the raw json POST content. .Net has this in XML string..
string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString();
// Parse the XML string into a XML document
XmlDocument doc = new XmlDocument();
doc.LoadXml(JSONstring);
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
node.Name // has key
node.InnerText; // has value
Мои извинения за предыдущий ответ, я тупо предположил, что я просто бросил WebOperationContext, чтобы получить в OperationContext, к сожалению, реальный ответ намного уродлив.
Позвольте мне предисловие к этому, должен быть лучший способ!
Сначала я создал свой собственный объект контекста, который может быть привязан к существующему объекту OperationContext.
public class TMRequestContext : IExtension<OperationContext> {
private OperationContext _Owner;
public void Attach(OperationContext owner) {
_Owner = owner;
}
public void Detach(OperationContext owner) {
_Owner = null;
}
public static TMRequestContext Current {
get {
if (OperationContext.Current != null) {
return OperationContext.Current.Extensions.Find<TMRequestContext>();
} else {
return null;
}
}
}
}
Чтобы получить доступ к этому новому объекту контекста, вам нужно добавить его как расширение к текущему. Я сделал это, создав класс инспектора сообщений.
public class TMMessageInspector : IDispatchMessageInspector {
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
OperationContext.Current.Extensions.Add(new TMRequestContext());
return null;
}
}
Для того, чтобы инспектор сообщений работал, вам нужно создать новое "поведение". Я сделал это, используя следующий код.
public class TMServerBehavior : IServiceBehavior {
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
//Do nothing
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) {
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector());
}
}
}
}
Поведение, которое вы должны добавить в конфигурационный файл, хотя я сделал это, создав новый хост и добавив объект поведения вручную в метод OnOpening. Я закончил использование этого класса гораздо больше, чем просто доступ к объекту OperationContext. Я использовал их для регистрации и переопределения обработки ошибок и доступа к объекту запроса http и т.д. Таким образом, это не совсем нелепое решение, как кажется. Почти, но не совсем!
Я действительно не помню, почему я не мог просто напрямую обратиться к OperationContext.Current. У меня слабое воспоминание о том, что он всегда пуст, и этот неприятный процесс был единственным способом получить экземпляр, на самом деле содержащий достоверные данные.
Вот что я сделал:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace YourSpaceName
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class YourClassName
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "YourMethodName({id})", BodyStyle = WebMessageBodyStyle.Bare)]
public Stream YourMethodName(Stream input, string id)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Content-Type", "application/json");
string response = [email protected]"{{""status"": ""failure"", ""message"": ""Please specify the Id of the vehicle requisition to retrieve."", ""d"":null}}";
try
{
string response = (new StreamReader(input)).ReadToEnd();
}
catch (Exception ecp)
{
response = [email protected]"{{""status"": ""failure"", ""message"": ""{ecp.Message}"", ""d"":null}}";
}
return new MemoryStream(Encoding.UTF8.GetBytes(response));
}
}
}
Этот код просто читает входные данные и записывает их. тело запроса POST автоматически присваивается вводу независимо от имени переменной. Как видите, в вашем UriTemplate все еще могут быть переменные.