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;
}
}
Related
I am working on a custom middlware that's going to set the response Http Status Code based on the response itself.
I have a class:
public class Response<T>
{
public T Data { get; set; }
public IEnumerable<CustomError> Errors { get; set; }
}
that is returned by every controller in my .Net Core API.
I want to create a custom middleware that's going to access the response after it is returned from the controller and it will assign a correct Http Status Code in the Response based on the Errors field.
I can see some solutions for accessing the Response.Body field of the HttpContext, but it would provide a serialized string that I would have to deserialize again and that's running around in circles.
Is it possible in .Net Core?
Best regards,
Marcin
Instead of middleware, you could create an ActionFilter, specifically your own implementation IAsyncResultFilter. It's going to be easier to cast to Response in the MVC context rather than in the middleware because you may access there ObjectResult.
It could look like this.
public class Response
{
public IEnumerable<string> Errors { get; set; }
}
public class Response<T> : Response
{
public T Data { get; set; }
}
Note that I changed the Response<T> class to make casting easier.
public class ErrorResultFilter : IAsyncResultFilter
{
public Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var result = context.Result as ObjectResult;
var response = result?.Value as Response;
if (response != null)
context.HttpContext.Response.StatusCode
= response.Errors.Any(x => x.Equals("SomeError")) ? 400 : 200;
return next();
}
}
This sample sets status code based on the presence of "SomeError". There's no serialization/deserialization involved, just casting.
services.AddControllers(o =>
{
o.Filters.Add(typeof(ErrorResultFilter));
});
This way, I registered my filter in the startup.cs
I'm building an Angular2 service to log certain events, stored in ILog objects, and send them to an API to be stored in a database.
My log service is pretty straightforward:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { EnvironmentModule } from "../environment";
import { ILog } from "./log";
#Injectable()
export class LogService {
constructor(private _http: Http, private environment: EnvironmentModule) { }
postLog(log: ILog): void {
this.json = this.convertToJSON(log);
this._http.post(this.environment.getWebApiUri() + 'api/Log/PostLog/', log, {})
.subscribe(
() => console.log('Success')
); //goes to localhost:3304/WebAPI/Log/PostLog/, returns 404 not found
}
}
And it calls a WebAPI Controller that passes the data off to a service to be processed:
[RoutePrefix("api/Log")]
public class LogController : ApiController
{
private ILogService _LogService;
public LogController() : this(new LogService())
{
} //constructor
public LogController(ILogService LogService)
{
_LogService = LogService;
} //constructor
[HttpPost()]
[Route("PostLog")]
public void PostLog(Log log)
{
_LogService.PostLog(log);
} //PostLog
} //class
Yet, when my service calls the API it throws a 404 Not Found Error.
Navigating to the path in the browser I see this:
<Error>
<Message>
No HTTP resource was found that matches the request URI
'http://localhost:3304/WebAPI/api/Log/PostLog/'.
</Message>
<MessageDetail>
No action was found on the controller 'Log' that matches the request.
</MessageDetail>
</Error>
Can anyone help me with this? I don't understand why it's behaving this way.
Its because you cant post an atomic value directly to your method as json. You could turn it into an object and then post it as a corresponding object or post it as form uri encoded which also works. This is a limitation of asp.net's web api.
There are some other similar questions all with similar answers. Here is a quick example of how you could change it to work.
c# code
[HttpPost()]
[Route("PostLog")]
public void PostLog(LogContainerModel logModel)
{
_LogService.PostLog(logModel.log);
}
// model
public sealed class LogContainerModel {
public string log { get; set; }
}
javascript code
private convertToJSON(log: ILog): string {
return JSON.stringify({log: log});
}
Option 2
Stringify it as an object according to this previous SO answer.
c# code
[HttpPost()]
[Route("PostLog")]
public void PostLog([FromBody] string jsonString)
javascript code
private convertToJSON(log: ILog): string {
return JSON.stringify({'': log}); // if that does not work try the snippet below
// return JSON.stringify({'': JSON.stringify(log)});
}
Option 3
Here are some options from bizcoder.com
Use HttpResponseMessage
[HttpPost()]
[Route("PostLog")]
public async Task PostLog(HttpRequestMessage request)
{
var jsonString = await request.Content.ReadAsStringAsync();
_LogService.PostLog(jsonString);
}
Or use json.net
[HttpPost()]
[Route("PostLog")]
public void PostLog([FromBody]JToken jsonbody)
{
var jsonString = jsonbody.ToString();
_LogService.PostLog(jsonString);
}
SO - How to post a single string (using form encoding)
SO - send single string parameter - this might be the better option, good answer.
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();
}
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.
How to inherit all DTO resource in one service?.
Say for example ,
I Have Resource Class :
[RestService("/getstudentname", "GET,POST,PUT,OPTIONS")]
public class RestResourcename
{
public string Name { get; set; }
}
[RestService("/getstudentID", "GET,POST,PUT,OPTIONS")]
public class CNextRestResourceid
{
public string Name { get; set; }
}
I have my Service Class :
1.How to inherit another DTO Class in this Service ????????
2.Do i need to create seperate class for this ?????
public class CnextRestService : RestServiceBase<RestResourcename>
{
public override object OnGet(RestResourcename request)
{
return request;
}
}
Please suggest me on this issues.......
You can implement multiple HTTP Verbs on the same Resource (aka Request) DTO in the same web service, e.g:
public class CustomersService : Service
{
object Get(GetCustomer request){...}
object Post(CreateCustomer request){...}
object Put(UpdateCustomer request){...}
object Delete(DeleteCustomer request){...}
}
This allows you to provide multiple implementations for the following HTTP actions:
GET /customers
GET /customers/1
POST /customers
PUT /customers/1
DELETE /customers/1
Although if you use SOAP you're limited to 1 RPC method for each web service since SOAP only supports HTTP POST.
The best way to do this is to inherit from Service and implement the Any() method which will be called regardless of which HTTP Verb or endpoint was used to invoke the service.