Running ServiceStack 4.0.44 I have the following on my test client:
return client.Send(new GetVendor { VenCode = vencode });
vs what I had
// return client.Get(new GetVendor { VenCode = vencode });
and then on the server I have/had
public class VendorsService : Service {
public object Any(GetVendor request) {
var vendor = Db.SingleWhere<Vendors>("VenCode", request.VenCode);
return vendor;
}
//public object Get(GetVendor request) {
// var vendor = Db.SingleWhere<Vendors>("VenCode", request.VenCode);
// return vendor;
//}
}
//[Route("/vendor/{VenCode}", "GET")]
[Route("/vendor/{VenCode}")]
public class GetVendor : IReturn<Vendors> {
public string VenCode { get; set; }
}
public class Vendors {
:
:
}
My question is why when I pass "B&T" for VenCode -- and I understand that IIS is interpreting the & as part of the URL -- why does the Send work and return Vendors -- but the Get blows up with Bad Request unless I put
<httpRuntime requestPathInvalidCharacters="" />
into my web.config
Bottom line what is the difference? How would I implement CRUD routines with all the html special characters without modifying the registry etc? Or do I need to urlEncode them somehow?
Using a Get() API sends the request using the ?QueryString which is what requestPathInvalidCharacters is validating against.
When you use Send() you're sending a JSV serialized Request DTO via a HTTP POST which isn't validated by requestPathInvalidCharacters.
It's unlikely there's any way to disable ASP.NET's default behavior other than using Web.config, note this validation happens in ASP.NET before the request reaches ServiceStack.
Related
My goal is to call api (via post), accept payload as base type and later cast it to concrete type. If I do that from main solution (where my api stands), everything works well. But I can't understand why same code doesn't work from other solutions.
So I have my request (declared in different solutions)
namespace Nb
{
public class NbRequestBase
{
public string BaseProp { get; set; }
}
public class NbRequestConcrete : NbRequestBase
{
public string ConcreteProp { get; set; }
}
}
And this is my endpoint:
[HttpPost]
[Route("payments/nb")]
public IHttpActionResult Prepare(NbRequestBase request)
{
if(request is NbRequestConcrete)
{
}
try
{
// <<< INSERT CODE HERE >>>
NbRequestConcrete nbRequestConcrete = (NbRequestConcrete)request;
return Ok();
}
catch (Exception ex)
{
_logger.Error(ex);
return InternalServerError();
}
}
and this is my calling code:
NbRequestConcrete requestTwo = new NbRequestConcrete()
{
BaseProp = "BaseProp",
ConcreteProp = "ConcreteProp"
};
using (var client = new HttpClient())
{
var _clientId = "_clientId";
var _clientSecret = "_clientSecret";
client.BaseAddress = new Uri("http://localhost:50228");
#region Formatter
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
List<MediaTypeFormatter> formatters = new List<MediaTypeFormatter>();
formatters.Add(formatter);
#endregion
var responseMessage = client.PostAsync($"payments/nb?clientId={_clientId}&clientSecret={_clientSecret}", requestTwo, formatter).Result;
responseMessage.EnsureSuccessStatusCode();
}
If I put my calling code into other project/solution (for example just new console app), API endpoint is hit, but payload is null.
payload when called form console app
If I put exacly same calling code into project where my api is (for example in same API endpoint method, at try/catch block start and call it again), API endpoint is hit, payload is NOT null and casting works. Why is it? And how to fix it?
payload when called from same solution try/catch start
And BTW. How to make this call via postman?
Regards
This line tells the model binder to set the values of any matching properties in request to the value that was passed to the API:
public IHttpActionResult Prepare(NbRequestBase request)
The model binder does not attach all the other properties to the request, because it has no idea what they would be.
Problem was Assemblies name where NbRequestConcrete in console app lived in one assembly and on API lived in other. So request was different.
{
"$type": "Nb.NbRequestConcrete, Tester",
"ConcreteProp": "ConcreteProp",
"BaseProp": "BaseProp"
}
VS
{
"$type": "Nb.NbRequestConcrete, MYApi",
"ConcreteProp": "ConcreteProp",
"BaseProp": "BaseProp"
}
I have an Azure Function 2.x that reside on a static class that looks like this
[FunctionName("Register")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req, ILogger log)
{
MyTypeClass defReturn = new MyTypeClass();
HttpStatusCode defCode = HttpStatusCode.BadRequest;
/*
* Logics that might or might not changes
* defReturn and defCode value
*/
return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn))
}
How can i achieve the return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn)) part ? is there any such method or equivalent in Azure Functions 2.x ?
in Azure Functions 1.x i can do the equivalent with req.CreateResponse(defCode, defReturn) where req is HttpRequestMessage , but i'm trying to stick with 2.x template/standard
Additional explanation : The said Code should return HTTP 400 Bad Request with the defReturn as it's response body to the client. But when i change the defCode to HttpStatusCode.Accepted, it should return HTTP 202 Accepted with the same response body. How can i achieve this ?
Additional explanation#2 : (If i remember correctly) in ASP.NET Core 1.x i can exactly do like that, returning IActionResult by calling a static method StatusCode not StatusCodes (which is a static class that contains HTTP codes constants
Thank you
Quite late reply, but I was stumbling into the same problem today, so maybe this is helpful for other searchers
Option 1: Default Codes
This is stated in detail on the blog Here
Some codes like 200 and 400 are predefined and can be used by
return new OkObjectResult("Your message"); // 200
return new BadRequestObjectResult("Your error message"); // 400
These functions are not available for every known Status Codes but some of the most frequent.
Option 2: Manual setting Code
If you need specific codes, that are not provided by default, you can use the base classes and create them yourself.
To achieve the Teapot Response for example, you can just use
using Microsoft.AspNetCore.Http;
var result = new ObjectResult("Your message");
result.StatusCode = StatusCodes.Status418ImATeapot;
return result;
In this example, the Statuscode is used from the StatusCodes class, but you can use enter other codes as well (usually, just stick to these codes)
Also, the ObjectResult class offers additional formatting options, if needed.
You can create a model class in which you can define two properties, i.e. one form your status code and one for you Json object and later on you can return the complete model. Code sample would be like below:
public static class QueueTriggerTableOutput
{
[FunctionName("QueueTriggerTableOutput")]
[return: Table("outTable", Connection = "MY_TABLE_STORAGE_ACCT_APP_SETTING")]
public static Person Run(
[QueueTrigger("myqueue-items", Connection = "MY_STORAGE_ACCT_APP_SETTING")]JObject order,
ILogger log)
{
return new Person() {
PartitionKey = "Orders",
RowKey = Guid.NewGuid().ToString(),
Name = order["Name"].ToString(),
MobileNumber = order["MobileNumber"].ToString() };
}
}
public class Person
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Name { get; set; }
public string MobileNumber { get; set; }
}
on the receiving front, you can catch both the property.
P.S.- you have to change the return type of your function.
Hope it helps.
I have a web API which takes a class object as an input parameter and posts it to a WCF service operation contract. The WCF service is designed such that it takes any type of request and does internal mapping of what type of request has come and what code needs to be executed. The logic for that is given below.
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequestBase request)
{
if (request is Request1)
{
//Process user report request and send back the response
}
if (request is Request2)
{
//Process request 2 and send back the response
}
return PdfPrinterFacade.PrintPdf();
}
}
//IPdfPrinter
public interface IPdfPrinter
{
[OperationContract]
PdfPrinterResponse Print(PdfPrinterRequestBase request);
}
//PdfPrinterRequestBase
[DataContract]
[KnownType(typeof(Request1))]
[KnownType(typeof(Request2))]
public class PdfPrinterRequestBase : IRequest
{
[DataMember]
public RequestHeader ReqHdr { get; set; }
}
//Web API Request1
public PdfPrinterResponse Print(Request1 _request1)
{
PdfPrinterService.PdfPrinterClient
_Client = new PdfPrinterService.PdfPrinterClient();
return _Client.Print(_request1);
}
//Web API Request2
public PdfPrinterResponse Print(Request2 _request2)
{
PdfPrinterService.PdfPrinterClient _Client =
new PdfPrinterService.PdfPrinterClient();
return _Client.Print(_request2);
}
The above approach is okay for me, But planning to have only 1 API which takes any type of object and passes it to the WCF service and the mapping that i did (if request 1, process and send back response else if request 2 process and sendback etc) should work with 1 API call. Any help is greatly appreciated to achieve this.
Maybe this could work?
Pseudo code
[HttpPost]
public PdfPrinterResponse Print()
{
var json = Request.Content.ReadAsByteArrayAsync().Result;
Requeste1 request1;
if(TrySerialzieTo<Request1>(json, out request1))
// send request to wcf service
Requeste2 request2;
if(TrySerializeTo<Request2>(json, out requeste2))
// send request to wcf service
}
private bool TrySerializeTo<T>(string json, out T request)
{
// use JsonConvert.Deserialize<T>(json);
throw new NotImplementedException();
}
I have a ServiceStack web service that requires support for the X-HTTP-Method-Override header.
I tried simulating the Delete request to through a Get request with the X-HTTP-Method-Override header set but I get a:-
404 - Handler for Request not found
Here's what the request format:
Get - http://localhost/test/1
Headers
User-Agent: Fiddler
Host: localhost
X-HTTP-Method-Override: Delete
And the Service and its DTO implementation looks like:
[Route("/test/{id}", HttpMethods.Delete)]
public class TestRequest {
public int id { get; set; }
}
public class TestService : Service {
public object Delete(TestRequest request){
return request.id;
}
}
I found a snippet in the ServiceStack source that says X-HTTP-Method-Override feature is supported.
Is there something else I need to configure in the project to get this to work? Help please...
I figured it out, I have the add the Get verb to the request dto like this:
[Route("/test/{id}", "Delete,Get")]
public class TestRequest {
public int id { get; set; }
}
Now the Delete method will be called when simulated via the Get request via the X-HTTP-Method-Override
public class TestService : Service {
public object Delete(TestRequest request){
return request.id;
}
}
I am trying to make a simple api post call using servicestack and it keeps throwing an exception "not found". When the same post call is made directly to the api using a web browser rest api e.g. postman, the api call works.
I have decorated my request object with the route attributes
[Route("/register", "POST")]
public class Register : IReturn<RegistrationResponse>
{
public DateTime? BirthDate { get; set; }
public string Continue { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Password { get; set; }
}
The JsonServiceClient is initialised with the base uri but the following call fails
_client = new JsonServiceClient(_apiUri);
_client.HttpMethod = HttpMethods.Post;
var response = _client.Send(body);
The exception that I catch is:
$exception {"Not Found"} System.Exception
{ServiceStack.ServiceClient.Web.WebServiceException} at
ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowWebServiceException[TResponse](Exception
ex, String requestUri) at
ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowResponseTypeException[TResponse](Object
request, Exception ex, String requestUri) at
ServiceStack.ServiceClient.Web.ServiceClientBase.HandleResponseException[TResponse](Exception
ex, Object request, String requestUri, Func1 createWebRequest, Func2
getResponse, TResponse& response) at
ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](Object
request) at
ApiService`2.Post(String
path, TParams body) in
ApiService.cs:line 81
The documentation on the new API at servicestack mentions the use of the Route attributes decorating the request DTO and the use of the IReturn but from looking at the code behind the Send method, it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.
public virtual TResponse Send<TResponse>(object request)
{
var requestUri = this.SyncReplyBaseUri.WithTrailingSlash() + request.GetType().Name;
var client = SendRequest(requestUri, request);
try
{
var webResponse = client.GetResponse();
return HandleResponse<TResponse>(webResponse);
}
catch (Exception ex)
{
TResponse response;
if (!HandleResponseException(ex,
request,
requestUri,
() => SendRequest(HttpMethods.Post, requestUri, request),
c => c.GetResponse(),
out response))
{
throw;
}
return response;
}
}
What is causing the Not Found exception?
Everthing in your Register class looks correct.
For your client call I would change it to
_client = new JsonServiceClient(_apiUri);
_client.Post(new Register()); //assuming you can map your 'body' variable to a Register class
Just to lose the extra line of code.
it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.
It is working out the endpoint that the rest api will hit. Once it hits the endpoint, the internals of ServiceStack should handle the routing based on the Operation (in this case Register) and Http method. Basically it will try to find a Service class (any class inheriting the Service marker interface) that has the request object (Register) as a parameter and it will use the Http method as the 'function' to call.
What is causing the Not Found exception?
Not exactly sure about this. If you could provide your 'Service' class it may help.
If you have a Service class like
public class RegisterService : Service
{
public RegistrationResponse Post(Register request)
{
//service code
return new RegistrationResponse();
}
}
the routing should work.
The fix for this was to ensure that the servicestack feature for predefined routes was enabled on the api. Once this is done, you don't need to bother with the Route attribute on the request objects.
The end point host config now looks like this:
new EndpointHostConfig
{
DefaultContentType = ContentType.Json,
EnableFeatures = Feature.None
.Add(Feature.Json)
.Add(Feature.PredefinedRoutes),
GlobalResponseHeaders = new Dictionary<string, string>(),
DefaultRedirectPath = "/documentation"
}