Passing back multiple service uris

I have created an asp.net web api 2 controller to manage a logical asset. As usual, the post creates it and returns the uri to the asset and the delete removes it, but I have 2 puts to perform seperate actions on the asset, so my code looks like:

public class Controller : ApiController
{        
    public IHttpActionResult Post(Stuff stuff)
    {
        var id = CreateNewStuff(stuff);

        return CreatedAtRoute("DefaultApi", new { id = this.id }, id);
    }   

    [HttpPut]
    public IHttpActionResult ActionA(int id, ActionAStuff stuff)
    {    
        // Perform action A

        return Ok();
    }   

    [HttpPut]
    public IHttpActionResult ActionB(int id, ActionBStuff stuff)
    {    
        // Perform action B

        return Ok();
    }   

    public IHttpActionResult Delete(int id)
    {
        // Delete the asset

        return Ok();
    }  
}

So that the routing understands this, my routing rules are (including the default rule):

config.Routes.MapHttpRoute(name: "ActionApi", routeTemplate: "api/{controller}/{action}/{id}");
config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });

This works, and feels like the system neatly seperates the put code by action, and ensures the system will reject requests for actions we do not support.

The problem is that the client that creates the asset via post does not know what the put (action) uris are, in the way it does for the asset uri via the location header returned from the post. If we change the uri form in future, the clients would break as they would be manually creating uris.

What is the correct way to either return multiple servicepoint uris from the post, or just do the above better.


Use Route for differentiating the actions. For Example:

[RoutePrefix("api/Admin")]
public class Controller : ApiController
{        
    [Route("ActionA")
    [HttpPut]
    public IHttpActionResult ActionA(int id, ActionAStuff stuff)
    {    
        // Perform action A

        return Ok();
    }   

    [Route("ActionB")
    [HttpPut]
    public IHttpActionResult ActionB(int id, ActionBStuff stuff)
    {    
        // Perform action B

        return Ok();
    }   

}

Then enable Attribute Routing in your webapiconfig.cs

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }

finally in your client you can differentiate the put URIs like below: http(s):///api/Admin/ActionA

http(s):///api/Admin/ActionB


One should use Route prefix for each method for differentiating.

[Route("ActionA")
[HttpPut]
public IHttpActionResult ActionA(int id, ActionAStuff stuff)
{    
    // Perform action A

    return Ok();
}

When we configure the route using Route config from config.Routes.MapHttpRoute it has to be proper order as it added to stack and used from stack.

Once it founds the any entry from the routing table it will try to execute it. So its better way is to use the Route Prefix.


You could add a link header to the result of the POST method:

  • In order to add a custom header to the IHttpActionResult class you could define an extension method, for example:

    public static class HttpActionResultHeaderExtensions
    { 
        public static IHttpActionResult AddHeader(this IHttpActionResult action,
                                                  string headerName, 
                                                  params string[] headerValues)
        {
            return new HeaderActionResult(action, headerName, headerValues);
        }
    
        private class HeaderActionResult : IHttpActionResult
        {
            private readonly IHttpActionResult action;
    
            private readonly Tuple<string, IEnumerable<string>> header;
    
            public HeaderActionResult(IHttpActionResult action, 
                                      string headerName,
                                      IEnumerable<string> headerValues)
            {
                this.action = action;
    
                header = Tuple.Create(headerName, headerValues);
            }
    
            public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
            {
                var response = await action.ExecuteAsync(cancellationToken);
    
                response.Headers.Add(header.Item1, header.Item2);
    
                return response;
            }
        }
    }
    
  • Add route attributes:

    [System.Web.Http.RoutePrefixAttribute("api/stuff")]
    public class Controller : ApiController
    {           
        [System.Web.Http.HttpPut]
        [System.Web.Http.RouteAttribute("ActionA", Name = "ActionA")]
        public IHttpActionResult ActionA(int id, ActionAStuff stuff)
        {
            // your code            
        }
    
        [System.Web.Http.HttpPut]
        [System.Web.Http.RouteAttribute("ActionB", Name = "ActionB")]
        public IHttpActionResult ActionB(int id, ActionBStuff stuff)
        {
            // your code    
        }         
    }
    
  • Extend the POST method adding the header link to the result:

    public IHttpActionResult Post(Stuff stuff)
    {
        var id = CreateNewStuff(stuff);
    
        var actionALink = this.Url.Link("ActionA", new { id = id });
    
        var actionBLink = this.Url.Link("ActionB", new { id = id });
    
        return CreatedAtRoute("DefaultApi", new { id = id }, id)
            .AddHeader("Link", $"<{actionALink}>; rel="ActionA"",     
                               $"<{actionBLink}>; rel="ActionB"");        
    }
    
  • Extend configuration:

    config.MapHttpAttributeRoutes();
    
    config.EnsureInitialized();
    
  • 链接地址: http://www.djcxy.com/p/37620.html

    上一篇: Spark:graphx api在无用RDD之后出现OOM错误

    下一篇: 传回多个服务uris