Content-Type must be 'application/json-patch+json' JsonServiceClient ServiceStack - servicestack

I'm trying to perform a patch with a JsonServiceClient to a service stack api as follows:
var patchRequest = new JsonPatchRequest
{
new JsonPatchElement
{
op = "replace",
path = "/firstName",
value = "Test"
}
};
_jsonClient.Patch<object>($"/testurl/{id}", patchRequest);
But I'm getting the following error:
Content-Type must be 'application/json-patch+json'
The error is clear. Is there a way to change the content type before perform the request for the JsonServiceClient?
This is the request POCO in the ServiceStack api:
[Api("Partial update .")]
[Route("/testurl/{Id}”, "PATCH")]
public class PartialTest : IReturn<PartialTestRequestResponse>, IJsonPatchDocumentRequest,
IRequiresRequestStream
{
[ApiMember(Name = “Id”, ParameterType = "path", DataType = "string", IsRequired = true)]
public string Id { get; set; }
public Stream RequestStream { get; set; }
}
public class PartialTestRequestResponse : IHasResponseStatus
{
public ResponseStatus ResponseStatus { get; set; }
}
Service implementation:
public object Patch(PartialTest request)
{
var dbTestRecord = Repo.GetDbTestRecord(request.Id);
if (dbTestRecord == null) throw HttpError.NotFound("Record not found.");
var patch =
(JsonPatchDocument<TestRecordPoco>)
JsonConvert.DeserializeObject(Request.GetRawBody(), typeof(JsonPatchDocument<TestRecordPoco>));
if (patch == null)
throw new HttpError(HttpStatusCode.BadRequest, "Body is not a valid JSON Patch Document.");
patch.ApplyTo(dbTestRecord);
Repo.UpdateDbTestRecord(dbTestRecord);
return new PartialTestResponse();
}
I'm using Marvin.JsonPatch V 1.0.0 library.

It's still not clear where the Exception is coming from as it's not an Error within ServiceStack. If you've registered a Custom Format or Filter that throws this error please include its impl (or a link to it) as well as the full StackTrace which will identify the source of the error.
But you should never call Patch<object> as an object return type doesn't specify what Response Type to deserialize into. Since you have an IReturn<T> marker you can just send the Request DTO:
_jsonClient.Patch(new PartialTest { ... });
Which will try to deserialize the Response in the IReturn<PartialTestRequestResponse> Response DTO. But as your Request DTO implements IRequiresRequestStream it's saying you're expecting unknown bytes that doesn't conform to a normal Request DTO, in which case you likely want to use a raw HTTP Client like HTTP Utils, e.g:
var bytes = request.Url.SendBytesToUrl(
method: HttpMethods.Path,
requestBody: jsonPatchBytes,
contentType: "application/json-patch+json",
accept: MimeTypes.Json);
You could modify the ContentType of a JSON Client using a request filter, e.g:
_jsonClient.RequestFilter = req =>
req.ContentType = "application/json-patch+json";
But it's more appropriate to use a low-level HTTP Client like HTTP Utils for non-JSON Service Requests like this.

Related

Unable to get from ServiceStack API using JsonServiceClient

I am trying to get all equipment types from my API using the following code.
client = new JsonServiceClient(environment.apiEndpoint);
var equipmentTypes = new GetEquipmentTypes();
var response = this.client.get(equipmentTypes);
I can see that it is in the network tab. The data is being transferred.
public class GetEquipmentTypeResponse
{
public IEnumerable<EquipmentType> Results { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
Is the return DTO from the API.
[Route("/api/EquipmentTypes", "GET")]
public class GetEquipmentTypes : IReturn<GetEquipmentTypeResponse>
{
}
Is the ServiceInterface used.
IEnumerable<EquipmentType> response = db.Select<EquipmentType>(x=>x.Name == request.Name);
return new GetEquipmentTypeResponse { Results = response,
ResponseStatus = new ResponseStatus { }};
Is what the API returns.
The API is written in asp.net. The client side is angular 6 (typescript).
I have attached two images, which is the request and the response given.
This is the request which is sent to the API.
This is what the API responds.
This is what I get from var response. (console.log(response))
The screenshot shows that the response is being returned fine, the Promise result is just not being awaited, try:
var response = await this.client.get(equipmentTypes);

GetRawBody() is returning empty for REST requests

I am trying to write the Raw data of my ServiceStack webservice using servicerunner. This is working for SOAP requests but for the REST request GetRawBody() is returning empty.
public override void BeforeEachRequest(IRequest requestContext, T request)
{
Logger.Write(requestContext.GetRawBody());
}
By default web servers only provide a forward-only Request Stream which you can tell ServiceStack to skip deserialization so you can read from the Request Stream by implementing IRequiresRequestStream on your Request DTO:
public class MyRequest : IRequiresRequestStream
{
Stream RequestStream { get; set; }
}
Which will inject the Request Stream instead of deserializing the Request DTO, e.g:
public class object Any(MyRequest request)
{
var requestBody = request.RequestStream.ReadFully().FromUtf8Bytes();
}
Otherwise if you want ServiceStack to deserialize the Request and you want to re-read from the Request Body later yourself you need to tell ServiceStack to buffer the Request using a pre-request filter:
appHost.PreRequestFilters.Add((httpReq, httpRes) => {
httpReq.UseBufferedStream = true;
});

ServiceStack request filter Attribute set a custom object

I am trying to write a Custom RequestFilterAttribute that would run on every service to check if the request has a a valid token. I want to return or set an object once the CanExecute method is called and forward it to the service method for further processing. Is there a way to do that in ServiceStack .
ServiceStack Request Filters lets you short-circuit a request so it does no further processing, to let a request go through you'd just ignore the request. One way to do this for specific Requests is to have them share a common interface which you can verify in your Request Filter, e.g:
public interface IValidateToken
{
string Token { get; }
}
public class MyRequest : IValidateToken
{
public string Token { get; set; }
}
Then in a Global Request Filter you can verify if the token is valid, otherwise return an error and short-circuit the request with something like:
GlobalRequestFilters.Add((httpReq, httpRes, dto) => {
var tokenRequest = dto as IValidateToken;
if (tokenRequest != null && !MyValidateToken(tokenRequest.Token))
{
httpRes.StatusCode = (int) HttpStatusCode.Forbidden;
httpRes.StatusDescription = "Token is invalid";
httpRes.EndRequest();
}
});
If the Request Token is valid the request gets processed as normal.

My servicestack cached service includes extra slashes in the response

I have created a cached webservice using ServiceStack.
public class ContentSearchService : ServiceBase<ContentSearch>
{
public ICacheClient CacheClient { get; set; }
protected override object Run(ContentSearch request)
{
var cacheKey = "unique_key_for_this_request2";
return base.RequestContext.ToOptimizedResultUsingCache(this.CacheClient, cacheKey, () =>
{
//Delegate is executed if item doesn't exist in cache
//Any response DTO returned here will be cached automatically
return new ContentSearchResponse()
{
Contents = new List<ContentData>()
{
new ContentData()
{
FileName = "testfile.jpg"
}
}
};
});
}
}
This is then run using:
IRestClient client = new JsonServiceClient("http://internal");
ContentSearch search = new ContentSearch();
ContentSearchResponse response = client.Put<ContentSearchResponse>("/json/syncreply/ContentSearch", search);
The first response is returned as expected and converted into the response object. The second, which is cached is returned with extra slashes and as a result can't be serialized.
First response:
{"Contents":[{"FileName":"testfile.jpg","Company":0,"Version":0}]}
Second response:
{\"Contents\":[{\"FileName\":\"testfile.jpg\",\"Company\":0,\"Version\":0}]}
I'm using Redis as the cache.
I've had a look at the redis server they are stored with the slashes.
Turns out this was related to the client using a different version of the ServiceStack assemblies. I updated both the server and client to the lastest and everything worked as expected.

Does ServiceStack support binary responses?

Is there any mechanism in ServiceStack services to return streaming/large binary data? WCF's MTOM support is awkward but effective in returning large amounts of data without text conversion overhead.
I love service stack, this litle code was enough to return an Excel report from memory stream
public class ExcelFileResult : IHasOptions, IStreamWriter
{
private readonly Stream _responseStream;
public IDictionary<string, string> Options { get; private set; }
public ExcelFileResult(Stream responseStream)
{
_responseStream = responseStream;
Options = new Dictionary<string, string> {
{"Content-Type", "application/octet-stream"},
{"Content-Disposition", "attachment; filename=\"report.xls\";"}
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
From a birds-eye view ServiceStack can return any of:
Any DTO object -> serialized to Response ContentType
HttpResult, HttpError, CompressedResult (IHttpResult) for Customized HTTP response
The following types are not converted and get written directly to the Response Stream:
String
Stream
IStreamWriter
byte[] - with the application/octet-stream Content Type.
Details
In addition to returning plain C# objects, ServiceStack allows you to return any Stream or IStreamWriter (which is a bit more flexible on how you write to the response stream):
public interface IStreamWriter
{
void WriteTo(Stream stream);
}
Both though allow you to write directly to the Response OutputStream without any additional conversion overhead.
If you want to customize the HTTP headers at the sametime you just need to implement IHasOptions where any Dictionary Entry is written to the Response HttpHeaders.
public interface IHasOptions
{
IDictionary<string, string> Options { get; }
}
Further than that, the IHttpResult allows even finer-grain control of the HTTP output where you can supply a custom Http Response status code. You can refer to the implementation of the HttpResult object for a real-world implementation of these above interfaces.
I had a similar requirement which also required me to track progress of the streaming file download. I did it roughly like this:
server-side:
service:
public object Get(FooRequest request)
{
var stream = ...//some Stream
return new StreamedResult(stream);
}
StreamedResult class:
public class StreamedResult : IHasOptions, IStreamWriter
{
public IDictionary<string, string> Options { get; private set; }
Stream _responseStream;
public StreamedResult(Stream responseStream)
{
_responseStream = responseStream;
long length = -1;
try { length = _responseStream.Length; }
catch (NotSupportedException) { }
Options = new Dictionary<string, string>
{
{"Content-Type", "application/octet-stream"},
{ "X-Api-Length", length.ToString() }
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
using (_responseStream)
{
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
}
client-side:
string path = Path.GetTempFileName();//in reality, wrap this in try... so as not to leave hanging tmp files
var response = client.Get<HttpWebResponse>("/foo/bar");
long length;
if (!long.TryParse(response.GetResponseHeader("X-Api-Length"), out length))
length = -1;
using (var fs = System.IO.File.OpenWrite(path))
fs.CopyFrom(response.GetResponseStream(), new CopyFromArguments(new ProgressChange((x, y) => { Console.WriteLine(">> {0} {1}".Fmt(x, y)); }), TimeSpan.FromMilliseconds(100), length));
The "CopyFrom" extension method was borrowed directly from the source code file "StreamHelper.cs" in this project here: Copy a Stream with Progress Reporting (Kudos to Henning Dieterichs)
And kudos to mythz and any contributor to ServiceStack. Great project!

Resources