Error 405 - Method Not Allowed After Nuget Update

Hi,

Since updating to the latest version of ServiceStack one of our web service methods has started failing with an Error 405 - Method Not Allowed.

It had always worked prior to updating the nuget packages on both our client and server applications to version 4.5.14.

It is not a straightforward call from the client as it is a method used for uploading a large file.

I have tried to show as much of the client side code as I can below.

The server side code is all correct and set up to allow POST.

Any guidance would be very much appreciated.

Many thanks in advance.

Simon

var request = string.Format("{0}/{1}/{2}/{3}/{4}/{5}/{6}/{7}/{8}/{9}/{10}", VqsServiceClientSettings.Settings.ApiUrl.Value, "webrequestmethodname", year, month,
scheduledDateTime.Day, scheduledDateTime.Month, scheduledDateTime.Year, scheduledDateTime.Hour, scheduledDateTime.Minute, HttpUtility.UrlEncode(auditUserName),
HttpUtility.UrlEncode(NetworkUtilities.GetCurrentIp4Address()));

var client = (HttpWebRequest) WebRequest.Create(request);
client.Method = WebRequestMethods.Http.Post;
client.AllowWriteStreamBuffering = false;
client.SendChunked = true;
client.ContentType = "multipart/form-data;";
client.Timeout = int.MaxValue;
client.Method = "POST";

client.Headers["Authorization"] = "Basic " +
                              Convert.ToBase64String(
                                  Encoding.Default.GetBytes("XXXUSER" + ":" + "XXXPASSWORD"));

client.Headers["CustomHeaders.LoggedOnUserNameHeader"] = "auditUserName";

requestStream.Copy(client.GetRequestStream());

var responseStream = client.GetResponse().GetResponseStream();
if (responseStream == null)
{
    throw new StormSmrException("Failed to get valid response from webrequestmethodname");
}

var responseData = new StreamReader(responseStream).ReadToEnd();
return JsonConvert.DeserializeObject<SmrImportRequestEntity>(responseData);

Please provide the Request DTO definition and the method signatures on the Service as well as the raw HTTP Request/Response Headers, you can omit the Request body and replace any sensitive info with xxxx.

Hi,

As requested, here is the request and response DTO’s as well as the service methods. Please note that the base class for the request does nothing, it just inherits from QueryBase

[Route("/addsmrimportrequest/{Year}/{Month}/{ScheduledDay}/{ScheduledMonth}/{ScheduledYear}/{ScheduledHour}/{ScheduledMinutes}/{AuditUserName}/{AuditIpAddress}", Verbs = "OPTIONS POST")]
[DataContract]
public class AddSmrImportRequest : StormPostRequest, IRequiresRequestStream, IReturn<AddSmrImportRequestResponse>
{
    [DataMember(IsRequired = true, Order = 1)]
    public int Year { get; set; }

    [DataMember(IsRequired = true, Order = 2)]
    public short Month { get; set; }

    [DataMember(IsRequired = true, Order = 3)]
    public short ScheduledDay { get; set; }

    [DataMember(IsRequired = true, Order = 4)]
    public short ScheduledMonth { get; set; }

    [DataMember(IsRequired = true, Order = 5)]
    public int ScheduledYear { get; set; }

    [DataMember(IsRequired = true, Order = 6)]
    public short ScheduledHour { get; set; }

    [DataMember(IsRequired = true, Order = 7)]
    public short ScheduledMinutes { get; set; }

    [DataMember(IsRequired = true, Order = 8)]
    public string AuditUserName { get; set; }

    [DataMember(IsRequired = true, Order = 9)]
    public string AuditIpAddress { get; set; }

    public Stream RequestStream { get; set; }

}

[DataContract]
public class GetSmrRequestsResponse : StormResponse
{
    [DataMember(IsRequired = true, Order = 1)]
    public List<SmrImportRequestEntity> SmrRequests { get; set; }
}


[Authenticate]
[GFeature]
public object Post(AddSmrImportRequest request)
{
    StormLogger.Logger.Debug("Entering object Put(AddSmrImportRequest request");
    var result = new AddSmrImportRequestResponse
    {
        SmrImportRequest =
            SmrUtilitiesRepository.AddSmrImportRequest(request.Year, request.Month, request.RequestStream,
                new DateTime(request.ScheduledYear, request.ScheduledMonth, request.ScheduledDay, request.ScheduledHour, request.ScheduledMinutes, 0),
                new AuditInformationEntity {User = request.AuditUserName, IpAddress = request.AuditIpAddress})
    };
    StormLogger.Logger.Debug("Exiting object Put(AddSmrImportRequest request");

    return result;
}

Also, here is the raw request. I have removed the binary data is it is massive.

POST https://dev.carbenefitsolutionstest.uk/VqsApi/addsmrimportrequest/2017/8/30/9/2017/0/0/xxxxuser/192.168.23.64 HTTP/1.1
Content-Type: multipart/form-data;
Authorization: Basic xxxxxxxxxxxxx
CustomHeaders.LoggedOnUserNameHeader: auditUserName
Host: xxx.xxxxxxxxxxtest.uk
Transfer-Encoding: chunked
Expect: 100-continue

1000
PK     {Kn $vV| =  > $ August040817.csv ..... BINARY DATA AFTER THIS

Many thanks

Simon

I haven’t been able to repro this, which I’ve tested in both an ASP.NET and MVC app, mounted at / and /api. But I noticed your HTTP Request is incorrect, you’re saying you’re sending a multipart/form-data but the Request is posting a Stream of bytes that is invalid as it doesn’t have a multi-part boundary.

If you want to upload a file I recommend submitting a HTTP File upload which you can test with a Web Page, the Uploaded file then becomes part of the base.Request.Files collection. In this case you’re not sending a byte[] request body so your Request DTO should no longer implement IRequiresRequestStream. Instead

I’d recommend using the File Upload APIs on the Service Clients to ensure the HTTP File Upload Request is properly constructed or if you want to continue to use a WebRequest you can use the UploadFile extension method which takes care of properly constructing the multipart/form-data request.

With that said I’m not sure if the 405 Response is related to the invalid request, if you’re still getting the issue can you submit a small stand-alone example (e.g. on GitHub) that repro’s the issue. The Service impl shouldn’t be required, what’s important is the App Host configuration, Request + Response DTOs and Server method signature used that causes the 405 error.

Hi,

Thank you so much for all the information. It is very much appreciated.

I have tried the FileUpload API which seems to the most applicable, however, I am now having another issue which is not letting me get far enough to see if it has resolved the original; issue.

I am now getting the error shown below.

I will admit, the stream is very large at 320MB in size. Is there a maximum stream size and if so, are we exceeding it?

Many thanks

Simon

Exception of type 'System.OutOfMemoryException' was thrown.
at System.Net.ScatterGatherBuffers.AllocateMemoryChunk(Int32 newSize)
at System.Net.ScatterGatherBuffers.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.ConnectStream.InternalWrite(Boolean async, Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.ConnectStream.Write(Byte[] buffer, Int32 offset, Int32 size)
at ServiceStack.ServiceClientBase.<>c__DisplayClass277_0`1.<PostFileWithRequest>b__0()
at ServiceStack.ServiceClientBase.PostFileWithRequest[TResponse](String relativeOrAbsoluteUrl, Stream fileToUpload, String fileName, Object request, String fieldName)
at ServiceStack.ServiceClientBase.PostFileWithRequest[TResponse](Stream fileToUpload, String fileName, Object request, String fieldName)......

There’s no maximum size imposed by ServiceStack, but the the internal OutOfMemoryException thrown from a 320MB file upload suggests you’ve exceeded the internal limits for writing a buffer to a Stream.

Hi, Thanks for that. Are you aware of any way to deal with this. I have had a google and some say to try and buffer the filestream, but I cannot see how to do this with the servicestack request.

Sorry to be a pain.

Regards

Simon

It’s an internal OutOfMemoryException which is typically limited by RAM available, otherwise I don’t see any way around it other than to split the upload in smaller chunks.

It looks like for large file sizes you’d need to go back to uploading raw bytes and implementing IRequiresRequestStream since that worked previously, but you’ll want to use the correct Content-Type for the file you’re uploading. If you’re still getting a 405 error response, can you put together a small standalone app that repro’s the issue so I can investigate what’s causing it.