How to implement Delete service call using Service

2019-01-14 15:15发布

问题:

I have couple of questions related to REST service implementation using ServiceStack.

  1. For GET operation, I define my request DTO as below :

    [Route("/Customer/{ID}", Verbs = "GET")]
    public class GetCustomer : IReturn<GetCustomerResponse>
    {
        ....
        ....
    }
    

Here "GetCustomer" is request DTO and "GetCustomerResponse" is response DTO. But for PUT/POST/DELETE operation, I just need to know whether operation got committed successfully or not and if 'not' then what is the exception. So what should be my request dto definition for POST/PUT/DELETE? Should it use IReturnVoid as shown below?

[Route("/Customer/{ID}", Verbs = "DELETE")]
public class DeleteCustomer : IReturnVoid
{
    ....
    ....
}

If I have to use IReturnVoid then how I can retrieve any exception information that might occur on committing my operation?

In the error handling document for service stack it is written and I quote below

Error Response Types

The Error Response that gets returned when an Exception is thrown varies on whether a conventionally-named {RequestDto}Response DTO exists or not.

If it exists:

The {RequestDto}Response is returned, regardless of the service method's response type. If the {RequestDto}Response DTO has a ResponseStatus property, it is populated otherwise no ResponseStatus will be returned. (If you have decorated the {ResponseDto}Response class and properties with [DataContract]/[DataMember] attributes, then ResponseStatus also needs to be decorated, to get populated).

Otherwise, if it doesn't:

A generic ErrorResponse gets returned with a populated ResponseStatus property.

The Service Clients transparently handles the different Error Response types, and for schema-less formats like JSON/JSV/etc there's no actual visible difference between returning a ResponseStatus in a custom or generic ErrorResponse - as they both output the same response on the wire.

What I'm not getting from above is what should be the return type for my Delete method in my service implementation? How I can implement my delete method without defining delete response DTO but yet I'm able to retrieve 'ErrorResponse' n exception info?

  1. Is it possible to define route with "DELETE" verb? I have following implementation.

Route:

[Route("/DeleteCustomer/{ID}", Verbs = "DELETE")]
public class DeleteCustomer : IReturn<DeleteCustomerResponse>
{
    public int ID { get; set; }
}

Method implementation:

public DeleteContactResponse Delete(DeleteContact request)
{
    .....
}

But whenever I call this delete using my client, I always get "NotFound" exception. I tried different clients but with all I get 404 error.

One of the reference link available alongwith Servicestack document reuses the "GET" and "DELETE" verb together.

Another link suggests not all browsers support delete operation.

So I wonder how Delete operation should be implemented?

回答1:

See this earlier answer for details on how to design a REST-ful API with ServiceStack.

The CustomerRestExample contains a complete stand-alone example of a Customer REST ServiceStack Service:

Customer Service Definition

Here's an example of the custom Routes and Request DTO's of what a typical Customer REST Service could look like:

[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> {}

public class GetCustomersResponse
{
    public List<Customer> Results { get; set; } 
}

[Route("/customers/{Id}", "GET")]
public class GetCustomer : IReturn<Customer>
{
    public int Id { get; set; }
}

[Route("/customers", "POST")]
public class CreateCustomer : IReturn<Customer>
{
    public string Name { get; set; }
}

[Route("/customers/{Id}", "PUT")]
public class UpdateCustomer : IReturn<Customer>
{
    public int Id { get; set; }

    public string Name { get; set; }
}

[Route("/customers/{Id}", "DELETE")]
public class DeleteCustomer : IReturnVoid
{
    public int Id { get; set; }
}

OrmLite POCO Model:

public class Customer
{
    [AutoIncrement]
    public int Id { get; set; }

    public string Name { get; set; }
}

Essentially the Custom Routes identify the Resource whilst the HTTP VERB indicates the operation on that Resource. Looking at the HTTP Requests makes this a little clearer:

GET    /customers   -> return all Customers
POST   /customers   -> Create a new Customer
GET    /customers/1 -> return Customer 1
PUT    /customers/1 -> Update Customer 1
DELETE /customers/1 -> Delete Customer 1

Customer Service Implementation

With the above DTO's definitions in-place, we can now implement this Customer REST Service by adding an implementation for each Request DTO - in this example using OrmLite:

public class CustomerService : Service
{
    public object Get(GetCustomers request)
    {
        return new GetCustomersResponse { Results = Db.Select<Customer>() };
    }

    public object Get(GetCustomer request)
    {
        return Db.SingleById<Customer>(request.Id);
    }

    public object Post(CreateCustomer request)
    {
        var customer = new Customer { Name = request.Name };
        Db.Save(customer);
        return customer;
    }

    public object Put(UpdateCustomer request)
    {
        var customer = Db.SingleById<Customer>(request.Id);
        if (customer == null)
            throw HttpError.NotFound("Customer '{0}' does not exist".Fmt(request.Id));

        customer.Name = request.Name;
        Db.Update(customer);

        return customer;
    }

    public void Delete(DeleteCustomer request)
    {
        Db.DeleteById<Customer>(request.Id);
    }
}

Customer Usage Example

With the above Customer REST Service implementation, we can re-use the Request DTO's with ServiceStack's .NET Service Clients to provide an end-to-end Typed API without code-gen, i.e:

var client = new JsonServiceClient(BaseUri);

//GET /customers
var all = client.Get(new GetCustomers());                         // Count = 0

//POST /customers
var customer = client.Post(new CreateCustomer { Name = "Foo" });

//GET /customer/1
customer = client.Get(new GetCustomer { Id = customer.Id });      // Name = Foo

//GET /customers
all = client.Get(new GetCustomers());                             // Count = 1

//PUT /customers/1
customer = client.Put(
    new UpdateCustomer { Id = customer.Id, Name = "Bar" });       // Name = Bar

//DELETE /customers/1
client.Delete(new DeleteCustomer { Id = customer.Id });

//GET /customers
all = client.Get(new GetCustomers());                             // Count = 0

The comments above include the HTTP Operations performed in each Service Client example.



回答2:

I got the fix for my 2nd question from following two links : 1. Link1 2. Link2

I don't fully understand this fix but doing above changes worked for me and now I can call Delete function from any clients.

For 1st question, pls refer @mythz 's reply below in detail.