I'm working with a rather large query string(~30+ parameters) and am trying to pass them to a WCF service I've setup.
I've run into a few issues specifically with the UriTemplate field. This service is setup to access a third party Api, so the query string may or may not contain all parameters. I'm curious if the best approach is to build a query string and pass that to the WCF service or to pass each parameter(and in some cases String.Empty) individually.
I've currently tried to dynamically build up a query string, however have hit a wall with either a 403 error when I try to pass the entire string( "?prm1=val&prm2=val" ) into the uritemplate of "ApiTool.jsp{query}", or I hit an invalid uritemplate response due to the fact I don't have name/value pairs listed.
I am pretty sure you'll need to list the parameters individually. Otherwise, UriTemplate will end up escaping things for you:
var ut = new UriTemplate("Api.jsp{query}");
var u = ut.BindByName(new Uri("http://localhost"), new Dictionary<string, string>() { { "query", "?param1=a¶m2=b" } });
Console.WriteLine(u); // http://localhost/Api.jsp%3Fparam1=a¶m2=b
You can 'unescape' querystring with IClientMessageInspector.
public class UriInspector: IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// change/replace request.Headers.To Uri object;
return null;
}
}
See MSDN how to add this to your Endpoint object.
Related
I am having some issues finding information about adding some logic field in my RestRequest using V 107. I am trying to add a filter to my GET query
dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'
There are a few other queries in the call which i am using Dictionary<string, string> to store them, and it works great however it only works if i am looking for something equal to, as adding it to the parameters it seems by default its equal to and i am not finding any way to add any other logic, gt/ge/lt/le etc. using the older version i would just append the url adding the logic i need, but i am not seeing a way to append the url either. Looking over their documentation i either missed it, cant find it, or its not there. Any help would be greatly appreciated! My method looks like this
public static async Task<string> GET_API(String RequestUrl, string RequestObject, Dictionary<string, string> parameters)
{
var request = new RestRequest(RequestObject);
var options = new RestClientOptions(RequestUrl)
{
ThrowOnAnyError = true,
Timeout = -1
};
var client = new RestClient(options);
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("Bearer " + TokenManager.GetAccessTokenString("TRN"));
foreach (var parameter in parameters)
{
request.AddQueryParameter(parameter.Key, parameter.Value);
}
var response = await client.GetAsync(request);
return response.Content.ToString();
}
I send the BaseURL , the RequestObject would be table i am calling in the base URL, and my dictionary item contains the Field name, and the field values that i am dynamically generating on another method that would append the string. and example would be
parameters.Add("dl_document_name", "TableA");
which would append the URL with dl_document_name eq 'TableA'
it would call the API after i add the OAuth Token i create and return the data i need and send it back. or another option i guess could be appending the string with the logic i need to return the data
You should use OData, it's easy to implement and it has different kind of filters, you also can set which filters are usable and which aren't.
https://www.odata.org/
I figured out a work around, if i only have one i can add it to the first parameter and adding the filter as the first key, which will work unless i have multiple conditions that are not eq
parameters.Add("filter","dl_document_indexed_date gt '2020-12-07T08:30:42.483Z'");
I have a service that contains a lot of functions with out parameters to return a few extra parameters.
I was wondering if it's possible to call a regular asp.NET web api service with out parameters and receive a value(in the form of out parameters, separate from the return value) from the service.
If it is possible, could you elaborate on what I need to do to achieve this?
Any help will be well appreciated.
No, this is not possible. The response from WebAPI will be a normal HTTP response with a body where the serialized returned data will be.
Of course, as usual, your response can be a complex object to serialize and you can include those out returns as members of it. For example:
public IHttpActionResult GetResponse(int id)
{
int outputInt;
string outputString;
YourMethodWithOutParameters(id, out outputInt, out outputString);
return Ok(new
{
Id = id,
OutputInt = outputInt,
OutputString = outputString,
});
}
We're working on developing an application that uses Plivo for sending and receiving SMS messages. For every request that Plivo sends, they also send a signature in the HTTP header so that we can verify the request came from Plivo and not from a random user.
https://www.plivo.com/docs/xml/request/#validation
To do this validation, we require the POST content as a query string (eg: To=15555555555&From=11234567890&TotalRate=0&Units=1&Text=Text!&TotalAmount=0&Type=sms&MessageUUID=2be622bc-79f8-11e6-8dc0-06435fceaad7).
Current solution
This is what we have so far:
private bool VerifyPlivo(object thing, HttpRequestMessage Request)
{
if (Request.Headers.Contains("X-Plivo-Signature"))
{
Dictionary<string, string> reqParams = (from x in thing.GetType().GetProperties() select x).ToDictionary(x => x.Name, x => (x.GetGetMethod().Invoke(thing, null) == null ? "" : x.GetGetMethod().Invoke(thing, null).ToString()));
IEnumerable<string> headerValues = Request.Headers.GetValues("X-Plivo-Signature");
string signature = headerValues.FirstOrDefault();
return XPlivoSignature.Verify(Request.RequestUri.ToString(), reqParams, signature, plivoToken);
}
else
{
return false;
}
}
[Route("RecieveSMS")]
[HttpPost]
public HttpResponseMessage RecieveSMS(PlivoRecieveSMS req)
{
if (!VerifyPlivo(req, Request))
{
return new HttpResponseMessage(HttpStatusCode.Forbidden);
}
... // do actual work here
}
This works by using the object that it maps to PlivoRecieveSMS and doing some reflection to get the properties and values, and sticking them in a Dictionary. This works well especially given our lack of the preferred solution...
Preferred solution
Right now, we require a model (PlivoRecieveSMS) to map the data, and then do introspection to find the key/values. We would like to move the logic to an extension of System.Web.Http.AuthorizeAttribute, so that we can do something as simple as:
[AuthorizedPlivoApi]
[Route("RecieveSMS")]
[HttpPost]
public HttpResponseMessage RecieveSMS(PlivoRecieveSMS req)
{
... // do actual work here
}
The actual authorization is done in AuthorizedPlivoApi - if it's not valid, the request never reaches the controller. But we cannot do this at the moment because we can't map it to a specific object inside of AuthorizedPlivoApi.
I would like to access the POST key's / values directly, or perhaps map it to a dynamic object that isn't pre-defined before hand. If I can do that, we can then achieve our preferred solution.
tl;dr: is there any way to push application/x-www-form-urlencoded data from a POST request into a Dictionary<string,string>() without using a specific model?
I'm very new to WCF and have a question that I hope you can help me with.
Project: I've been asked to create a WCF service that allows a client to be able to upload a word file along with some metadata.
The client doesn't have a sample of the POST call they'll be making so I can't create a class off of that WSDL, but the post would contain data like this:
{
author: 'John Doe',
pages: '32',
size: '14432',
authToken: '322222222233',
encoding: 'binary'
name: 'Document1.doc'
}
I'm thinking of creating an [OperationContract] such as bool UploadFile(CustomDocument inputDocument) instead of bool UploadFile (string author, string encoding ....).
My question: If I use a custom object as an input parameter (CustomDocument) for an [OperationContract] would the client be able to pass all the information as string, int etc in its service call, or would they have to first create an instance of CustomDocument on their end, and then include that object in the post?
Sorry, I'm very new to WCF, my apologies in advance if this question doesn't make any sense; I'll update it based on your feedback.
You have to make sure that CustomDocument is a Serializable object and have a public parameterless constructor.
The easiest way is share the dll that contains the class CustomDocument between the WebService and the Application that will use it.
But personally when I try to send a complex object to a WebServce I prefer to serialize as a byte array and then Deserialize inside the WebService.
Good luck!
You don't need the custom object CustomDocument. Suppose you have this service
[ServiceContract]
public interface IMyTestServce
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "/Upload?author={author}&pages={pages}&size={size}&name={name}&authToken={authToken}")]
void Upload(string author, int pages, long size, string name,
string authToken,
Stream file);
}
public class MyTestService : IMyTestServce
{
public void Upload(string author, int pages, long size, string name,
string authToken,
Stream file)
{
Console.WriteLine(String.Format("author={0}&pages={1}&size={2}&name={3}&authToken={4}", author, pages, size, name, authToken));
Console.WriteLine(new StreamReader(file).ReadToEnd());
}
}
You can easily call it like
HttpClient client = new HttpClient();
var content = new StreamContent(File.OpenRead(filename);
await client.PostAsync("http://localhost:8088/Upload?author=aa&pages=3&name=bb&authToken=112233", content);
PS: You need to use webHttpBinding (or WebServiceHost if it is not hosted in IIS).
public static class HttpRequestHelper
{
public static string RequestBody()
{
var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
var bodyText = bodyStream.ReadToEnd();
return bodyText;
}
}
I plan to call this from ActionFilters to log incoming requests. Of course there could be multiple simultaneous requests.
Is this approach ok?
Is your question from the perspective of concurrency or ASP.NET Web API in general? Every request has its own context and you are okay with multiple requests going on in parallel. But here are two things for you to look at.
(1) Since you are using HttpContext, you are locking yourself to web hosting (IIS), which in many cases should be okay. But I would like you to be aware of this.
(2) Your code HttpRequestHelper.RequestBody() will work when called from an action filter, as you mentioned. However, if you try to call this from other places, say a message handler, this will not work. When I say this will not work, parameter binding that binds request body to action method parameter will not work. You will need to seek to the beginning once you are done. The reason it works from action filter is that binding would have already happened by the time action filter runs in the pipeline. This is another thing you might need to be aware of.
I've needed use InputStream of Http Request. I have a WebApp and IOS App that navigates to a aspx page, if the url request contains some parameters i read the information in database and if i not find any parameters in url request i read the request body and i work fine !
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (string.IsNullOrEmpty(Request.QueryString["AdHoc"]) == false)
{
string v_AdHocParam = Request.QueryString["AdHoc"];
string [] v_ListParam = v_AdHocParam.Split(new char[] {','});
if (v_ListParam.Length < 2)
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(WS_DemandeIntervention));
WS_DemandeIntervention response = (WS_DemandeIntervention)jsonSerializer.ReadObject(Request.InputStream);
....
}
if (string.IsNullOrEmpty(Request.QueryString["IdBonDeCommande"])==false)
{
....